@ohhwells/bridge 0.1.158-next.424 → 0.1.159

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 React12, { useCallback as useCallback12, useEffect as useEffect17, useLayoutEffect as useLayoutEffect7, useRef as useRef14, useState as useState18 } from "react";
4
+ import React12, { useCallback as useCallback12, useEffect as useEffect17, useLayoutEffect as useLayoutEffect6, useRef as useRef14, useState as useState18 } from "react";
5
5
  import { createRoot as createRoot3 } from "react-dom/client";
6
6
  import { flushSync as flushSync3 } from "react-dom";
7
7
 
@@ -6649,21 +6649,6 @@ function pricingCardFeatureCount(tree, index) {
6649
6649
  const target = findPricingCollection(tree)?.children?.[index];
6650
6650
  return target ? pricingFeatureList(target).length : 0;
6651
6651
  }
6652
- function prevailingFeatureIcon(features) {
6653
- const counts = /* @__PURE__ */ new Map();
6654
- for (const row of features) {
6655
- const key = `${row.icon ?? ""}|${row.iconColor ?? ""}`;
6656
- const entry = counts.get(key) ?? { count: 0, ...row.icon ? { icon: row.icon } : {}, ...row.iconColor ? { iconColor: row.iconColor } : {} };
6657
- entry.count += 1;
6658
- counts.set(key, entry);
6659
- }
6660
- let best = null;
6661
- for (const entry of counts.values()) {
6662
- if (!best || entry.count > best.count) best = entry;
6663
- }
6664
- if (!best) return {};
6665
- return { ...best.icon ? { icon: best.icon } : {}, ...best.iconColor ? { iconColor: best.iconColor } : {} };
6666
- }
6667
6652
  function addPricingCardFeature(tree, index, text) {
6668
6653
  return withPricingCollection(tree, (collection) => {
6669
6654
  const children = collection.children;
@@ -6672,7 +6657,7 @@ function addPricingCardFeature(tree, index, text) {
6672
6657
  const features = pricingFeatureList(target);
6673
6658
  if (features.length >= PRICING_FEATURES_MAX) return false;
6674
6659
  target.slots = target.slots ?? {};
6675
- target.slots.features = [...features, { text, ...prevailingFeatureIcon(features) }];
6660
+ target.slots.features = [...features, { text }];
6676
6661
  return true;
6677
6662
  });
6678
6663
  }
@@ -7872,7 +7857,7 @@ var AI_MOBILE_CSS = [
7872
7857
  // page's first section. Top only (bottom needs no nav clearance); ~96px approximates the
7873
7858
  // template nav's own height (measured ~94px) — there's no shared `--ohw-header-h` var to read
7874
7859
  // an exact figure from (no live measurement exists), so this is a safe approximation, not a
7875
- // pixel-exact fit. Horizontal spacing already comes from `data-ai-section-inner`.
7860
+ // pixel-exact fit. Horizontal spacing already comes from `data-ai-container`.
7876
7861
  '[data-ai-overlay-clearance] [data-ai-overlap-layer="overlay"]{padding-top:96px}',
7877
7862
  // A full-bleed carousel's height is pure arithmetic: it spans the whole window, so 16/9 turns
7878
7863
  // window width straight into height. Measured at 16/9 it came out ~90% of the viewport at EVERY
@@ -7888,10 +7873,8 @@ var AI_MOBILE_CSS = [
7888
7873
  "[data-ai-section]{overflow-x:hidden}",
7889
7874
  // `:not([data-ai-full-bleed])`: this `!important` gutter beat the inline `padding: 0` that a
7890
7875
  // full-bleed section's container sets, so a carousel/media hero that is edge-to-edge on desktop
7891
- // still got a 20px band of section background down both sides on a phone. The footer's own
7892
- // container keeps the unguarded rule below — `data-ai-full-bleed` can never appear on it.
7893
- "[data-ai-section-inner]:not([data-ai-full-bleed]){padding:0 20px !important}",
7894
- "[data-ai-container]{padding:0 20px !important}",
7876
+ // still got a 20px band of section background down both sides on a phone.
7877
+ "[data-ai-container]:not([data-ai-full-bleed]){padding:0 20px !important}",
7895
7878
  "[data-ai-row]{display:flex !important;flex-direction:column !important;align-items:stretch !important}",
7896
7879
  "[data-ai-cell]{width:100%;min-width:0}",
7897
7880
  // The rule above turns every row into a COLUMN flex container on phones, which silently breaks
@@ -8096,31 +8079,6 @@ function pricingCardEmphasis(featured, ctx, hasBg) {
8096
8079
  function textAttrs(ctx, path) {
8097
8080
  return ctx.keyFor ? { "data-ohw-key": ctx.keyFor(path), "data-ohw-editable": "text" } : {};
8098
8081
  }
8099
- var AI_RESPONSIVE_CSS = [
8100
- "@media (max-width: 960px) {",
8101
- " [data-ai-responsive] [data-ai-grid] { grid-template-columns: repeat(2, 1fr) !important; }",
8102
- ' [data-ai-responsive] [data-ai-grid="1"] { grid-template-columns: 1fr !important; }',
8103
- "}",
8104
- "@media (max-width: 640px) {",
8105
- " [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
8106
- " [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
8107
- // Group containers flatten to a column on phones; span placements come along for free.
8108
- // `:not([data-ai-group="overlap"])` for the same reason as the twin pair in AI_MOBILE_CSS — see
8109
- // the comment there. This sheet's copy is the narrower selector, so without the exemption it
8110
- // flattens an `overlap` hero below 640px even though the other rule stands down: measured a 390px
8111
- // viewport with the heading/CTA in flow *under* the image, 219px of section background where the
8112
- // layered text used to be.
8113
- ' [data-ai-responsive] [data-ai-group]:not([data-ai-group="overlap"]) { display: flex !important; flex-direction: column !important; }',
8114
- ' [data-ai-responsive] [data-ai-group]:not([data-ai-group="overlap"]) > * { grid-column: auto !important; }',
8115
- // `:not([data-ai-full-bleed])` for the same reason as the twin rule in AI_MOBILE_CSS: this one
8116
- // is narrower (0,2,0 vs 0,1,0) and also `!important`, so without the guard it wins outright and
8117
- // a full-bleed hero gets its 20px side band back below 640px only — the one width where the
8118
- // other rule had already been taught to stand down.
8119
- " [data-ai-responsive] [data-ai-section-inner]:not([data-ai-full-bleed]) { padding-left: 20px !important; padding-right: 20px !important; }",
8120
- " [data-ai-responsive] { overflow-x: hidden; }",
8121
- " [data-ai-responsive] img { max-width: 100%; }",
8122
- "}"
8123
- ].join("\n");
8124
8082
  var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
8125
8083
  function MediaBox({
8126
8084
  refValue,
@@ -8175,7 +8133,7 @@ function MediaBox({
8175
8133
  position: fill ? "absolute" : void 0,
8176
8134
  inset: fill ? 0 : void 0,
8177
8135
  width: fill ? "100%" : fixedSize ? "auto" : "100%",
8178
- height: fill ? "100%" : fixedSize ? height : void 0,
8136
+ height: fill ? "100%" : void 0,
8179
8137
  aspectRatio: fill ? void 0 : aspectRatio,
8180
8138
  minHeight: fill || fixedSize ? void 0 : height ?? (aspectRatio ? void 0 : 180),
8181
8139
  borderRadius: radius,
@@ -9671,7 +9629,7 @@ function CollectionBlock({ node, ctx, path }) {
9671
9629
  return /* @__PURE__ */ jsx(
9672
9630
  "div",
9673
9631
  {
9674
- "data-ai-grid": String(itemsPerRow),
9632
+ "data-ai-grid": "",
9675
9633
  "data-ohw-pricing-collection": isPricing ? "" : void 0,
9676
9634
  style: {
9677
9635
  display: "grid",
@@ -9973,7 +9931,7 @@ function renderNode(node, ctx, path, ambientAlign) {
9973
9931
  },
9974
9932
  children: [
9975
9933
  i > 0 && ctx.heroOverlapFullBleed ? (
9976
- // The section's `data-ai-section-inner` gave up its max-width/gutter so the base
9934
+ // The section's `data-ai-container` gave up its max-width/gutter so the base
9977
9935
  // layer (this group's i===0 sibling) can span edge-to-edge — reintroduced here,
9978
9936
  // scoped to just the overlay text, so a heading/copy/CTA stack doesn't stretch
9979
9937
  // the full section width instead of reading at the usual content width.
@@ -10392,7 +10350,6 @@ function AiTreeRenderer({
10392
10350
  ...fillHeight ? { "data-ai-fill-height": "" } : {},
10393
10351
  ...needsOverlayClearance ? { "data-ai-overlay-clearance": "" } : {},
10394
10352
  ...bgAttrs,
10395
- "data-ai-responsive": "",
10396
10353
  style: {
10397
10354
  position: "relative",
10398
10355
  padding: `${padCss} 0`,
@@ -10406,13 +10363,12 @@ function AiTreeRenderer({
10406
10363
  ...fillHeight ? { display: "grid" } : {}
10407
10364
  },
10408
10365
  children: [
10409
- /* @__PURE__ */ jsx("style", { children: AI_RESPONSIVE_CSS }),
10410
- /* @__PURE__ */ jsx("style", { children: AI_MOBILE_CSS }),
10411
10366
  isOverlay && backgroundUrl && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
10367
+ /* @__PURE__ */ jsx("style", { children: AI_MOBILE_CSS }),
10412
10368
  /* @__PURE__ */ jsx(
10413
10369
  "div",
10414
10370
  {
10415
- "data-ai-section-inner": "",
10371
+ "data-ai-container": "",
10416
10372
  ...fullBleedMedia ? { "data-ai-full-bleed": "" } : {},
10417
10373
  style: {
10418
10374
  position: "relative",
@@ -10429,7 +10385,7 @@ function AiTreeRenderer({
10429
10385
  children: tree.rows.map((row, r2) => /* @__PURE__ */ jsx(
10430
10386
  "div",
10431
10387
  {
10432
- "data-ai-columns": "",
10388
+ "data-ai-row": "",
10433
10389
  style: {
10434
10390
  display: "grid",
10435
10391
  gridTemplateColumns: "repeat(12, 1fr)",
@@ -11091,10 +11047,9 @@ function applyAiSectionsToDom(state, options) {
11091
11047
  if (existing && existing.serialized === serialized && existing.container.isConnected) {
11092
11048
  continue;
11093
11049
  }
11050
+ const isNewMount = !existing || !existing.container.isConnected;
11094
11051
  let container = existing?.container;
11095
- let isNewMount = false;
11096
11052
  if (!container || !container.isConnected) {
11097
- isNewMount = true;
11098
11053
  existing?.root.unmount();
11099
11054
  container?.remove();
11100
11055
  container = document.createElement("div");
@@ -17253,7 +17208,7 @@ function apiBase() {
17253
17208
  function bridgeVersion() {
17254
17209
  try {
17255
17210
  if (true) {
17256
- return "0.1.158-next.424";
17211
+ return "0.1.159";
17257
17212
  }
17258
17213
  } catch {
17259
17214
  }
@@ -17267,7 +17222,7 @@ function stampBridgeVersionOnDocument() {
17267
17222
  return;
17268
17223
  }
17269
17224
  try {
17270
- const v = true ? "0.1.158-next.424" : "";
17225
+ const v = true ? "0.1.159" : "";
17271
17226
  if (v) {
17272
17227
  document.documentElement.setAttribute("data-ohw-bridge-version", v);
17273
17228
  window.__OHW_BRIDGE_VERSION__ = v;
@@ -17945,7 +17900,6 @@ function MediaOverlay({
17945
17900
  (prev) => prev && prev.fullWidth === width && prev.height === height ? prev : { fullWidth: width, height }
17946
17901
  );
17947
17902
  }, [isVideo]);
17948
- const replaceLabel = isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image";
17949
17903
  const replaceMode = buttonSize ? replaceButtonMode({ width: rect.width, height: rect.height }, buttonSize) : "none";
17950
17904
  const box = {
17951
17905
  position: "fixed",
@@ -18044,10 +17998,11 @@ function MediaOverlay({
18044
17998
  className: "flex items-center justify-center cursor-pointer",
18045
17999
  style: {
18046
18000
  ...box,
18047
- // Visual only. `pointer-events: auto` on this full-bleed rect covered the navbar
18048
- // (hero bg includes the absolute TopNav) and swallowed CTA press-drag. Text overlay
18049
- // and cueOnly also need clicks to fall through — Replace / video controls opt back in.
18050
- pointerEvents: "none",
18001
+ // When text overlays this media, let clicks fall THROUGH to the text underneath. In the
18002
+ // parent this needed a whole `ow:click-at` round-trip to re-hit-test inside the iframe;
18003
+ // in-document, pointer-events does it natively. The button below opts back in, so
18004
+ // Replace still works.
18005
+ pointerEvents: hover.hasTextOverlap || cueOnly ? "none" : "auto",
18051
18006
  // Selected: a firm component ring with no wash, so the image reads as chosen rather
18052
18007
  // than hovered. Hover keeps the existing tinted preview.
18053
18008
  boxShadow: selected ? "inset 0 0 0 2px var(--color-bridge-primary)" : "inset 0 0 0 1.5px var(--color-bridge-primary)",
@@ -18074,17 +18029,17 @@ function MediaOverlay({
18074
18029
  },
18075
18030
  children: [
18076
18031
  isVideo ? /* @__PURE__ */ jsx17(Film, { size: 14 }) : /* @__PURE__ */ jsx17(ImageIcon, { size: 14 }),
18077
- replaceLabel
18032
+ isVideo ? "Replace video" : "Replace image"
18078
18033
  ]
18079
18034
  }
18080
18035
  ),
18081
- showChrome && replaceMode !== "none" && /* @__PURE__ */ jsxs8(
18036
+ replaceMode === "none" || cueOnly ? null : /* @__PURE__ */ jsxs8(
18082
18037
  Button,
18083
18038
  {
18084
18039
  "data-ohw-media-overlay": "",
18085
18040
  variant: "outline",
18086
18041
  size: "sm",
18087
- "aria-label": replaceLabel,
18042
+ "aria-label": isVideo ? "Replace video" : "Replace image",
18088
18043
  className: "gap-1.5 cursor-pointer hover:bg-background",
18089
18044
  style: {
18090
18045
  ...OVERLAY_BUTTON_STYLE,
@@ -18107,7 +18062,7 @@ function MediaOverlay({
18107
18062
  },
18108
18063
  children: [
18109
18064
  isVideo ? /* @__PURE__ */ jsx17(Film, { size: 14 }) : /* @__PURE__ */ jsx17(ImageIcon, { size: 14 }),
18110
- replaceMode === "full" ? replaceLabel : null
18065
+ replaceMode === "full" ? isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image" : null
18111
18066
  ]
18112
18067
  }
18113
18068
  )
@@ -18700,24 +18655,8 @@ function AiSectionOverlay({
18700
18655
  }
18701
18656
 
18702
18657
  // src/ui/ai-section/AiMediaStripOverlay.tsx
18703
- import { useCallback as useCallback3, useEffect as useEffect6, useLayoutEffect as useLayoutEffect4, useRef as useRef5, useState as useState8 } from "react";
18704
- import {
18705
- AlignCenter,
18706
- AlignLeft,
18707
- AlignRight,
18708
- ArrowLeft as ArrowLeft2,
18709
- ArrowRight as ArrowRight2,
18710
- Check as Check2,
18711
- ChevronDown as ChevronDown3,
18712
- Image as ImageIcon2,
18713
- ImageMinus,
18714
- ImagePlus,
18715
- Link as LinkIcon,
18716
- Minus,
18717
- Palette,
18718
- Plus as Plus2,
18719
- Star
18720
- } from "lucide-react";
18658
+ import { useCallback as useCallback3, useEffect as useEffect6, useRef as useRef5, useState as useState8 } from "react";
18659
+ import { Star } from "lucide-react";
18721
18660
 
18722
18661
  // src/lib/ai-media-strip-tree.ts
18723
18662
  var MEDIA_STRIP_ITEM_MIN = 1;
@@ -18831,6 +18770,14 @@ function setMediaStripItemAsset(tree, index, assetId) {
18831
18770
  return true;
18832
18771
  });
18833
18772
  }
18773
+ function setMediaStripItemAlt(tree, index, alt) {
18774
+ return withMediaStrip(tree, (block) => {
18775
+ const items = block.items;
18776
+ if (index < 0 || index >= items.length) return false;
18777
+ items[index] = { ...items[index], alt };
18778
+ return true;
18779
+ });
18780
+ }
18834
18781
  function setMediaStripItemLink(tree, index, link) {
18835
18782
  return withMediaStrip(tree, (block) => {
18836
18783
  const items = block.items;
@@ -18848,117 +18795,11 @@ function setMediaStripItemLink(tree, index, link) {
18848
18795
  });
18849
18796
  }
18850
18797
 
18851
- // src/ui/dialog.tsx
18852
- import * as React9 from "react";
18853
- import { Dialog as DialogPrimitive } from "radix-ui";
18854
- import { X as X2 } from "lucide-react";
18855
- import { jsx as jsx20, jsxs as jsxs11 } from "react/jsx-runtime";
18856
- function Dialog2({
18857
- ...props
18858
- }) {
18859
- return /* @__PURE__ */ jsx20(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
18860
- }
18861
- function DialogPortal({
18862
- ...props
18863
- }) {
18864
- return /* @__PURE__ */ jsx20(DialogPrimitive.Portal, { ...props });
18865
- }
18866
- function DialogOverlay({
18867
- className,
18868
- ...props
18869
- }) {
18870
- return /* @__PURE__ */ jsx20(
18871
- DialogPrimitive.Overlay,
18872
- {
18873
- "data-slot": "dialog-overlay",
18874
- "data-ohw-link-modal-root": "",
18875
- className: cn("fixed inset-0 z-[2147483646] bg-black/50", className),
18876
- ...props
18877
- }
18878
- );
18879
- }
18880
- var DialogContent = React9.forwardRef(
18881
- ({ className, children, showCloseButton = true, container, ...props }, ref) => {
18882
- const positionMode = container ? "absolute" : "fixed";
18883
- return /* @__PURE__ */ jsxs11(DialogPortal, { container: container ?? void 0, children: [
18884
- /* @__PURE__ */ jsx20(DialogOverlay, { className: cn(positionMode, "inset-0") }),
18885
- /* @__PURE__ */ jsxs11(
18886
- DialogPrimitive.Content,
18887
- {
18888
- ref,
18889
- "data-slot": "dialog-content",
18890
- className: cn(
18891
- positionMode,
18892
- "left-1/2 top-1/2 z-[2147483647] flex w-full max-w-[448px] -translate-x-1/2 -translate-y-1/2 flex-col overflow-visible",
18893
- "rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
18894
- container ? "max-h-[calc(100%-32px)]" : "max-h-[calc(100vh-32px)]",
18895
- className
18896
- ),
18897
- onMouseDown: (e) => e.stopPropagation(),
18898
- ...props,
18899
- children: [
18900
- children,
18901
- showCloseButton ? /* @__PURE__ */ jsx20(
18902
- DialogPrimitive.Close,
18903
- {
18904
- type: "button",
18905
- className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
18906
- "aria-label": "Close",
18907
- children: /* @__PURE__ */ jsx20(X2, { size: 16, "aria-hidden": true })
18908
- }
18909
- ) : null
18910
- ]
18911
- }
18912
- )
18913
- ] });
18914
- }
18915
- );
18916
- DialogContent.displayName = DialogPrimitive.Content.displayName;
18917
- function DialogHeader({
18918
- className,
18919
- ...props
18920
- }) {
18921
- return /* @__PURE__ */ jsx20("div", { className: cn("flex flex-col gap-1.5", className), ...props });
18922
- }
18923
- function DialogFooter({
18924
- className,
18925
- ...props
18926
- }) {
18927
- return /* @__PURE__ */ jsx20(
18928
- "div",
18929
- {
18930
- className: cn("flex items-center justify-end gap-2", className),
18931
- ...props
18932
- }
18933
- );
18934
- }
18935
- var DialogTitle = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx20(
18936
- DialogPrimitive.Title,
18937
- {
18938
- ref,
18939
- className: cn(
18940
- "text-lg font-semibold leading-none tracking-tight text-card-foreground",
18941
- className
18942
- ),
18943
- ...props
18944
- }
18945
- ));
18946
- DialogTitle.displayName = DialogPrimitive.Title.displayName;
18947
- var DialogDescription = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx20(
18948
- DialogPrimitive.Description,
18949
- {
18950
- ref,
18951
- className: cn("text-sm text-muted-foreground", className),
18952
- ...props
18953
- }
18954
- ));
18955
- DialogDescription.displayName = DialogPrimitive.Description.displayName;
18956
- var DialogClose = DialogPrimitive.Close;
18957
-
18958
18798
  // src/ui/ai-section/AiMediaStripOverlay.tsx
18959
- import { Fragment as Fragment7, jsx as jsx21, jsxs as jsxs12 } from "react/jsx-runtime";
18960
- var PRIMARY3 = "var(--ohw-primary, #0885FE)";
18961
- var CHROME_SELECTOR = "[data-ohw-toolbar], [data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-link-modal-root], [data-ohw-section-picker], [data-ohw-ai-review], [data-ohw-more-menu]";
18799
+ import { Fragment as Fragment7, jsx as jsx20, jsxs as jsxs11 } from "react/jsx-runtime";
18800
+ var PRIMARY3 = "#0885FE";
18801
+ var OHW_BLUE = `var(--ohw-primary, ${PRIMARY3})`;
18802
+ var OHW_BLUE_DARK = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY3}) 60%, #000000)`;
18962
18803
  function findMediaStripItemElement(sectionId, index) {
18963
18804
  const sectionEl = findByInstanceId(sectionId);
18964
18805
  if (!sectionEl) return null;
@@ -18968,35 +18809,14 @@ function findMediaStripItemElement(sectionId, index) {
18968
18809
  }
18969
18810
  return null;
18970
18811
  }
18971
- function itemRefFromTarget(target) {
18972
- if (!(target instanceof Element)) return null;
18973
- if (target.closest(CHROME_SELECTOR)) return null;
18974
- const itemEl = target.closest("[data-ohw-media-strip-item]");
18975
- if (!itemEl) return null;
18976
- const sectionEl = itemEl.closest("[data-ohw-section]");
18977
- const unit = sectionEl ? movableUnit(sectionEl) : null;
18978
- const sectionId = unit ? instanceIdOf(unit) || null : null;
18979
- const index = Number(itemEl.dataset.ohwMediaStripIndex);
18980
- if (!sectionId || Number.isNaN(index)) return null;
18981
- return { sectionId, index };
18982
- }
18983
- function sameRef(a, b) {
18984
- return a?.sectionId === b?.sectionId && a?.index === b?.index;
18985
- }
18986
- function rectOf(el) {
18812
+ function readRect2(ref) {
18813
+ const el = ref && findMediaStripItemElement(ref.sectionId, ref.index);
18987
18814
  if (!el) return null;
18988
18815
  const r2 = el.getBoundingClientRect();
18989
18816
  if (r2.width <= 0 || r2.height <= 0) return null;
18990
18817
  return { top: r2.top, left: r2.left, width: r2.width, height: r2.height };
18991
18818
  }
18992
- function readRect2(ref) {
18993
- return rectOf(ref && findMediaStripItemElement(ref.sectionId, ref.index));
18994
- }
18995
- function readStripRect(ref) {
18996
- const item = ref && findMediaStripItemElement(ref.sectionId, ref.index);
18997
- return rectOf(item?.closest("[data-ohw-media-strip]") ?? item);
18998
- }
18999
- function useLiveItemRect(ref, strip = false) {
18819
+ function useLiveItemRect(ref) {
19000
18820
  const [rect, setRect] = useState8(null);
19001
18821
  useEffect6(() => {
19002
18822
  if (!ref) {
@@ -19004,7 +18824,7 @@ function useLiveItemRect(ref, strip = false) {
19004
18824
  return;
19005
18825
  }
19006
18826
  const update = () => {
19007
- const next = strip ? readStripRect(ref) : readRect2(ref);
18827
+ const next = readRect2(ref);
19008
18828
  setRect(
19009
18829
  (prev) => prev && next && prev.top === next.top && prev.left === next.left && prev.width === next.width && prev.height === next.height ? prev : next
19010
18830
  );
@@ -19019,49 +18839,9 @@ function useLiveItemRect(ref, strip = false) {
19019
18839
  window.removeEventListener("resize", update);
19020
18840
  clearInterval(interval);
19021
18841
  };
19022
- }, [ref?.sectionId, ref?.index, strip]);
18842
+ }, [ref?.sectionId, ref?.index]);
19023
18843
  return rect;
19024
18844
  }
19025
- var TOOLBAR_STRIP_GAP = 52;
19026
- var TOOLBAR_STRIP_GAP_MOBILE = 42;
19027
- var TOOLBAR_EDGE_MARGIN2 = 4;
19028
- function StripToolbarSlot({ stripRect, children }) {
19029
- const slotRef = useRef5(null);
19030
- const [shift, setShift] = useState8(0);
19031
- const centerX = stripRect.left + stripRect.width / 2;
19032
- useLayoutEffect4(() => {
19033
- const slot = slotRef.current;
19034
- if (!slot) return;
19035
- const clamp = () => {
19036
- const w = slot.offsetWidth;
19037
- if (w <= 0) return;
19038
- const half = w / 2;
19039
- const clamped = Math.max(TOOLBAR_EDGE_MARGIN2 + half, Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN2 - half));
19040
- setShift((prev) => Math.abs(prev - (clamped - centerX)) < 0.5 ? prev : clamped - centerX);
19041
- };
19042
- clamp();
19043
- const ro = new ResizeObserver(clamp);
19044
- ro.observe(slot);
19045
- return () => ro.disconnect();
19046
- }, [centerX, children]);
19047
- const mobile = window.innerWidth <= 480;
19048
- return /* @__PURE__ */ jsx21(
19049
- "div",
19050
- {
19051
- ref: slotRef,
19052
- "data-ohw-bridge": "",
19053
- "data-ohw-item-interaction": "",
19054
- className: "pointer-events-auto fixed",
19055
- style: {
19056
- top: stripRect.top - (mobile ? TOOLBAR_STRIP_GAP_MOBILE : TOOLBAR_STRIP_GAP),
19057
- left: centerX + shift,
19058
- transform: "translate(-50%, -100%)",
19059
- zIndex: 2147483645
19060
- },
19061
- children
19062
- }
19063
- );
19064
- }
19065
18845
  function requestValidateTree(postToParent2, tree) {
19066
18846
  return new Promise((resolve) => {
19067
18847
  const id = `ai-validate-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
@@ -19098,72 +18878,116 @@ function requestAssetPick(postToParent2) {
19098
18878
  postToParent2({ type: "ow:asset-pick", id });
19099
18879
  });
19100
18880
  }
19101
- function ToolbarAction({
18881
+ function Pill({
19102
18882
  label,
19103
18883
  onClick,
19104
- disabled = false,
19105
- active = false,
19106
- children
18884
+ disabled,
18885
+ active
19107
18886
  }) {
19108
- const button = /* @__PURE__ */ jsx21(CustomToolbarButton, { type: "button", "aria-label": label, disabled, active, onClick, className: "size-8", children });
19109
- return /* @__PURE__ */ jsxs12(Tooltip, { children: [
19110
- /* @__PURE__ */ jsx21(TooltipTrigger, { asChild: true, children: disabled ? /* @__PURE__ */ jsx21("span", { className: "inline-flex", children: button }) : button }),
19111
- /* @__PURE__ */ jsx21(TooltipContent, { side: "bottom", sideOffset: 8, children: label })
19112
- ] });
19113
- }
19114
- function ToolbarSeparator() {
19115
- return /* @__PURE__ */ jsx21("span", { "aria-hidden": true, className: "mx-0.5 block h-6 w-px shrink-0 bg-border" });
18887
+ return /* @__PURE__ */ jsx20(
18888
+ "button",
18889
+ {
18890
+ type: "button",
18891
+ onClick,
18892
+ disabled,
18893
+ style: {
18894
+ height: 28,
18895
+ padding: "0 10px",
18896
+ display: "inline-flex",
18897
+ alignItems: "center",
18898
+ gap: 4,
18899
+ borderRadius: 999,
18900
+ border: "none",
18901
+ cursor: disabled ? "default" : "pointer",
18902
+ background: active ? OHW_BLUE_DARK : "#ffffff",
18903
+ color: active ? "#ffffff" : OHW_BLUE_DARK,
18904
+ opacity: disabled ? 0.4 : 1,
18905
+ fontSize: 12,
18906
+ lineHeight: "16px",
18907
+ fontFamily: "Figtree, ui-sans-serif, system-ui, sans-serif",
18908
+ whiteSpace: "nowrap",
18909
+ flex: "0 0 auto"
18910
+ },
18911
+ children: label
18912
+ }
18913
+ );
19116
18914
  }
19117
- function OptionMenu({
19118
- label,
19119
- value,
19120
- options,
19121
- onChange
19122
- }) {
19123
- const current = options.find((o) => o.value === value) ?? options[0];
19124
- return /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
19125
- /* @__PURE__ */ jsx21("span", { className: "text-xs text-muted-foreground", children: label }),
19126
- /* @__PURE__ */ jsxs12(DropdownMenu, { modal: false, children: [
19127
- /* @__PURE__ */ jsx21(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs12(
19128
- "button",
19129
- {
19130
- type: "button",
19131
- "aria-label": label,
19132
- className: "inline-flex h-8 min-w-[76px] items-center gap-[7px] rounded-[6px] bg-transparent px-2 text-[13px] font-medium text-foreground transition-colors hover:bg-muted data-[state=open]:bg-muted",
19133
- children: [
19134
- /* @__PURE__ */ jsx21("span", { className: "flex-1 text-left", children: current.label }),
19135
- /* @__PURE__ */ jsx21(ChevronDown3, { size: 16, style: { pointerEvents: "none" } })
19136
- ]
19137
- }
19138
- ) }),
19139
- /* @__PURE__ */ jsx21(DropdownMenuContent, { align: "end", sideOffset: 8, className: "w-[132px] min-w-[132px] p-1", children: options.map((option) => {
19140
- const selected = option.value === value;
19141
- return /* @__PURE__ */ jsxs12(
19142
- DropdownMenuItem,
18915
+ function AltInput({ value, onCommit }) {
18916
+ const [draft, setDraft] = useState8(value);
18917
+ useEffect6(() => setDraft(value), [value]);
18918
+ return /* @__PURE__ */ jsxs11(
18919
+ "div",
18920
+ {
18921
+ style: {
18922
+ display: "flex",
18923
+ alignItems: "center",
18924
+ gap: 6,
18925
+ height: 28,
18926
+ padding: "0 12px",
18927
+ borderRadius: 999,
18928
+ background: "#ffffff",
18929
+ width: "100%",
18930
+ maxWidth: 260,
18931
+ boxSizing: "border-box"
18932
+ },
18933
+ children: [
18934
+ /* @__PURE__ */ jsx20(
18935
+ "span",
19143
18936
  {
19144
- role: "menuitemradio",
19145
- "aria-checked": selected,
19146
- onSelect: () => onChange(option.value),
19147
- className: cn("grid grid-cols-[1fr_16px] gap-2 py-2 pl-2 pr-2 text-[13px]", selected && "bg-muted"),
19148
- children: [
19149
- /* @__PURE__ */ jsx21("span", { children: option.label }),
19150
- selected && /* @__PURE__ */ jsx21(Check2, { size: 16, className: "text-bridge-primary" })
19151
- ]
19152
- },
19153
- option.value
19154
- );
19155
- }) })
19156
- ] })
19157
- ] });
18937
+ style: {
18938
+ flex: "0 0 auto",
18939
+ color: OHW_BLUE_DARK,
18940
+ fontSize: 10,
18941
+ fontWeight: 700,
18942
+ textTransform: "uppercase",
18943
+ letterSpacing: 0.4
18944
+ },
18945
+ children: "Alt"
18946
+ }
18947
+ ),
18948
+ /* @__PURE__ */ jsx20(
18949
+ "input",
18950
+ {
18951
+ type: "text",
18952
+ value: draft,
18953
+ onChange: (e) => setDraft(e.target.value),
18954
+ onBlur: () => {
18955
+ if (draft !== value) onCommit(draft);
18956
+ },
18957
+ onKeyDown: (e) => {
18958
+ if (e.key === "Enter") e.currentTarget.blur();
18959
+ if (e.key === "Escape") {
18960
+ setDraft(value);
18961
+ e.currentTarget.blur();
18962
+ }
18963
+ },
18964
+ placeholder: "Describe this image",
18965
+ "aria-label": "Image alt text",
18966
+ style: {
18967
+ flex: 1,
18968
+ minWidth: 0,
18969
+ border: "none",
18970
+ outline: "none",
18971
+ background: "transparent",
18972
+ fontSize: 12,
18973
+ lineHeight: "16px",
18974
+ fontFamily: "Figtree, ui-sans-serif, system-ui, sans-serif",
18975
+ color: "#1f2937"
18976
+ }
18977
+ }
18978
+ )
18979
+ ]
18980
+ }
18981
+ );
19158
18982
  }
19159
18983
  var VARIANTS = [
19160
18984
  { value: "logos", label: "Logos" },
19161
18985
  { value: "reel", label: "Reel" }
19162
18986
  ];
19163
18987
  var ALIGNS = [
19164
- { value: "left", label: "Align left", Icon: AlignLeft },
19165
- { value: "center", label: "Align center", Icon: AlignCenter },
19166
- { value: "right", label: "Align right", Icon: AlignRight }
18988
+ { value: "left", label: "Left" },
18989
+ { value: "center", label: "Center" },
18990
+ { value: "right", label: "Right" }
19167
18991
  ];
19168
18992
  var HOVERS = [
19169
18993
  { value: "none", label: "None" },
@@ -19171,104 +18995,104 @@ var HOVERS = [
19171
18995
  { value: "zoom", label: "Zoom" },
19172
18996
  { value: "fade", label: "Fade" }
19173
18997
  ];
18998
+ var TREATMENTS = [
18999
+ { value: "color", label: "Color" },
19000
+ { value: "grayscale", label: "Grayscale" }
19001
+ ];
19174
19002
  var HTTP_URL_RE = /^https?:\/\/\S+$/i;
19175
- function LinkModal({
19176
- href,
19177
- linkTarget,
19178
- onClose,
19179
- onSave
19180
- }) {
19181
- const [draft, setDraft] = useState8(href);
19182
- const [newTab, setNewTab] = useState8(linkTarget !== "same-tab");
19003
+ function LinkInput({ value, onCommit }) {
19004
+ const [draft, setDraft] = useState8(value);
19183
19005
  const [invalid, setInvalid] = useState8(false);
19184
- const save = () => {
19006
+ useEffect6(() => {
19007
+ setDraft(value);
19008
+ setInvalid(false);
19009
+ }, [value]);
19010
+ const commit = () => {
19185
19011
  const next = draft.trim();
19186
- if (next && !HTTP_URL_RE.test(next)) {
19187
- setInvalid(true);
19188
- return;
19189
- }
19190
- onSave(next, newTab ? "new-tab" : "same-tab");
19191
- onClose();
19012
+ if (next === value) return setInvalid(false);
19013
+ if (next && !HTTP_URL_RE.test(next)) return setInvalid(true);
19014
+ setInvalid(false);
19015
+ onCommit(next);
19192
19016
  };
19193
- return /* @__PURE__ */ jsx21(Dialog2, { open: true, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxs12(DialogContent, { "data-ohw-link-modal-root": "", className: "max-w-[400px] gap-4 p-5", children: [
19194
- /* @__PURE__ */ jsx21(DialogHeader, { children: /* @__PURE__ */ jsx21(DialogTitle, { className: "text-base", children: "Image link" }) }),
19195
- /* @__PURE__ */ jsxs12("div", { className: "flex flex-col gap-3", children: [
19196
- /* @__PURE__ */ jsx21(
19197
- "input",
19198
- {
19199
- autoFocus: true,
19200
- type: "text",
19201
- inputMode: "url",
19202
- value: draft,
19203
- onChange: (e) => {
19204
- setDraft(e.target.value);
19205
- setInvalid(false);
19206
- },
19207
- onKeyDown: (e) => {
19208
- if (e.key === "Enter") save();
19209
- },
19210
- placeholder: "https://example.com",
19211
- "aria-label": "Image link URL",
19212
- "aria-invalid": invalid,
19213
- className: cn(
19214
- "h-9 w-full rounded-[6px] border border-border bg-background px-3 text-[13px] text-foreground outline-none placeholder:text-muted-foreground/70 focus:border-bridge-primary",
19215
- invalid && "border-destructive focus:border-destructive"
19216
- )
19217
- }
19218
- ),
19219
- invalid && /* @__PURE__ */ jsx21("p", { className: "text-xs text-destructive", children: "Enter a full URL starting with http:// or https://" }),
19220
- /* @__PURE__ */ jsxs12("label", { className: "flex items-center gap-2 text-[13px] text-foreground", children: [
19221
- /* @__PURE__ */ jsx21("input", { type: "checkbox", checked: newTab, onChange: (e) => setNewTab(e.target.checked) }),
19222
- "Open in new tab"
19223
- ] })
19224
- ] }),
19225
- /* @__PURE__ */ jsxs12(DialogFooter, { children: [
19226
- href && /* @__PURE__ */ jsx21(
19227
- "button",
19228
- {
19229
- type: "button",
19230
- onClick: () => {
19231
- onSave("", "new-tab");
19232
- onClose();
19233
- },
19234
- className: "mr-auto rounded-[6px] px-3 py-2 text-[13px] font-medium text-destructive transition-colors hover:bg-muted",
19235
- children: "Remove link"
19236
- }
19237
- ),
19238
- /* @__PURE__ */ jsx21(
19239
- "button",
19240
- {
19241
- type: "button",
19242
- onClick: onClose,
19243
- className: "rounded-[6px] px-3 py-2 text-[13px] font-medium text-foreground transition-colors hover:bg-muted",
19244
- children: "Cancel"
19245
- }
19246
- ),
19247
- /* @__PURE__ */ jsx21(
19248
- "button",
19249
- {
19250
- type: "button",
19251
- onClick: save,
19252
- className: "rounded-[6px] bg-bridge-primary px-3 py-2 text-[13px] font-medium text-primary-foreground transition-opacity hover:opacity-90",
19253
- children: "Save"
19254
- }
19255
- )
19256
- ] })
19257
- ] }) });
19017
+ return /* @__PURE__ */ jsxs11(
19018
+ "div",
19019
+ {
19020
+ style: {
19021
+ display: "flex",
19022
+ alignItems: "center",
19023
+ gap: 6,
19024
+ height: 28,
19025
+ padding: "0 12px",
19026
+ borderRadius: 999,
19027
+ background: "#ffffff",
19028
+ width: "100%",
19029
+ maxWidth: 260,
19030
+ boxSizing: "border-box",
19031
+ boxShadow: invalid ? "inset 0 0 0 1.5px #EF4444" : "none"
19032
+ },
19033
+ children: [
19034
+ /* @__PURE__ */ jsx20(
19035
+ "span",
19036
+ {
19037
+ style: {
19038
+ flex: "0 0 auto",
19039
+ color: OHW_BLUE_DARK,
19040
+ fontSize: 10,
19041
+ fontWeight: 700,
19042
+ textTransform: "uppercase",
19043
+ letterSpacing: 0.4
19044
+ },
19045
+ children: "Link"
19046
+ }
19047
+ ),
19048
+ /* @__PURE__ */ jsx20(
19049
+ "input",
19050
+ {
19051
+ type: "text",
19052
+ inputMode: "url",
19053
+ value: draft,
19054
+ onChange: (e) => setDraft(e.target.value),
19055
+ onBlur: commit,
19056
+ onKeyDown: (e) => {
19057
+ if (e.key === "Enter") e.currentTarget.blur();
19058
+ if (e.key === "Escape") {
19059
+ setDraft(value);
19060
+ setInvalid(false);
19061
+ e.currentTarget.blur();
19062
+ }
19063
+ },
19064
+ placeholder: "https://example.com",
19065
+ "aria-label": "Image link URL",
19066
+ "aria-invalid": invalid,
19067
+ style: {
19068
+ flex: 1,
19069
+ minWidth: 0,
19070
+ border: "none",
19071
+ outline: "none",
19072
+ background: "transparent",
19073
+ fontSize: 12,
19074
+ lineHeight: "16px",
19075
+ fontFamily: "Figtree, ui-sans-serif, system-ui, sans-serif",
19076
+ color: "#1f2937"
19077
+ }
19078
+ }
19079
+ )
19080
+ ]
19081
+ }
19082
+ );
19258
19083
  }
19259
19084
  function MediaStripToolbar({
19260
19085
  entry,
19261
19086
  index,
19262
19087
  pending,
19263
19088
  error,
19264
- itemEditOn,
19265
- onToggleItemEdit,
19266
19089
  onSetVariant,
19267
19090
  onSetAlign,
19268
19091
  onSetHover,
19269
19092
  onSetTreatment,
19270
19093
  onResize,
19271
- onOpenLink,
19094
+ onSetAlt,
19095
+ onSetLink,
19272
19096
  onAddItem,
19273
19097
  onRemoveItem,
19274
19098
  onChangeImage,
@@ -19277,102 +19101,81 @@ function MediaStripToolbar({
19277
19101
  }) {
19278
19102
  const items = mediaStripItems(entry.tree);
19279
19103
  const itemCount = items.length;
19104
+ const currentAlt = items[index]?.alt ?? "";
19105
+ const currentHref = items[index]?.href ?? "";
19106
+ const currentTarget = items[index]?.linkTarget === "same-tab" ? "same-tab" : "new-tab";
19280
19107
  const slots = findMediaStripSlots(entry.tree);
19281
19108
  const variant = slots?.variant === "reel" ? "reel" : "logos";
19282
19109
  const align = slots?.align === "left" || slots?.align === "right" ? slots.align : "center";
19283
19110
  const hover = slots?.hover === "none" || slots?.hover === "lift" || slots?.hover === "zoom" || slots?.hover === "fade" ? slots.hover : "lift";
19284
19111
  const treatment = slots?.treatment === "color" || slots?.treatment === "grayscale" ? slots.treatment : variant === "logos" ? "grayscale" : "color";
19285
19112
  const sizeValue = variant === "logos" ? typeof slots?.logoMaxHeight === "number" ? slots.logoMaxHeight : 48 : typeof slots?.cardWidth === "number" ? slots.cardWidth : 240;
19286
- const mobile = window.innerWidth <= 480;
19287
- const gatedLabel = (label) => itemEditOn ? label : "Turn on the star to edit this image";
19288
- return /* @__PURE__ */ jsx21(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsxs12(
19113
+ const isMobile = window.innerWidth <= 480;
19114
+ const rowStyle = isMobile ? {
19115
+ display: "flex",
19116
+ gap: 6,
19117
+ flexWrap: "nowrap",
19118
+ justifyContent: "center",
19119
+ overflowX: "auto",
19120
+ WebkitOverflowScrolling: "touch",
19121
+ width: "100%",
19122
+ scrollbarWidth: "none"
19123
+ } : { display: "flex", gap: 6, flexWrap: "nowrap", justifyContent: "center" };
19124
+ return /* @__PURE__ */ jsxs11(
19289
19125
  "div",
19290
19126
  {
19291
19127
  "data-ohw-bridge": "",
19292
19128
  "data-ohw-toolbar": "",
19293
19129
  onMouseDown: (e) => e.stopPropagation(),
19294
- className: cn("flex flex-col items-center gap-1.5 font-sans", pending && "pointer-events-none opacity-60"),
19295
- style: { maxWidth: mobile ? "calc(100vw - 24px)" : void 0 },
19130
+ style: {
19131
+ display: "flex",
19132
+ flexDirection: "column",
19133
+ gap: 6,
19134
+ alignItems: "center",
19135
+ background: OHW_BLUE,
19136
+ padding: 8,
19137
+ borderRadius: 12,
19138
+ boxShadow: "0 8px 24px rgba(0,0,0,0.25)",
19139
+ pointerEvents: pending ? "none" : "auto",
19140
+ opacity: pending ? 0.6 : 1,
19141
+ maxWidth: isMobile ? "calc(100vw - 24px)" : "none"
19142
+ },
19296
19143
  children: [
19297
- /* @__PURE__ */ jsxs12(
19298
- "div",
19299
- {
19300
- className: cn(
19301
- "flex items-center gap-1 rounded-[10px] border border-border bg-background p-1 whitespace-nowrap",
19302
- "shadow-[0px_10px_15px_-3px_rgba(0,0,0,0.1),0px_4px_6px_-4px_rgba(0,0,0,0.1)]",
19303
- mobile ? "flex-wrap justify-center" : "flex-nowrap"
19304
- ),
19305
- children: [
19306
- /* @__PURE__ */ jsxs12(Tooltip, { children: [
19307
- /* @__PURE__ */ jsx21(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx21(
19308
- CustomToolbarButton,
19309
- {
19310
- type: "button",
19311
- "aria-label": itemEditOn ? "Done editing this image" : "Edit this image",
19312
- "aria-pressed": itemEditOn,
19313
- onClick: onToggleItemEdit,
19314
- className: "group size-8",
19315
- children: /* @__PURE__ */ jsx21(
19316
- Star,
19317
- {
19318
- size: 16,
19319
- fill: "currentColor",
19320
- strokeWidth: 0,
19321
- className: cn(
19322
- "transition-colors",
19323
- itemEditOn ? "text-[#f59e0b] group-hover:text-[#d97706]" : "text-[#d6d3d1] group-hover:text-[#fbbf24]"
19324
- ),
19325
- style: { pointerEvents: "none" }
19326
- }
19327
- )
19328
- }
19329
- ) }),
19330
- /* @__PURE__ */ jsx21(TooltipContent, { side: "bottom", sideOffset: 8, children: itemEditOn ? "Done editing this image" : "Edit this image" })
19331
- ] }),
19332
- /* @__PURE__ */ jsx21(ToolbarSeparator, {}),
19333
- /* @__PURE__ */ jsx21(ToolbarAction, { label: gatedLabel("Change image"), onClick: onChangeImage, disabled: !itemEditOn, children: /* @__PURE__ */ jsx21(ImageIcon2, { size: 16, style: { pointerEvents: "none" } }) }),
19334
- /* @__PURE__ */ jsx21(ToolbarAction, { label: gatedLabel("Link"), onClick: onOpenLink, disabled: !itemEditOn, children: /* @__PURE__ */ jsx21(LinkIcon, { size: 16, style: { pointerEvents: "none" } }) }),
19335
- /* @__PURE__ */ jsx21(ToolbarAction, { label: gatedLabel("Move left"), onClick: onMoveLeft, disabled: !itemEditOn || index <= 0, children: /* @__PURE__ */ jsx21(ArrowLeft2, { size: 16, style: { pointerEvents: "none" } }) }),
19336
- /* @__PURE__ */ jsx21(
19337
- ToolbarAction,
19338
- {
19339
- label: gatedLabel("Move right"),
19340
- onClick: onMoveRight,
19341
- disabled: !itemEditOn || index >= itemCount - 1,
19342
- children: /* @__PURE__ */ jsx21(ArrowRight2, { size: 16, style: { pointerEvents: "none" } })
19343
- }
19344
- ),
19345
- /* @__PURE__ */ jsx21(ToolbarAction, { label: gatedLabel("Remove image"), onClick: onRemoveItem, disabled: !itemEditOn || itemCount <= 1, children: /* @__PURE__ */ jsx21(ImageMinus, { size: 16, style: { pointerEvents: "none" } }) }),
19346
- /* @__PURE__ */ jsx21(ToolbarSeparator, {}),
19347
- /* @__PURE__ */ jsx21(OptionMenu, { label: "Layout", value: variant, options: VARIANTS, onChange: onSetVariant }),
19348
- /* @__PURE__ */ jsx21(OptionMenu, { label: "Hover", value: hover, options: HOVERS, onChange: onSetHover }),
19349
- /* @__PURE__ */ jsx21(
19350
- ToolbarAction,
19351
- {
19352
- label: treatment === "grayscale" ? "Show in color" : "Show grayscale",
19353
- active: treatment === "grayscale",
19354
- onClick: () => onSetTreatment(treatment === "grayscale" ? "color" : "grayscale"),
19355
- children: /* @__PURE__ */ jsx21(Palette, { size: 16, style: { pointerEvents: "none" } })
19356
- }
19357
- ),
19358
- /* @__PURE__ */ jsx21(ToolbarSeparator, {}),
19359
- ALIGNS.map(({ value, label, Icon }) => /* @__PURE__ */ jsx21(ToolbarAction, { label, active: align === value, onClick: () => onSetAlign(value), children: /* @__PURE__ */ jsx21(Icon, { size: 16, style: { pointerEvents: "none" } }) }, value)),
19360
- /* @__PURE__ */ jsx21(ToolbarSeparator, {}),
19361
- /* @__PURE__ */ jsx21(ToolbarAction, { label: "Smaller", onClick: () => onResize(-8), children: /* @__PURE__ */ jsx21(Minus, { size: 16, style: { pointerEvents: "none" } }) }),
19362
- /* @__PURE__ */ jsxs12("span", { className: "min-w-[40px] text-center text-xs tabular-nums text-muted-foreground", children: [
19363
- sizeValue,
19364
- "px"
19365
- ] }),
19366
- /* @__PURE__ */ jsx21(ToolbarAction, { label: "Larger", onClick: () => onResize(8), children: /* @__PURE__ */ jsx21(Plus2, { size: 16, style: { pointerEvents: "none" } }) }),
19367
- /* @__PURE__ */ jsx21(ToolbarSeparator, {}),
19368
- /* @__PURE__ */ jsx21(ToolbarAction, { label: "Add image", onClick: onAddItem, children: /* @__PURE__ */ jsx21(ImagePlus, { size: 16, style: { pointerEvents: "none" } }) })
19369
- ]
19370
- }
19371
- ),
19372
- error && /* @__PURE__ */ jsx21("div", { className: "max-w-[260px] rounded-md bg-background px-2 py-1 text-center text-[11px] text-destructive shadow-sm", children: error })
19144
+ /* @__PURE__ */ jsxs11("div", { style: rowStyle, children: [
19145
+ VARIANTS.map((v) => /* @__PURE__ */ jsx20(Pill, { label: v.label, active: variant === v.value, onClick: () => onSetVariant(v.value) }, v.value)),
19146
+ ALIGNS.map((a) => /* @__PURE__ */ jsx20(Pill, { label: a.label, active: align === a.value, onClick: () => onSetAlign(a.value) }, a.value))
19147
+ ] }),
19148
+ /* @__PURE__ */ jsxs11("div", { style: rowStyle, children: [
19149
+ HOVERS.map((h) => /* @__PURE__ */ jsx20(Pill, { label: h.label, active: hover === h.value, onClick: () => onSetHover(h.value) }, h.value)),
19150
+ TREATMENTS.map((t) => /* @__PURE__ */ jsx20(Pill, { label: t.label, active: treatment === t.value, onClick: () => onSetTreatment(t.value) }, t.value))
19151
+ ] }),
19152
+ /* @__PURE__ */ jsxs11("div", { style: { display: "flex", gap: 6, alignItems: "center" }, children: [
19153
+ /* @__PURE__ */ jsx20(Pill, { label: "\u2212", onClick: () => onResize(-8) }),
19154
+ /* @__PURE__ */ jsxs11("span", { style: { color: "#ffffff", fontSize: 12, minWidth: 34, textAlign: "center" }, children: [
19155
+ sizeValue,
19156
+ "px"
19157
+ ] }),
19158
+ /* @__PURE__ */ jsx20(Pill, { label: "+", onClick: () => onResize(8) })
19159
+ ] }),
19160
+ /* @__PURE__ */ jsx20("div", { style: { display: "flex", justifyContent: "center", width: "100%" }, children: /* @__PURE__ */ jsx20(AltInput, { value: currentAlt, onCommit: onSetAlt }) }),
19161
+ /* @__PURE__ */ jsx20("div", { style: { display: "flex", justifyContent: "center", width: "100%" }, children: /* @__PURE__ */ jsx20(LinkInput, { value: currentHref, onCommit: (href) => onSetLink(href, currentTarget) }) }),
19162
+ currentHref && /* @__PURE__ */ jsxs11("div", { style: rowStyle, children: [
19163
+ /* @__PURE__ */ jsx20(Pill, { label: "New tab", active: currentTarget === "new-tab", onClick: () => onSetLink(currentHref, "new-tab") }),
19164
+ /* @__PURE__ */ jsx20(Pill, { label: "Same tab", active: currentTarget === "same-tab", onClick: () => onSetLink(currentHref, "same-tab") })
19165
+ ] }),
19166
+ /* @__PURE__ */ jsxs11("div", { style: rowStyle, children: [
19167
+ /* @__PURE__ */ jsx20(Pill, { label: "Change Image", onClick: onChangeImage }),
19168
+ /* @__PURE__ */ jsx20(Pill, { label: "+ Item", onClick: onAddItem }),
19169
+ /* @__PURE__ */ jsx20(Pill, { label: "\u2212 Item", onClick: onRemoveItem, disabled: itemCount <= 1 })
19170
+ ] }),
19171
+ /* @__PURE__ */ jsxs11("div", { style: rowStyle, children: [
19172
+ /* @__PURE__ */ jsx20(Pill, { label: "\u2190 Move", onClick: onMoveLeft, disabled: index <= 0 }),
19173
+ /* @__PURE__ */ jsx20(Pill, { label: "\u2192 Move", onClick: onMoveRight, disabled: index >= itemCount - 1 })
19174
+ ] }),
19175
+ error && /* @__PURE__ */ jsx20("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
19373
19176
  ]
19374
19177
  }
19375
- ) });
19178
+ );
19376
19179
  }
19377
19180
  function findMediaStripSlots(tree) {
19378
19181
  const stack = tree.rows.flatMap((row) => row.blocks);
@@ -19391,23 +19194,31 @@ function AiMediaStripOverlay({
19391
19194
  }) {
19392
19195
  const [hover, setHover] = useState8(null);
19393
19196
  const [active, setActive] = useState8(null);
19394
- const [itemEditOn, setItemEditOn] = useState8(false);
19395
- const [linkOpen, setLinkOpen] = useState8(false);
19396
19197
  const [pending, setPending] = useState8(false);
19397
19198
  const [error, setError] = useState8(null);
19398
19199
  const activeRef = useRef5(null);
19399
19200
  activeRef.current = active;
19400
- const linkOpenRef = useRef5(false);
19401
- linkOpenRef.current = linkOpen;
19402
19201
  useEffect6(() => {
19403
19202
  const onMove = (e) => {
19404
- const ref = itemRefFromTarget(e.target);
19405
19203
  const t = e.target;
19406
- const overEditable = Boolean(t?.closest?.("[data-ohw-editable]"));
19407
- setHover((prev) => {
19408
- const next = ref && !overEditable ? ref : null;
19409
- return sameRef(prev, next) ? prev : next;
19410
- });
19204
+ if (!(t instanceof HTMLElement)) return;
19205
+ if (t.closest("[data-ohw-toolbar], [data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-link-modal-root], [data-ohw-section-picker], [data-ohw-ai-review]")) {
19206
+ return;
19207
+ }
19208
+ const itemEl = t.closest("[data-ohw-media-strip-item]");
19209
+ if (!itemEl) {
19210
+ setHover(null);
19211
+ return;
19212
+ }
19213
+ const sectionEl = itemEl.closest("[data-ohw-section]");
19214
+ const unit = sectionEl ? movableUnit(sectionEl) : null;
19215
+ const sectionId = unit ? instanceIdOf(unit) || null : null;
19216
+ const index = Number(itemEl.dataset.ohwMediaStripIndex);
19217
+ if (!sectionId || Number.isNaN(index)) {
19218
+ setHover(null);
19219
+ return;
19220
+ }
19221
+ setHover({ sectionId, index });
19411
19222
  };
19412
19223
  const onLeave = () => setHover(null);
19413
19224
  document.addEventListener("mousemove", onMove, { passive: true });
@@ -19419,25 +19230,23 @@ function AiMediaStripOverlay({
19419
19230
  }, []);
19420
19231
  useEffect6(() => {
19421
19232
  const onClick = (e) => {
19233
+ const current = activeRef.current;
19234
+ if (!current) return;
19422
19235
  const t = e.target;
19423
- if (!(t instanceof Element)) return;
19424
- if (t.closest(CHROME_SELECTOR)) return;
19425
- const ref = itemRefFromTarget(t);
19426
- if (sameRef(activeRef.current, ref)) return;
19427
- if (ref) clearInteractionChromeRef.current();
19428
- setItemEditOn(false);
19236
+ if (!(t instanceof HTMLElement)) return;
19237
+ if (t.closest("[data-ohw-item-interaction]")) return;
19238
+ const itemEl = findMediaStripItemElement(current.sectionId, current.index);
19239
+ if (itemEl && itemEl.contains(t)) return;
19240
+ setActive(null);
19429
19241
  setError(null);
19430
- setActive(ref);
19431
19242
  };
19432
19243
  document.addEventListener("click", onClick, true);
19433
19244
  return () => document.removeEventListener("click", onClick, true);
19434
- }, [clearInteractionChromeRef]);
19245
+ }, []);
19435
19246
  useEffect6(() => {
19436
19247
  const onBlur = () => {
19437
19248
  if (!activeRef.current) return;
19438
19249
  setActive(null);
19439
- setItemEditOn(false);
19440
- setLinkOpen(false);
19441
19250
  setError(null);
19442
19251
  };
19443
19252
  window.addEventListener("blur", onBlur);
@@ -19447,10 +19256,7 @@ function AiMediaStripOverlay({
19447
19256
  const onKeyDown = (e) => {
19448
19257
  if (e.key !== "Escape") return;
19449
19258
  if (!activeRef.current) return;
19450
- if (linkOpenRef.current) return;
19451
- if (document.querySelector('[data-ohw-more-menu][data-state="open"]')) return;
19452
19259
  setActive(null);
19453
- setItemEditOn(false);
19454
19260
  setError(null);
19455
19261
  };
19456
19262
  document.addEventListener("keydown", onKeyDown);
@@ -19484,133 +19290,137 @@ function AiMediaStripOverlay({
19484
19290
  tree: nextTree,
19485
19291
  label: entry.label,
19486
19292
  media: extraMedia ? { ...entry.media, ...extraMedia } : entry.media,
19487
- ...entry.brand ? { brand: entry.brand } : {},
19488
- // The user is already looking at the strip they just edited — recentering the viewport
19489
- // on every edit reads as the section jumping around.
19490
- skipScrollIntoView: true
19293
+ ...entry.brand ? { brand: entry.brand } : {}
19491
19294
  });
19492
19295
  return true;
19493
19296
  },
19494
19297
  [findEntry, postToParent2, applyTreeLocalRef]
19495
19298
  );
19496
19299
  const activeEntry = active ? findEntry(active.sectionId) : null;
19300
+ const hoverHandleRect = useLiveItemRect(!active && hover ? hover : null);
19497
19301
  const activeItemRect = useLiveItemRect(active);
19498
- const activeStripRect = useLiveItemRect(active, true);
19499
- const hoverRect = useLiveItemRect(hover && !sameRef(hover, active) ? hover : null);
19500
- const activeItems = activeEntry ? mediaStripItems(activeEntry.tree) : [];
19501
- const activeItem = active ? activeItems[active.index] : void 0;
19502
- const activeHref = activeItem?.href ?? "";
19503
- const activeTarget = activeItem?.linkTarget === "same-tab" ? "same-tab" : "new-tab";
19504
- return /* @__PURE__ */ jsxs12(Fragment7, { children: [
19505
- hoverRect && /* @__PURE__ */ jsx21(ItemInteractionLayer, { rect: hoverRect, state: "hover" }),
19506
- activeItemRect && activeStripRect && active && activeEntry && /* @__PURE__ */ jsxs12(Fragment7, { children: [
19507
- /* @__PURE__ */ jsx21(ItemInteractionLayer, { rect: activeItemRect, state: "active-top" }),
19508
- /* @__PURE__ */ jsx21(StripToolbarSlot, { stripRect: activeStripRect, children: /* @__PURE__ */ jsx21(
19509
- MediaStripToolbar,
19510
- {
19511
- entry: activeEntry,
19512
- index: active.index,
19513
- pending,
19514
- error,
19515
- itemEditOn,
19516
- onToggleItemEdit: () => setItemEditOn((on) => !on),
19517
- onSetVariant: (variant) => runMutation(active.sectionId, (tree) => setMediaStripVariant(tree, variant)),
19518
- onSetAlign: (align) => runMutation(active.sectionId, (tree) => setMediaStripAlign(tree, align)),
19519
- onSetHover: (hoverPreset) => runMutation(active.sectionId, (tree) => setMediaStripHover(tree, hoverPreset)),
19520
- onSetTreatment: (treatment) => runMutation(active.sectionId, (tree) => setMediaStripTreatment(tree, treatment)),
19521
- onResize: (delta) => {
19522
- const slots = findMediaStripSlots(activeEntry.tree);
19523
- const variant = slots?.variant === "reel" ? "reel" : "logos";
19524
- const current = variant === "logos" ? typeof slots?.logoMaxHeight === "number" ? slots.logoMaxHeight : 48 : typeof slots?.cardWidth === "number" ? slots.cardWidth : 240;
19525
- void runMutation(active.sectionId, (tree) => setMediaStripSizing(tree, current + delta));
19526
- },
19527
- onOpenLink: () => setLinkOpen(true),
19528
- onAddItem: () => {
19529
- void requestAssetPick(postToParent2).then((picked) => {
19530
- if (!picked) return;
19531
- const item = { assetId: picked.assetId, alt: "Logo" };
19302
+ const TOOLBAR_APPROX_H = 170;
19303
+ const toolbarState = activeItemRect && activeItemRect.top < TOOLBAR_APPROX_H ? "active-bottom" : "active-top";
19304
+ return /* @__PURE__ */ jsxs11(Fragment7, { children: [
19305
+ hoverHandleRect && hover && /* @__PURE__ */ jsx20(
19306
+ "button",
19307
+ {
19308
+ type: "button",
19309
+ "data-ohw-toolbar": "",
19310
+ onClick: () => {
19311
+ clearInteractionChromeRef.current();
19312
+ setActive(hover);
19313
+ },
19314
+ "aria-label": "Configure media item",
19315
+ style: {
19316
+ position: "fixed",
19317
+ top: hoverHandleRect.top + 8,
19318
+ left: hoverHandleRect.left + hoverHandleRect.width - 36,
19319
+ width: 28,
19320
+ height: 28,
19321
+ borderRadius: 8,
19322
+ border: `1.5px dashed ${PRIMARY3}`,
19323
+ background: "#ffffff",
19324
+ color: PRIMARY3,
19325
+ display: "flex",
19326
+ alignItems: "center",
19327
+ justifyContent: "center",
19328
+ cursor: "pointer",
19329
+ zIndex: 2147483643,
19330
+ fontSize: 14,
19331
+ fontWeight: 700
19332
+ },
19333
+ children: /* @__PURE__ */ jsx20(Star, { size: 14, style: { pointerEvents: "none" } })
19334
+ }
19335
+ ),
19336
+ activeItemRect && active && activeEntry && /* @__PURE__ */ jsx20(
19337
+ ItemInteractionLayer,
19338
+ {
19339
+ rect: activeItemRect,
19340
+ state: toolbarState,
19341
+ toolbar: /* @__PURE__ */ jsx20(
19342
+ MediaStripToolbar,
19343
+ {
19344
+ entry: activeEntry,
19345
+ index: active.index,
19346
+ pending,
19347
+ error,
19348
+ onSetVariant: (variant) => runMutation(active.sectionId, (tree) => setMediaStripVariant(tree, variant)),
19349
+ onSetAlign: (align) => runMutation(active.sectionId, (tree) => setMediaStripAlign(tree, align)),
19350
+ onSetHover: (hoverPreset) => runMutation(active.sectionId, (tree) => setMediaStripHover(tree, hoverPreset)),
19351
+ onSetTreatment: (treatment) => runMutation(active.sectionId, (tree) => setMediaStripTreatment(tree, treatment)),
19352
+ onResize: (delta) => {
19353
+ const slots = findMediaStripSlots(activeEntry.tree);
19354
+ const variant = slots?.variant === "reel" ? "reel" : "logos";
19355
+ const current = variant === "logos" ? typeof slots?.logoMaxHeight === "number" ? slots.logoMaxHeight : 48 : typeof slots?.cardWidth === "number" ? slots.cardWidth : 240;
19356
+ void runMutation(active.sectionId, (tree) => setMediaStripSizing(tree, current + delta));
19357
+ },
19358
+ onSetAlt: (alt) => {
19359
+ void runMutation(active.sectionId, (tree) => setMediaStripItemAlt(tree, active.index, alt));
19360
+ },
19361
+ onSetLink: (href, linkTarget) => {
19532
19362
  void runMutation(
19533
19363
  active.sectionId,
19534
- (tree) => addMediaStripItem(tree, item, active.index),
19535
- { [picked.assetId]: picked.url }
19536
- ).then((applied) => {
19537
- if (applied) setActive({ sectionId: active.sectionId, index: active.index + 1 });
19364
+ (tree) => setMediaStripItemLink(tree, active.index, href ? { href, linkTarget } : {})
19365
+ );
19366
+ },
19367
+ onAddItem: () => {
19368
+ void requestAssetPick(postToParent2).then((picked) => {
19369
+ if (!picked) return;
19370
+ const item = { assetId: picked.assetId, alt: "Logo" };
19371
+ void runMutation(
19372
+ active.sectionId,
19373
+ (tree) => addMediaStripItem(tree, item, active.index),
19374
+ { [picked.assetId]: picked.url }
19375
+ ).then((applied) => {
19376
+ if (applied) setActive({ sectionId: active.sectionId, index: active.index + 1 });
19377
+ });
19538
19378
  });
19539
- });
19540
- },
19541
- onChangeImage: () => {
19542
- void requestAssetPick(postToParent2).then((picked) => {
19543
- if (!picked) return;
19544
- void runMutation(
19545
- active.sectionId,
19546
- (tree) => setMediaStripItemAsset(tree, active.index, picked.assetId),
19547
- { [picked.assetId]: picked.url }
19379
+ },
19380
+ onChangeImage: () => {
19381
+ void requestAssetPick(postToParent2).then((picked) => {
19382
+ if (!picked) return;
19383
+ void runMutation(
19384
+ active.sectionId,
19385
+ (tree) => setMediaStripItemAsset(tree, active.index, picked.assetId),
19386
+ { [picked.assetId]: picked.url }
19387
+ );
19388
+ });
19389
+ },
19390
+ onRemoveItem: () => {
19391
+ void runMutation(active.sectionId, (tree) => removeMediaStripItem(tree, active.index)).then(
19392
+ (applied) => {
19393
+ if (applied) setActive(null);
19394
+ }
19548
19395
  );
19549
- });
19550
- },
19551
- onRemoveItem: () => {
19552
- void runMutation(active.sectionId, (tree) => removeMediaStripItem(tree, active.index)).then(
19553
- (applied) => {
19554
- if (applied) setActive(null);
19555
- }
19556
- );
19557
- },
19558
- onMoveLeft: () => {
19559
- void runMutation(active.sectionId, (tree) => reorderMediaStripItem(tree, active.index, -1)).then(
19560
- (applied) => {
19561
- if (applied) setActive({ sectionId: active.sectionId, index: active.index - 1 });
19562
- }
19563
- );
19564
- },
19565
- onMoveRight: () => {
19566
- void runMutation(active.sectionId, (tree) => reorderMediaStripItem(tree, active.index, 1)).then(
19567
- (applied) => {
19568
- if (applied) setActive({ sectionId: active.sectionId, index: active.index + 1 });
19569
- }
19570
- );
19571
- }
19572
- }
19573
- ) }),
19574
- /* @__PURE__ */ jsx21(
19575
- "div",
19576
- {
19577
- "data-ohw-bridge": "",
19578
- "data-ohw-item-interaction": "",
19579
- "aria-hidden": true,
19580
- className: "pointer-events-none fixed rounded-[4px] px-[7px] py-[3px] font-sans text-[11px] font-medium leading-none text-primary-foreground whitespace-nowrap",
19581
- style: {
19582
- top: activeItemRect.top + activeItemRect.height + 10,
19583
- left: activeItemRect.left - 5,
19584
- background: PRIMARY3,
19585
- zIndex: 2147483644
19586
- },
19587
- children: "Media item"
19588
- }
19589
- ),
19590
- linkOpen && /* @__PURE__ */ jsx21(
19591
- LinkModal,
19592
- {
19593
- href: activeHref,
19594
- linkTarget: activeTarget,
19595
- onClose: () => setLinkOpen(false),
19596
- onSave: (href, linkTarget) => {
19597
- void runMutation(
19598
- active.sectionId,
19599
- (tree) => setMediaStripItemLink(tree, active.index, href ? { href, linkTarget } : {})
19600
- );
19396
+ },
19397
+ onMoveLeft: () => {
19398
+ void runMutation(active.sectionId, (tree) => reorderMediaStripItem(tree, active.index, -1)).then(
19399
+ (applied) => {
19400
+ if (applied) setActive({ sectionId: active.sectionId, index: active.index - 1 });
19401
+ }
19402
+ );
19403
+ },
19404
+ onMoveRight: () => {
19405
+ void runMutation(active.sectionId, (tree) => reorderMediaStripItem(tree, active.index, 1)).then(
19406
+ (applied) => {
19407
+ if (applied) setActive({ sectionId: active.sectionId, index: active.index + 1 });
19408
+ }
19409
+ );
19410
+ }
19601
19411
  }
19602
- }
19603
- )
19604
- ] })
19412
+ )
19413
+ }
19414
+ )
19605
19415
  ] });
19606
19416
  }
19607
19417
 
19608
19418
  // src/ui/ai-section/AiPricingCardOverlay.tsx
19609
- import { useCallback as useCallback4, useEffect as useEffect7, useLayoutEffect as useLayoutEffect5, useRef as useRef6, useState as useState9 } from "react";
19610
- import { Check as Check3, ChevronDown as ChevronDown4, Minus as Minus2, Plus as Plus3, Star as Star2 } from "lucide-react";
19611
- import { Fragment as Fragment8, jsx as jsx22, jsxs as jsxs13 } from "react/jsx-runtime";
19419
+ import { useCallback as useCallback4, useEffect as useEffect7, useLayoutEffect as useLayoutEffect4, useRef as useRef6, useState as useState9 } from "react";
19420
+ import { Check as Check2, ChevronDown as ChevronDown3, Minus, Plus as Plus2, Star as Star2 } from "lucide-react";
19421
+ import { Fragment as Fragment8, jsx as jsx21, jsxs as jsxs12 } from "react/jsx-runtime";
19612
19422
  var PRIMARY4 = "var(--ohw-primary, #0885FE)";
19613
- var CHROME_SELECTOR2 = "[data-ohw-toolbar], [data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-link-modal-root], [data-ohw-section-picker], [data-ohw-ai-review], [data-ohw-more-menu]";
19423
+ var CHROME_SELECTOR = "[data-ohw-toolbar], [data-ohw-edit-chrome], [data-ohw-item-interaction], [data-ohw-link-modal-root], [data-ohw-section-picker], [data-ohw-ai-review], [data-ohw-more-menu]";
19614
19424
  function findPricingCardElement(sectionId, index) {
19615
19425
  const sectionEl = findByInstanceId(sectionId);
19616
19426
  if (!sectionEl) return null;
@@ -19622,7 +19432,7 @@ function findPricingCardElement(sectionId, index) {
19622
19432
  }
19623
19433
  function cardRefFromTarget(target) {
19624
19434
  if (!(target instanceof Element)) return null;
19625
- if (target.closest(CHROME_SELECTOR2)) return null;
19435
+ if (target.closest(CHROME_SELECTOR)) return null;
19626
19436
  const cardEl = target.closest("[data-ohw-pricing-card]");
19627
19437
  if (!cardEl) return null;
19628
19438
  const sectionEl = cardEl.closest("[data-ohw-section]");
@@ -19632,21 +19442,21 @@ function cardRefFromTarget(target) {
19632
19442
  if (!sectionId || Number.isNaN(index)) return null;
19633
19443
  return { sectionId, index };
19634
19444
  }
19635
- function sameRef2(a, b) {
19445
+ function sameRef(a, b) {
19636
19446
  return a?.sectionId === b?.sectionId && a?.index === b?.index;
19637
19447
  }
19638
- function rectOf2(el) {
19448
+ function rectOf(el) {
19639
19449
  if (!el) return null;
19640
19450
  const r2 = el.getBoundingClientRect();
19641
19451
  if (r2.width <= 0 || r2.height <= 0) return null;
19642
19452
  return { top: r2.top, left: r2.left, width: r2.width, height: r2.height };
19643
19453
  }
19644
19454
  function readRect3(ref) {
19645
- return rectOf2(ref && findPricingCardElement(ref.sectionId, ref.index));
19455
+ return rectOf(ref && findPricingCardElement(ref.sectionId, ref.index));
19646
19456
  }
19647
19457
  function readGridRect(ref) {
19648
19458
  const card = ref && findPricingCardElement(ref.sectionId, ref.index);
19649
- return rectOf2(card?.closest("[data-ohw-pricing-collection]") ?? card);
19459
+ return rectOf(card?.closest("[data-ohw-pricing-collection]") ?? card);
19650
19460
  }
19651
19461
  function useLiveCardRect(ref, grid = false) {
19652
19462
  const [rect, setRect] = useState9(null);
@@ -19676,19 +19486,19 @@ function useLiveCardRect(ref, grid = false) {
19676
19486
  }
19677
19487
  var TOOLBAR_GRID_GAP = 52;
19678
19488
  var TOOLBAR_GRID_GAP_MOBILE = 42;
19679
- var TOOLBAR_EDGE_MARGIN3 = 4;
19489
+ var TOOLBAR_EDGE_MARGIN2 = 4;
19680
19490
  function GridToolbarSlot({ gridRect, children }) {
19681
19491
  const slotRef = useRef6(null);
19682
19492
  const [shift, setShift] = useState9(0);
19683
19493
  const centerX = gridRect.left + gridRect.width / 2;
19684
- useLayoutEffect5(() => {
19494
+ useLayoutEffect4(() => {
19685
19495
  const slot = slotRef.current;
19686
19496
  if (!slot) return;
19687
19497
  const clamp = () => {
19688
19498
  const w = slot.offsetWidth;
19689
19499
  if (w <= 0) return;
19690
19500
  const half = w / 2;
19691
- const clamped = Math.max(TOOLBAR_EDGE_MARGIN3 + half, Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN3 - half));
19501
+ const clamped = Math.max(TOOLBAR_EDGE_MARGIN2 + half, Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN2 - half));
19692
19502
  setShift((prev) => Math.abs(prev - (clamped - centerX)) < 0.5 ? prev : clamped - centerX);
19693
19503
  };
19694
19504
  clamp();
@@ -19697,7 +19507,7 @@ function GridToolbarSlot({ gridRect, children }) {
19697
19507
  return () => ro.disconnect();
19698
19508
  }, [centerX, children]);
19699
19509
  const mobile = window.innerWidth <= 480;
19700
- return /* @__PURE__ */ jsx22(
19510
+ return /* @__PURE__ */ jsx21(
19701
19511
  "div",
19702
19512
  {
19703
19513
  ref: slotRef,
@@ -19732,7 +19542,7 @@ function requestValidateTree2(postToParent2, tree) {
19732
19542
  });
19733
19543
  }
19734
19544
  function StyleSwatch({ style }) {
19735
- return /* @__PURE__ */ jsx22(
19545
+ return /* @__PURE__ */ jsx21(
19736
19546
  "span",
19737
19547
  {
19738
19548
  "aria-hidden": true,
@@ -19746,35 +19556,35 @@ function StyleSwatch({ style }) {
19746
19556
  );
19747
19557
  }
19748
19558
  function ButtonGlyph() {
19749
- return /* @__PURE__ */ jsxs13("svg", { width: "18", height: "12", viewBox: "0 0 18 12", fill: "none", "aria-hidden": true, style: { pointerEvents: "none" }, children: [
19750
- /* @__PURE__ */ jsx22("rect", { x: "0.75", y: "0.75", width: "16.5", height: "10.5", rx: "3", stroke: "currentColor", strokeWidth: "1.5" }),
19751
- /* @__PURE__ */ jsx22("path", { d: "M5.5 6H12.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
19559
+ return /* @__PURE__ */ jsxs12("svg", { width: "18", height: "12", viewBox: "0 0 18 12", fill: "none", "aria-hidden": true, style: { pointerEvents: "none" }, children: [
19560
+ /* @__PURE__ */ jsx21("rect", { x: "0.75", y: "0.75", width: "16.5", height: "10.5", rx: "3", stroke: "currentColor", strokeWidth: "1.5" }),
19561
+ /* @__PURE__ */ jsx21("path", { d: "M5.5 6H12.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
19752
19562
  ] });
19753
19563
  }
19754
19564
  function FeatureGlyph({ mode }) {
19755
- return /* @__PURE__ */ jsxs13("svg", { width: "18", height: "18", viewBox: "0 0 18 18", fill: "none", "aria-hidden": true, style: { pointerEvents: "none" }, children: [
19756
- /* @__PURE__ */ jsx22("circle", { cx: "8", cy: "8", r: "7.25", stroke: "currentColor", strokeWidth: "1.5" }),
19757
- /* @__PURE__ */ jsx22("path", { d: "M5 8.2L7.2 10.3L11 6", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }),
19758
- /* @__PURE__ */ jsx22("circle", { cx: "13.5", cy: "13.5", r: "4.25", fill: "var(--ohw-background, #ffffff)", stroke: "var(--ohw-background, #ffffff)", strokeWidth: "1" }),
19759
- /* @__PURE__ */ jsx22("circle", { cx: "13.5", cy: "13.5", r: "3.75", fill: "var(--ohw-background, #ffffff)", stroke: PRIMARY4, strokeWidth: "1" }),
19760
- /* @__PURE__ */ jsx22("path", { d: "M11.5 13.5H15.5", stroke: PRIMARY4, strokeWidth: "1.5", strokeLinecap: "round" }),
19761
- mode === "add" && /* @__PURE__ */ jsx22("path", { d: "M13.5 11.5V15.5", stroke: PRIMARY4, strokeWidth: "1.5", strokeLinecap: "round" })
19565
+ return /* @__PURE__ */ jsxs12("svg", { width: "18", height: "18", viewBox: "0 0 18 18", fill: "none", "aria-hidden": true, style: { pointerEvents: "none" }, children: [
19566
+ /* @__PURE__ */ jsx21("circle", { cx: "8", cy: "8", r: "7.25", stroke: "currentColor", strokeWidth: "1.5" }),
19567
+ /* @__PURE__ */ jsx21("path", { d: "M5 8.2L7.2 10.3L11 6", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }),
19568
+ /* @__PURE__ */ jsx21("circle", { cx: "13.5", cy: "13.5", r: "4.25", fill: "var(--ohw-background, #ffffff)", stroke: "var(--ohw-background, #ffffff)", strokeWidth: "1" }),
19569
+ /* @__PURE__ */ jsx21("circle", { cx: "13.5", cy: "13.5", r: "3.75", fill: "var(--ohw-background, #ffffff)", stroke: PRIMARY4, strokeWidth: "1" }),
19570
+ /* @__PURE__ */ jsx21("path", { d: "M11.5 13.5H15.5", stroke: PRIMARY4, strokeWidth: "1.5", strokeLinecap: "round" }),
19571
+ mode === "add" && /* @__PURE__ */ jsx21("path", { d: "M13.5 11.5V15.5", stroke: PRIMARY4, strokeWidth: "1.5", strokeLinecap: "round" })
19762
19572
  ] });
19763
19573
  }
19764
- function ToolbarAction2({
19574
+ function ToolbarAction({
19765
19575
  label,
19766
19576
  onClick,
19767
19577
  disabled = false,
19768
19578
  children
19769
19579
  }) {
19770
- const button = /* @__PURE__ */ jsx22(CustomToolbarButton, { type: "button", "aria-label": label, disabled, onClick, className: "size-8", children });
19771
- return /* @__PURE__ */ jsxs13(Tooltip, { children: [
19772
- /* @__PURE__ */ jsx22(TooltipTrigger, { asChild: true, children: disabled ? /* @__PURE__ */ jsx22("span", { className: "inline-flex", children: button }) : button }),
19773
- /* @__PURE__ */ jsx22(TooltipContent, { side: "bottom", sideOffset: 8, children: label })
19580
+ const button = /* @__PURE__ */ jsx21(CustomToolbarButton, { type: "button", "aria-label": label, disabled, onClick, className: "size-8", children });
19581
+ return /* @__PURE__ */ jsxs12(Tooltip, { children: [
19582
+ /* @__PURE__ */ jsx21(TooltipTrigger, { asChild: true, children: disabled ? /* @__PURE__ */ jsx21("span", { className: "inline-flex", children: button }) : button }),
19583
+ /* @__PURE__ */ jsx21(TooltipContent, { side: "bottom", sideOffset: 8, children: label })
19774
19584
  ] });
19775
19585
  }
19776
- function ToolbarSeparator2() {
19777
- return /* @__PURE__ */ jsx22("span", { "aria-hidden": true, className: "mx-0.5 block h-6 w-px shrink-0 bg-border" });
19586
+ function ToolbarSeparator() {
19587
+ return /* @__PURE__ */ jsx21("span", { "aria-hidden": true, className: "mx-0.5 block h-6 w-px shrink-0 bg-border" });
19778
19588
  }
19779
19589
  function CardStyleMenu({
19780
19590
  value,
@@ -19782,7 +19592,7 @@ function CardStyleMenu({
19782
19592
  disabled = false
19783
19593
  }) {
19784
19594
  const current = PRICING_CARD_STYLES.find((s) => s.value === value) ?? PRICING_CARD_STYLES[0];
19785
- const trigger = /* @__PURE__ */ jsxs13(
19595
+ const trigger = /* @__PURE__ */ jsxs12(
19786
19596
  "button",
19787
19597
  {
19788
19598
  type: "button",
@@ -19793,28 +19603,28 @@ function CardStyleMenu({
19793
19603
  disabled && "cursor-not-allowed opacity-40 hover:bg-transparent"
19794
19604
  ),
19795
19605
  children: [
19796
- /* @__PURE__ */ jsx22(StyleSwatch, { style: current.value }),
19797
- /* @__PURE__ */ jsx22("span", { className: "flex-1 text-left", children: current.label }),
19798
- /* @__PURE__ */ jsx22(ChevronDown4, { size: 16, style: { pointerEvents: "none" } })
19606
+ /* @__PURE__ */ jsx21(StyleSwatch, { style: current.value }),
19607
+ /* @__PURE__ */ jsx21("span", { className: "flex-1 text-left", children: current.label }),
19608
+ /* @__PURE__ */ jsx21(ChevronDown3, { size: 16, style: { pointerEvents: "none" } })
19799
19609
  ]
19800
19610
  }
19801
19611
  );
19802
19612
  if (disabled) {
19803
- return /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
19804
- /* @__PURE__ */ jsx22("span", { className: "text-xs text-muted-foreground opacity-50", children: "Card style" }),
19805
- /* @__PURE__ */ jsxs13(Tooltip, { children: [
19806
- /* @__PURE__ */ jsx22(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx22("span", { className: "inline-flex", children: trigger }) }),
19807
- /* @__PURE__ */ jsx22(TooltipContent, { side: "bottom", sideOffset: 8, children: "Mark as featured to edit card style" })
19613
+ return /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
19614
+ /* @__PURE__ */ jsx21("span", { className: "text-xs text-muted-foreground opacity-50", children: "Card style" }),
19615
+ /* @__PURE__ */ jsxs12(Tooltip, { children: [
19616
+ /* @__PURE__ */ jsx21(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx21("span", { className: "inline-flex", children: trigger }) }),
19617
+ /* @__PURE__ */ jsx21(TooltipContent, { side: "bottom", sideOffset: 8, children: "Mark as featured to edit card style" })
19808
19618
  ] })
19809
19619
  ] });
19810
19620
  }
19811
- return /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
19812
- /* @__PURE__ */ jsx22("span", { className: "text-xs text-muted-foreground", children: "Card style" }),
19813
- /* @__PURE__ */ jsxs13(DropdownMenu, { modal: false, children: [
19814
- /* @__PURE__ */ jsx22(DropdownMenuTrigger, { asChild: true, children: trigger }),
19815
- /* @__PURE__ */ jsx22(DropdownMenuContent, { align: "end", sideOffset: 8, className: "w-[152px] min-w-[152px] p-1", children: PRICING_CARD_STYLES.map((style) => {
19621
+ return /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
19622
+ /* @__PURE__ */ jsx21("span", { className: "text-xs text-muted-foreground", children: "Card style" }),
19623
+ /* @__PURE__ */ jsxs12(DropdownMenu, { modal: false, children: [
19624
+ /* @__PURE__ */ jsx21(DropdownMenuTrigger, { asChild: true, children: trigger }),
19625
+ /* @__PURE__ */ jsx21(DropdownMenuContent, { align: "end", sideOffset: 8, className: "w-[152px] min-w-[152px] p-1", children: PRICING_CARD_STYLES.map((style) => {
19816
19626
  const selected = style.value === value;
19817
- return /* @__PURE__ */ jsxs13(
19627
+ return /* @__PURE__ */ jsxs12(
19818
19628
  DropdownMenuItem,
19819
19629
  {
19820
19630
  role: "menuitemradio",
@@ -19822,9 +19632,9 @@ function CardStyleMenu({
19822
19632
  onSelect: () => onChange(style.value),
19823
19633
  className: cn("grid grid-cols-[18px_1fr_16px] gap-2 py-2 pl-2 pr-2 text-[13px]", selected && "bg-muted"),
19824
19634
  children: [
19825
- /* @__PURE__ */ jsx22(StyleSwatch, { style: style.value }),
19826
- /* @__PURE__ */ jsx22("span", { children: style.label }),
19827
- selected && /* @__PURE__ */ jsx22(Check3, { size: 16, className: "text-bridge-primary" })
19635
+ /* @__PURE__ */ jsx21(StyleSwatch, { style: style.value }),
19636
+ /* @__PURE__ */ jsx21("span", { children: style.label }),
19637
+ selected && /* @__PURE__ */ jsx21(Check2, { size: 16, className: "text-bridge-primary" })
19828
19638
  ]
19829
19639
  },
19830
19640
  style.value
@@ -19854,7 +19664,7 @@ function PricingCardToolbar({
19854
19664
  const style = pricingCardStyle(entry.tree, index);
19855
19665
  const featuredTag = pricingCardFeaturedTag(entry.tree, index);
19856
19666
  const mobile = window.innerWidth <= 480;
19857
- return /* @__PURE__ */ jsx22(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsxs13(
19667
+ return /* @__PURE__ */ jsx21(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsxs12(
19858
19668
  "div",
19859
19669
  {
19860
19670
  "data-ohw-bridge": "",
@@ -19866,7 +19676,7 @@ function PricingCardToolbar({
19866
19676
  ),
19867
19677
  style: { maxWidth: mobile ? "calc(100vw - 24px)" : void 0 },
19868
19678
  children: [
19869
- /* @__PURE__ */ jsxs13(
19679
+ /* @__PURE__ */ jsxs12(
19870
19680
  "div",
19871
19681
  {
19872
19682
  className: cn(
@@ -19875,8 +19685,8 @@ function PricingCardToolbar({
19875
19685
  mobile ? "flex-wrap justify-center" : "flex-nowrap"
19876
19686
  ),
19877
19687
  children: [
19878
- /* @__PURE__ */ jsxs13(Tooltip, { children: [
19879
- /* @__PURE__ */ jsx22(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx22(
19688
+ /* @__PURE__ */ jsxs12(Tooltip, { children: [
19689
+ /* @__PURE__ */ jsx21(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx21(
19880
19690
  CustomToolbarButton,
19881
19691
  {
19882
19692
  type: "button",
@@ -19884,7 +19694,7 @@ function PricingCardToolbar({
19884
19694
  "aria-pressed": featuredTag,
19885
19695
  onClick: () => onSetFeaturedTag(!featuredTag),
19886
19696
  className: "group size-8",
19887
- children: /* @__PURE__ */ jsx22(
19697
+ children: /* @__PURE__ */ jsx21(
19888
19698
  Star2,
19889
19699
  {
19890
19700
  size: 16,
@@ -19899,25 +19709,25 @@ function PricingCardToolbar({
19899
19709
  )
19900
19710
  }
19901
19711
  ) }),
19902
- /* @__PURE__ */ jsx22(TooltipContent, { side: "bottom", sideOffset: 8, children: featuredTag ? "Unmark featured" : "Mark as featured" })
19712
+ /* @__PURE__ */ jsx21(TooltipContent, { side: "bottom", sideOffset: 8, children: featuredTag ? "Unmark featured" : "Mark as featured" })
19903
19713
  ] }),
19904
- /* @__PURE__ */ jsx22(ToolbarSeparator2, {}),
19905
- /* @__PURE__ */ jsx22(CardStyleMenu, { value: style, onChange: onSetStyle, disabled: !featuredTag }),
19906
- /* @__PURE__ */ jsx22(ToolbarSeparator2, {}),
19907
- /* @__PURE__ */ jsx22(ToolbarAction2, { label: "Add button", onClick: onAddButton, disabled: buttonCount >= 2, children: /* @__PURE__ */ jsx22(ButtonGlyph, {}) }),
19908
- /* @__PURE__ */ jsx22(ToolbarAction2, { label: "Remove button", onClick: onRemoveButton, disabled: buttonCount === 0, children: /* @__PURE__ */ jsxs13("span", { className: "relative inline-flex", children: [
19909
- /* @__PURE__ */ jsx22(ButtonGlyph, {}),
19910
- /* @__PURE__ */ jsx22(Minus2, { size: 10, strokeWidth: 2.5, className: "absolute -right-1.5 -top-1.5 rounded-full bg-background text-bridge-primary" })
19714
+ /* @__PURE__ */ jsx21(ToolbarSeparator, {}),
19715
+ /* @__PURE__ */ jsx21(CardStyleMenu, { value: style, onChange: onSetStyle, disabled: !featuredTag }),
19716
+ /* @__PURE__ */ jsx21(ToolbarSeparator, {}),
19717
+ /* @__PURE__ */ jsx21(ToolbarAction, { label: "Add button", onClick: onAddButton, disabled: buttonCount >= 2, children: /* @__PURE__ */ jsx21(ButtonGlyph, {}) }),
19718
+ /* @__PURE__ */ jsx21(ToolbarAction, { label: "Remove button", onClick: onRemoveButton, disabled: buttonCount === 0, children: /* @__PURE__ */ jsxs12("span", { className: "relative inline-flex", children: [
19719
+ /* @__PURE__ */ jsx21(ButtonGlyph, {}),
19720
+ /* @__PURE__ */ jsx21(Minus, { size: 10, strokeWidth: 2.5, className: "absolute -right-1.5 -top-1.5 rounded-full bg-background text-bridge-primary" })
19911
19721
  ] }) }),
19912
- /* @__PURE__ */ jsx22(ToolbarAction2, { label: "Add feature", onClick: onAddFeature, disabled: featureCount >= 10, children: /* @__PURE__ */ jsx22(FeatureGlyph, { mode: "add" }) }),
19913
- /* @__PURE__ */ jsx22(ToolbarAction2, { label: "Remove feature", onClick: onRemoveFeature, disabled: featureCount <= 1, children: /* @__PURE__ */ jsx22(FeatureGlyph, { mode: "remove" }) }),
19914
- /* @__PURE__ */ jsx22(ToolbarSeparator2, {}),
19915
- /* @__PURE__ */ jsx22(ToolbarAction2, { label: "Add tier", onClick: onAddTier, disabled: collectionCount >= 4, children: /* @__PURE__ */ jsx22(Plus3, { size: 16, style: { pointerEvents: "none" } }) }),
19916
- /* @__PURE__ */ jsx22(ToolbarAction2, { label: "Remove tier", onClick: onRemoveTier, disabled: collectionCount <= 1, children: /* @__PURE__ */ jsx22(Minus2, { size: 16, style: { pointerEvents: "none" } }) })
19722
+ /* @__PURE__ */ jsx21(ToolbarAction, { label: "Add feature", onClick: onAddFeature, disabled: featureCount >= 10, children: /* @__PURE__ */ jsx21(FeatureGlyph, { mode: "add" }) }),
19723
+ /* @__PURE__ */ jsx21(ToolbarAction, { label: "Remove feature", onClick: onRemoveFeature, disabled: featureCount <= 1, children: /* @__PURE__ */ jsx21(FeatureGlyph, { mode: "remove" }) }),
19724
+ /* @__PURE__ */ jsx21(ToolbarSeparator, {}),
19725
+ /* @__PURE__ */ jsx21(ToolbarAction, { label: "Add tier", onClick: onAddTier, disabled: collectionCount >= 4, children: /* @__PURE__ */ jsx21(Plus2, { size: 16, style: { pointerEvents: "none" } }) }),
19726
+ /* @__PURE__ */ jsx21(ToolbarAction, { label: "Remove tier", onClick: onRemoveTier, disabled: collectionCount <= 1, children: /* @__PURE__ */ jsx21(Minus, { size: 16, style: { pointerEvents: "none" } }) })
19917
19727
  ]
19918
19728
  }
19919
19729
  ),
19920
- error && /* @__PURE__ */ jsx22("div", { className: "max-w-[260px] rounded-md bg-background px-2 py-1 text-center text-[11px] text-destructive shadow-sm", children: error })
19730
+ error && /* @__PURE__ */ jsx21("div", { className: "max-w-[260px] rounded-md bg-background px-2 py-1 text-center text-[11px] text-destructive shadow-sm", children: error })
19921
19731
  ]
19922
19732
  }
19923
19733
  ) });
@@ -19951,7 +19761,7 @@ function AiPricingCardOverlay({
19951
19761
  const overEditable = Boolean(t?.closest?.("[data-ohw-editable], [data-ohw-role], a, button"));
19952
19762
  setHover((prev) => {
19953
19763
  const next = ref && !overEditable ? ref : null;
19954
- return sameRef2(prev, next) ? prev : next;
19764
+ return sameRef(prev, next) ? prev : next;
19955
19765
  });
19956
19766
  };
19957
19767
  const onLeave = () => setHover(null);
@@ -19966,10 +19776,10 @@ function AiPricingCardOverlay({
19966
19776
  const onClick = (e) => {
19967
19777
  const t = e.target;
19968
19778
  if (!(t instanceof Element)) return;
19969
- if (t.closest(CHROME_SELECTOR2)) return;
19779
+ if (t.closest(CHROME_SELECTOR)) return;
19970
19780
  const ref = cardRefFromTarget(t);
19971
- setActive((prev) => sameRef2(prev, ref) ? prev : ref);
19972
- if (!ref || !sameRef2(activeRef.current, ref)) setError(null);
19781
+ setActive((prev) => sameRef(prev, ref) ? prev : ref);
19782
+ if (!ref || !sameRef(activeRef.current, ref)) setError(null);
19973
19783
  };
19974
19784
  document.addEventListener("click", onClick, true);
19975
19785
  return () => document.removeEventListener("click", onClick, true);
@@ -20034,12 +19844,12 @@ function AiPricingCardOverlay({
20034
19844
  const activeEntry = active ? findEntry(active.sectionId) : null;
20035
19845
  const activeCardRect = useLiveCardRect(active);
20036
19846
  const activeGridRect = useLiveCardRect(active, true);
20037
- const hoverRect = useLiveCardRect(hover && !sameRef2(hover, active) ? hover : null);
20038
- return /* @__PURE__ */ jsxs13(Fragment8, { children: [
20039
- hoverRect && /* @__PURE__ */ jsx22(ItemInteractionLayer, { rect: hoverRect, state: "hover" }),
20040
- activeCardRect && activeGridRect && active && activeEntry && /* @__PURE__ */ jsxs13(Fragment8, { children: [
20041
- /* @__PURE__ */ jsx22(ItemInteractionLayer, { rect: activeCardRect, state: "active-top" }),
20042
- /* @__PURE__ */ jsx22(GridToolbarSlot, { gridRect: activeGridRect, children: /* @__PURE__ */ jsx22(
19847
+ const hoverRect = useLiveCardRect(hover && !sameRef(hover, active) ? hover : null);
19848
+ return /* @__PURE__ */ jsxs12(Fragment8, { children: [
19849
+ hoverRect && /* @__PURE__ */ jsx21(ItemInteractionLayer, { rect: hoverRect, state: "hover" }),
19850
+ activeCardRect && activeGridRect && active && activeEntry && /* @__PURE__ */ jsxs12(Fragment8, { children: [
19851
+ /* @__PURE__ */ jsx21(ItemInteractionLayer, { rect: activeCardRect, state: "active-top" }),
19852
+ /* @__PURE__ */ jsx21(GridToolbarSlot, { gridRect: activeGridRect, children: /* @__PURE__ */ jsx21(
20043
19853
  PricingCardToolbar,
20044
19854
  {
20045
19855
  entry: activeEntry,
@@ -20067,7 +19877,7 @@ function AiPricingCardOverlay({
20067
19877
  onRemoveFeature: () => runMutation(active.sectionId, (tree) => removePricingCardFeature(tree, active.index))
20068
19878
  }
20069
19879
  ) }),
20070
- /* @__PURE__ */ jsx22(
19880
+ /* @__PURE__ */ jsx21(
20071
19881
  "div",
20072
19882
  {
20073
19883
  "data-ohw-bridge": "",
@@ -20160,10 +19970,10 @@ function extractYouTubeId(input) {
20160
19970
  }
20161
19971
 
20162
19972
  // src/ui/ai-section/AiVideoOverlay.tsx
20163
- import { Fragment as Fragment9, jsx as jsx23, jsxs as jsxs14 } from "react/jsx-runtime";
19973
+ import { Fragment as Fragment9, jsx as jsx22, jsxs as jsxs13 } from "react/jsx-runtime";
20164
19974
  var PRIMARY5 = "#0885FE";
20165
- var OHW_BLUE = `var(--ohw-primary, ${PRIMARY5})`;
20166
- var OHW_BLUE_DARK = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY5}) 60%, #000000)`;
19975
+ var OHW_BLUE2 = `var(--ohw-primary, ${PRIMARY5})`;
19976
+ var OHW_BLUE_DARK2 = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY5}) 60%, #000000)`;
20167
19977
  function findVideoBlockElement(sectionId, path) {
20168
19978
  const sectionEl = findByInstanceId(sectionId);
20169
19979
  if (!sectionEl) return null;
@@ -20223,12 +20033,12 @@ function requestValidateTree3(postToParent2, tree) {
20223
20033
  postToParent2({ type: "ow:ai-validate-tree-request", id, tree });
20224
20034
  });
20225
20035
  }
20226
- function Pill({
20036
+ function Pill2({
20227
20037
  label,
20228
20038
  onClick,
20229
20039
  disabled
20230
20040
  }) {
20231
- return /* @__PURE__ */ jsx23(
20041
+ return /* @__PURE__ */ jsx22(
20232
20042
  "button",
20233
20043
  {
20234
20044
  type: "button",
@@ -20244,7 +20054,7 @@ function Pill({
20244
20054
  border: "none",
20245
20055
  cursor: disabled ? "default" : "pointer",
20246
20056
  background: "#ffffff",
20247
- color: OHW_BLUE_DARK,
20057
+ color: OHW_BLUE_DARK2,
20248
20058
  opacity: disabled ? 0.4 : 1,
20249
20059
  fontSize: 12,
20250
20060
  lineHeight: "16px",
@@ -20266,7 +20076,7 @@ function VideoUrlInput({ value, onCommit }) {
20266
20076
  setDraft(value ? watchUrl(value) : "");
20267
20077
  setInvalid(false);
20268
20078
  }, [value]);
20269
- const commit2 = () => {
20079
+ const commit = () => {
20270
20080
  const next = draft.trim();
20271
20081
  if (!next) {
20272
20082
  setInvalid(false);
@@ -20287,8 +20097,8 @@ function VideoUrlInput({ value, onCommit }) {
20287
20097
  onCommit(id);
20288
20098
  }
20289
20099
  };
20290
- return /* @__PURE__ */ jsxs14("div", { style: { display: "flex", flexDirection: "column", gap: 4, width: "100%", maxWidth: 280 }, children: [
20291
- /* @__PURE__ */ jsxs14(
20100
+ return /* @__PURE__ */ jsxs13("div", { style: { display: "flex", flexDirection: "column", gap: 4, width: "100%", maxWidth: 280 }, children: [
20101
+ /* @__PURE__ */ jsxs13(
20292
20102
  "div",
20293
20103
  {
20294
20104
  style: {
@@ -20304,12 +20114,12 @@ function VideoUrlInput({ value, onCommit }) {
20304
20114
  boxShadow: invalid ? "inset 0 0 0 1.5px #EF4444" : "none"
20305
20115
  },
20306
20116
  children: [
20307
- /* @__PURE__ */ jsx23(
20117
+ /* @__PURE__ */ jsx22(
20308
20118
  "span",
20309
20119
  {
20310
20120
  style: {
20311
20121
  flex: "0 0 auto",
20312
- color: OHW_BLUE_DARK,
20122
+ color: OHW_BLUE_DARK2,
20313
20123
  fontSize: 10,
20314
20124
  lineHeight: "16px",
20315
20125
  fontWeight: 700,
@@ -20319,14 +20129,14 @@ function VideoUrlInput({ value, onCommit }) {
20319
20129
  children: "Video"
20320
20130
  }
20321
20131
  ),
20322
- /* @__PURE__ */ jsx23(
20132
+ /* @__PURE__ */ jsx22(
20323
20133
  "input",
20324
20134
  {
20325
20135
  type: "text",
20326
20136
  inputMode: "url",
20327
20137
  value: draft,
20328
20138
  onChange: (e) => handleChange(e.target.value),
20329
- onBlur: commit2,
20139
+ onBlur: commit,
20330
20140
  onKeyDown: (e) => {
20331
20141
  if (e.key === "Enter") e.currentTarget.blur();
20332
20142
  if (e.key === "Escape") {
@@ -20356,7 +20166,7 @@ function VideoUrlInput({ value, onCommit }) {
20356
20166
  ]
20357
20167
  }
20358
20168
  ),
20359
- invalid && /* @__PURE__ */ jsx23("div", { style: { color: "#FCA5A5", fontSize: 11, textAlign: "center" }, children: "That doesn't look like a YouTube link \u2014 try pasting the video URL again." })
20169
+ invalid && /* @__PURE__ */ jsx22("div", { style: { color: "#FCA5A5", fontSize: 11, textAlign: "center" }, children: "That doesn't look like a YouTube link \u2014 try pasting the video URL again." })
20360
20170
  ] });
20361
20171
  }
20362
20172
  function VideoToolbar({
@@ -20366,7 +20176,7 @@ function VideoToolbar({
20366
20176
  onSetVideoId,
20367
20177
  onRemove
20368
20178
  }) {
20369
- return /* @__PURE__ */ jsxs14(
20179
+ return /* @__PURE__ */ jsxs13(
20370
20180
  "div",
20371
20181
  {
20372
20182
  "data-ohw-bridge": "",
@@ -20378,7 +20188,7 @@ function VideoToolbar({
20378
20188
  gap: 6,
20379
20189
  alignItems: "center",
20380
20190
  width: 280,
20381
- background: OHW_BLUE,
20191
+ background: OHW_BLUE2,
20382
20192
  padding: 8,
20383
20193
  borderRadius: 12,
20384
20194
  boxShadow: "0 8px 24px rgba(0,0,0,0.25)",
@@ -20386,9 +20196,9 @@ function VideoToolbar({
20386
20196
  opacity: pending ? 0.6 : 1
20387
20197
  },
20388
20198
  children: [
20389
- /* @__PURE__ */ jsx23(VideoUrlInput, { value: videoId, onCommit: onSetVideoId }),
20390
- /* @__PURE__ */ jsx23(Pill, { label: "Remove video", onClick: onRemove, disabled: !videoId }),
20391
- error && /* @__PURE__ */ jsx23("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
20199
+ /* @__PURE__ */ jsx22(VideoUrlInput, { value: videoId, onCommit: onSetVideoId }),
20200
+ /* @__PURE__ */ jsx22(Pill2, { label: "Remove video", onClick: onRemove, disabled: !videoId }),
20201
+ error && /* @__PURE__ */ jsx22("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
20392
20202
  ]
20393
20203
  }
20394
20204
  );
@@ -20508,8 +20318,8 @@ function AiVideoOverlay({
20508
20318
  const activeBlockRect = useLiveBlockRect(active);
20509
20319
  const TOOLBAR_APPROX_H = 90;
20510
20320
  const toolbarState = activeBlockRect && activeBlockRect.top < TOOLBAR_APPROX_H ? "active-bottom" : "active-top";
20511
- return /* @__PURE__ */ jsxs14(Fragment9, { children: [
20512
- hoverHandleRect && hover && /* @__PURE__ */ jsx23(
20321
+ return /* @__PURE__ */ jsxs13(Fragment9, { children: [
20322
+ hoverHandleRect && hover && /* @__PURE__ */ jsx22(
20513
20323
  "button",
20514
20324
  {
20515
20325
  type: "button",
@@ -20537,15 +20347,15 @@ function AiVideoOverlay({
20537
20347
  fontSize: 14,
20538
20348
  fontWeight: 700
20539
20349
  },
20540
- children: /* @__PURE__ */ jsx23(Star3, { size: 14, style: { pointerEvents: "none" } })
20350
+ children: /* @__PURE__ */ jsx22(Star3, { size: 14, style: { pointerEvents: "none" } })
20541
20351
  }
20542
20352
  ),
20543
- activeBlockRect && active && activeEntry && /* @__PURE__ */ jsx23(
20353
+ activeBlockRect && active && activeEntry && /* @__PURE__ */ jsx22(
20544
20354
  ItemInteractionLayer,
20545
20355
  {
20546
20356
  rect: activeBlockRect,
20547
20357
  state: toolbarState,
20548
- toolbar: /* @__PURE__ */ jsx23(
20358
+ toolbar: /* @__PURE__ */ jsx22(
20549
20359
  VideoToolbar,
20550
20360
  {
20551
20361
  videoId: videoIdAt(activeEntry.tree, active.path),
@@ -20620,10 +20430,10 @@ function setCarouselAutoplay(tree, on) {
20620
20430
  }
20621
20431
 
20622
20432
  // src/ui/ai-section/AiFadeCarouselOverlay.tsx
20623
- import { Fragment as Fragment10, jsx as jsx24, jsxs as jsxs15 } from "react/jsx-runtime";
20433
+ import { Fragment as Fragment10, jsx as jsx23, jsxs as jsxs14 } from "react/jsx-runtime";
20624
20434
  var PRIMARY6 = "#0885FE";
20625
- var OHW_BLUE2 = `var(--ohw-primary, ${PRIMARY6})`;
20626
- var OHW_BLUE_DARK2 = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY6}) 60%, #000000)`;
20435
+ var OHW_BLUE3 = `var(--ohw-primary, ${PRIMARY6})`;
20436
+ var OHW_BLUE_DARK3 = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY6}) 60%, #000000)`;
20627
20437
  function findCarouselItemElement(sectionId, index) {
20628
20438
  const sectionEl = findByInstanceId(sectionId);
20629
20439
  if (!sectionEl) return null;
@@ -20702,8 +20512,8 @@ function requestAssetPick2(postToParent2) {
20702
20512
  postToParent2({ type: "ow:asset-pick", id });
20703
20513
  });
20704
20514
  }
20705
- function Pill2({ label, onClick, disabled }) {
20706
- return /* @__PURE__ */ jsx24(
20515
+ function Pill3({ label, onClick, disabled }) {
20516
+ return /* @__PURE__ */ jsx23(
20707
20517
  "button",
20708
20518
  {
20709
20519
  type: "button",
@@ -20719,7 +20529,7 @@ function Pill2({ label, onClick, disabled }) {
20719
20529
  border: "none",
20720
20530
  cursor: disabled ? "default" : "pointer",
20721
20531
  background: "#ffffff",
20722
- color: OHW_BLUE_DARK2,
20532
+ color: OHW_BLUE_DARK3,
20723
20533
  opacity: disabled ? 0.4 : 1,
20724
20534
  fontSize: 12,
20725
20535
  lineHeight: "16px",
@@ -20737,7 +20547,7 @@ function TogglePill({
20737
20547
  onToggle,
20738
20548
  disabled
20739
20549
  }) {
20740
- return /* @__PURE__ */ jsxs15(
20550
+ return /* @__PURE__ */ jsxs14(
20741
20551
  "button",
20742
20552
  {
20743
20553
  type: "button",
@@ -20756,7 +20566,7 @@ function TogglePill({
20756
20566
  border: "none",
20757
20567
  cursor: disabled ? "default" : "pointer",
20758
20568
  background: "#ffffff",
20759
- color: OHW_BLUE_DARK2,
20569
+ color: OHW_BLUE_DARK3,
20760
20570
  opacity: disabled ? 0.4 : 1,
20761
20571
  fontSize: 12,
20762
20572
  lineHeight: "16px",
@@ -20766,7 +20576,7 @@ function TogglePill({
20766
20576
  },
20767
20577
  children: [
20768
20578
  label,
20769
- /* @__PURE__ */ jsx24(
20579
+ /* @__PURE__ */ jsx23(
20770
20580
  "span",
20771
20581
  {
20772
20582
  "aria-hidden": true,
@@ -20779,10 +20589,10 @@ function TogglePill({
20779
20589
  // Off is a grey track, not a washed-out blue: the pill's own white background already
20780
20590
  // carries the brand colour in the text, and a pale blue track next to it read as
20781
20591
  // "disabled control" rather than "switch that is off".
20782
- background: on ? OHW_BLUE_DARK2 : "#CBD5E1",
20592
+ background: on ? OHW_BLUE_DARK3 : "#CBD5E1",
20783
20593
  transition: "background 120ms ease"
20784
20594
  },
20785
- children: /* @__PURE__ */ jsx24(
20595
+ children: /* @__PURE__ */ jsx23(
20786
20596
  "span",
20787
20597
  {
20788
20598
  style: {
@@ -20811,7 +20621,7 @@ function CarouselSlideToolbar({
20811
20621
  onChangeImage,
20812
20622
  onToggleAutoplay
20813
20623
  }) {
20814
- return /* @__PURE__ */ jsxs15(
20624
+ return /* @__PURE__ */ jsxs14(
20815
20625
  "div",
20816
20626
  {
20817
20627
  "data-ohw-bridge": "",
@@ -20822,7 +20632,7 @@ function CarouselSlideToolbar({
20822
20632
  flexDirection: "column",
20823
20633
  gap: 6,
20824
20634
  alignItems: "center",
20825
- background: OHW_BLUE2,
20635
+ background: OHW_BLUE3,
20826
20636
  padding: 8,
20827
20637
  borderRadius: 12,
20828
20638
  boxShadow: "0 8px 24px rgba(0,0,0,0.25)",
@@ -20834,11 +20644,11 @@ function CarouselSlideToolbar({
20834
20644
  pointerEvents: pending ? "none" : "auto"
20835
20645
  },
20836
20646
  children: [
20837
- /* @__PURE__ */ jsxs15("div", { style: { display: "flex", gap: 6, alignItems: "center" }, children: [
20838
- /* @__PURE__ */ jsx24(Pill2, { label: "Change Image", onClick: onChangeImage }),
20839
- /* @__PURE__ */ jsx24(TogglePill, { label: "Autoplay", on: autoplay, onToggle: onToggleAutoplay })
20647
+ /* @__PURE__ */ jsxs14("div", { style: { display: "flex", gap: 6, alignItems: "center" }, children: [
20648
+ /* @__PURE__ */ jsx23(Pill3, { label: "Change Image", onClick: onChangeImage }),
20649
+ /* @__PURE__ */ jsx23(TogglePill, { label: "Autoplay", on: autoplay, onToggle: onToggleAutoplay })
20840
20650
  ] }),
20841
- error && /* @__PURE__ */ jsx24("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
20651
+ error && /* @__PURE__ */ jsx23("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
20842
20652
  ]
20843
20653
  }
20844
20654
  );
@@ -20978,8 +20788,8 @@ function AiFadeCarouselOverlay({
20978
20788
  const activeItemRect = useLiveItemRect2(active);
20979
20789
  const TOOLBAR_APPROX_H = 90;
20980
20790
  const toolbarState = activeItemRect && activeItemRect.top < TOOLBAR_APPROX_H ? "active-bottom" : "active-top";
20981
- return /* @__PURE__ */ jsxs15(Fragment10, { children: [
20982
- hoverHandleRect && hover && /* @__PURE__ */ jsx24(
20791
+ return /* @__PURE__ */ jsxs14(Fragment10, { children: [
20792
+ hoverHandleRect && hover && /* @__PURE__ */ jsx23(
20983
20793
  "button",
20984
20794
  {
20985
20795
  type: "button",
@@ -21008,10 +20818,10 @@ function AiFadeCarouselOverlay({
21008
20818
  fontSize: 14,
21009
20819
  fontWeight: 700
21010
20820
  },
21011
- children: /* @__PURE__ */ jsx24(Star4, { size: 14, style: { pointerEvents: "none" } })
20821
+ children: /* @__PURE__ */ jsx23(Star4, { size: 14, style: { pointerEvents: "none" } })
21012
20822
  }
21013
20823
  ),
21014
- swapping && /* @__PURE__ */ jsx24(
20824
+ swapping && /* @__PURE__ */ jsx23(
21015
20825
  "div",
21016
20826
  {
21017
20827
  ref: skeletonRef,
@@ -21029,15 +20839,15 @@ function AiFadeCarouselOverlay({
21029
20839
  zIndex: 2147483646,
21030
20840
  pointerEvents: "none"
21031
20841
  },
21032
- children: /* @__PURE__ */ jsx24("style", { children: MEDIA_SKELETON_CSS })
20842
+ children: /* @__PURE__ */ jsx23("style", { children: MEDIA_SKELETON_CSS })
21033
20843
  }
21034
20844
  ),
21035
- activeItemRect && active && activeEntry && /* @__PURE__ */ jsx24(
20845
+ activeItemRect && active && activeEntry && /* @__PURE__ */ jsx23(
21036
20846
  ItemInteractionLayer,
21037
20847
  {
21038
20848
  rect: activeItemRect,
21039
20849
  state: toolbarState,
21040
- toolbar: /* @__PURE__ */ jsx24(
20850
+ toolbar: /* @__PURE__ */ jsx23(
21041
20851
  CarouselSlideToolbar,
21042
20852
  {
21043
20853
  pending,
@@ -21255,11 +21065,118 @@ function scrollToHashSectionWhenReady(behavior = "smooth") {
21255
21065
  // src/ui/link-modal/LinkPopover.tsx
21256
21066
  import { useEffect as useEffect13 } from "react";
21257
21067
 
21068
+ // src/ui/dialog.tsx
21069
+ import * as React9 from "react";
21070
+ import { Dialog as DialogPrimitive } from "radix-ui";
21071
+ import { X as X2 } from "lucide-react";
21072
+ import { jsx as jsx24, jsxs as jsxs15 } from "react/jsx-runtime";
21073
+ function Dialog2({
21074
+ ...props
21075
+ }) {
21076
+ return /* @__PURE__ */ jsx24(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
21077
+ }
21078
+ function DialogPortal({
21079
+ ...props
21080
+ }) {
21081
+ return /* @__PURE__ */ jsx24(DialogPrimitive.Portal, { ...props });
21082
+ }
21083
+ function DialogOverlay({
21084
+ className,
21085
+ ...props
21086
+ }) {
21087
+ return /* @__PURE__ */ jsx24(
21088
+ DialogPrimitive.Overlay,
21089
+ {
21090
+ "data-slot": "dialog-overlay",
21091
+ "data-ohw-link-modal-root": "",
21092
+ className: cn("fixed inset-0 z-[2147483646] bg-black/50", className),
21093
+ ...props
21094
+ }
21095
+ );
21096
+ }
21097
+ var DialogContent = React9.forwardRef(
21098
+ ({ className, children, showCloseButton = true, container, ...props }, ref) => {
21099
+ const positionMode = container ? "absolute" : "fixed";
21100
+ return /* @__PURE__ */ jsxs15(DialogPortal, { container: container ?? void 0, children: [
21101
+ /* @__PURE__ */ jsx24(DialogOverlay, { className: cn(positionMode, "inset-0") }),
21102
+ /* @__PURE__ */ jsxs15(
21103
+ DialogPrimitive.Content,
21104
+ {
21105
+ ref,
21106
+ "data-slot": "dialog-content",
21107
+ className: cn(
21108
+ positionMode,
21109
+ "left-1/2 top-1/2 z-[2147483647] flex w-full max-w-[448px] -translate-x-1/2 -translate-y-1/2 flex-col overflow-visible",
21110
+ "rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
21111
+ container ? "max-h-[calc(100%-32px)]" : "max-h-[calc(100vh-32px)]",
21112
+ className
21113
+ ),
21114
+ onMouseDown: (e) => e.stopPropagation(),
21115
+ ...props,
21116
+ children: [
21117
+ children,
21118
+ showCloseButton ? /* @__PURE__ */ jsx24(
21119
+ DialogPrimitive.Close,
21120
+ {
21121
+ type: "button",
21122
+ className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
21123
+ "aria-label": "Close",
21124
+ children: /* @__PURE__ */ jsx24(X2, { size: 16, "aria-hidden": true })
21125
+ }
21126
+ ) : null
21127
+ ]
21128
+ }
21129
+ )
21130
+ ] });
21131
+ }
21132
+ );
21133
+ DialogContent.displayName = DialogPrimitive.Content.displayName;
21134
+ function DialogHeader({
21135
+ className,
21136
+ ...props
21137
+ }) {
21138
+ return /* @__PURE__ */ jsx24("div", { className: cn("flex flex-col gap-1.5", className), ...props });
21139
+ }
21140
+ function DialogFooter({
21141
+ className,
21142
+ ...props
21143
+ }) {
21144
+ return /* @__PURE__ */ jsx24(
21145
+ "div",
21146
+ {
21147
+ className: cn("flex items-center justify-end gap-2", className),
21148
+ ...props
21149
+ }
21150
+ );
21151
+ }
21152
+ var DialogTitle = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx24(
21153
+ DialogPrimitive.Title,
21154
+ {
21155
+ ref,
21156
+ className: cn(
21157
+ "text-lg font-semibold leading-none tracking-tight text-card-foreground",
21158
+ className
21159
+ ),
21160
+ ...props
21161
+ }
21162
+ ));
21163
+ DialogTitle.displayName = DialogPrimitive.Title.displayName;
21164
+ var DialogDescription = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx24(
21165
+ DialogPrimitive.Description,
21166
+ {
21167
+ ref,
21168
+ className: cn("text-sm text-muted-foreground", className),
21169
+ ...props
21170
+ }
21171
+ ));
21172
+ DialogDescription.displayName = DialogPrimitive.Description.displayName;
21173
+ var DialogClose = DialogPrimitive.Close;
21174
+
21258
21175
  // src/ui/link-modal/LinkEditorPanel.tsx
21259
21176
  import { Info, X as X4 } from "lucide-react";
21260
21177
 
21261
21178
  // src/ui/link-modal/DestinationBreadcrumb.tsx
21262
- import { ArrowRight as ArrowRight3, File, GalleryVertical } from "lucide-react";
21179
+ import { ArrowRight as ArrowRight2, File, GalleryVertical } from "lucide-react";
21263
21180
  import { jsx as jsx25, jsxs as jsxs16 } from "react/jsx-runtime";
21264
21181
  function DestinationBreadcrumb({
21265
21182
  pageTitle,
@@ -21273,7 +21190,7 @@ function DestinationBreadcrumb({
21273
21190
  /* @__PURE__ */ jsx25("span", { className: "text-sm font-medium leading-none text-foreground!", children: pageTitle })
21274
21191
  ] }),
21275
21192
  /* @__PURE__ */ jsx25(
21276
- ArrowRight3,
21193
+ ArrowRight2,
21277
21194
  {
21278
21195
  size: 16,
21279
21196
  className: "shrink-0 text-muted-foreground",
@@ -21385,7 +21302,7 @@ function Label({ className, ...props }) {
21385
21302
  }
21386
21303
 
21387
21304
  // src/ui/link-modal/UrlOrPageInput.tsx
21388
- import { ChevronDown as ChevronDown5, File as File2, X as X3 } from "lucide-react";
21305
+ import { ChevronDown as ChevronDown4, File as File2, X as X3 } from "lucide-react";
21389
21306
  import { jsx as jsx29, jsxs as jsxs18 } from "react/jsx-runtime";
21390
21307
  function FieldChevron({
21391
21308
  onClick
@@ -21398,7 +21315,7 @@ function FieldChevron({
21398
21315
  onClick,
21399
21316
  "aria-label": "Open page list",
21400
21317
  tabIndex: -1,
21401
- children: /* @__PURE__ */ jsx29(ChevronDown5, { size: 16 })
21318
+ children: /* @__PURE__ */ jsx29(ChevronDown4, { size: 16 })
21402
21319
  }
21403
21320
  );
21404
21321
  }
@@ -21628,7 +21545,7 @@ function LinkEditorPanel({ state, onClose }) {
21628
21545
  // src/ui/link-modal/SectionPickerOverlay.tsx
21629
21546
  import { useCallback as useCallback7, useEffect as useEffect11, useMemo as useMemo2, useRef as useRef10, useState as useState13 } from "react";
21630
21547
  import { createPortal as createPortal2 } from "react-dom";
21631
- import { ArrowLeft as ArrowLeft3, Check as Check4 } from "lucide-react";
21548
+ import { ArrowLeft as ArrowLeft2, Check as Check3 } from "lucide-react";
21632
21549
  import { usePathname, useRouter as useRouter2 } from "next/navigation";
21633
21550
 
21634
21551
  // src/lib/parent-scroll.ts
@@ -21905,7 +21822,7 @@ function SectionPickerOverlay({
21905
21822
  className: "h-8 min-w-0 gap-1 border-border bg-background px-2 py-1.5 shadow-sm hover:bg-muted cursor-pointer",
21906
21823
  onClick: onBack,
21907
21824
  children: [
21908
- /* @__PURE__ */ jsx31(ArrowLeft3, { className: "size-4 shrink-0", "aria-hidden": true }),
21825
+ /* @__PURE__ */ jsx31(ArrowLeft2, { className: "size-4 shrink-0", "aria-hidden": true }),
21909
21826
  "Back"
21910
21827
  ]
21911
21828
  }
@@ -21974,7 +21891,7 @@ function SectionPickerOverlay({
21974
21891
  className: "absolute right-3 top-3 flex size-8 items-center justify-center rounded-full text-white",
21975
21892
  style: { backgroundColor: "var(--ohw-primary, #0885fe)" },
21976
21893
  "aria-hidden": true,
21977
- children: /* @__PURE__ */ jsx31(Check4, { className: "size-5" })
21894
+ children: /* @__PURE__ */ jsx31(Check3, { className: "size-5" })
21978
21895
  }
21979
21896
  ) : null
21980
21897
  ]
@@ -22358,7 +22275,6 @@ function shouldUseDevFixtures() {
22358
22275
  function getLogoElement(el) {
22359
22276
  const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
22360
22277
  if (marked) return marked;
22361
- if (el.closest('[data-ohw-editable="icon"]')) return null;
22362
22278
  const root = el.closest("nav, [data-ohw-nav-root], footer");
22363
22279
  if (!root) return null;
22364
22280
  const anchor = el.closest("a");
@@ -22428,7 +22344,7 @@ function addFooterColumnWithPersist({
22428
22344
  import {
22429
22345
  useCallback as useCallback9,
22430
22346
  useEffect as useEffect14,
22431
- useLayoutEffect as useLayoutEffect6,
22347
+ useLayoutEffect as useLayoutEffect5,
22432
22348
  useRef as useRef11,
22433
22349
  useState as useState15
22434
22350
  } from "react";
@@ -22525,7 +22441,7 @@ function FloatingPanel({
22525
22441
  return () => window.removeEventListener("message", onParentScroll);
22526
22442
  }, [open]);
22527
22443
  const positionRef = useRef11(position);
22528
- useLayoutEffect6(() => {
22444
+ useLayoutEffect5(() => {
22529
22445
  if (!open) {
22530
22446
  positionRef.current = position;
22531
22447
  return;
@@ -22535,7 +22451,7 @@ function FloatingPanel({
22535
22451
  }
22536
22452
  positionRef.current = position;
22537
22453
  }, [open, position, liveScroll, parentScroll]);
22538
- useLayoutEffect6(() => {
22454
+ useLayoutEffect5(() => {
22539
22455
  if (!open || !panelRef.current) return;
22540
22456
  const el = panelRef.current;
22541
22457
  const header = el.querySelector("[data-ohw-floating-panel-header]");
@@ -22744,7 +22660,7 @@ function SocialsDisplayPanel({ display, onChange, className }) {
22744
22660
  }
22745
22661
 
22746
22662
  // src/ui/button-style-panel.tsx
22747
- import { Check as Check5 } from "lucide-react";
22663
+ import { Check as Check4 } from "lucide-react";
22748
22664
  import { jsx as jsx35, jsxs as jsxs24 } from "react/jsx-runtime";
22749
22665
  var SIZE_LABEL = { s: "S", m: "M", l: "L" };
22750
22666
  function ButtonStylePanel({
@@ -22815,7 +22731,7 @@ function ButtonStylePanel({
22815
22731
  {
22816
22732
  className: "absolute right-0.5 top-0.5 flex size-5 items-center justify-center rounded-full bg-primary text-primary-foreground",
22817
22733
  "aria-hidden": true,
22818
- children: /* @__PURE__ */ jsx35(Check5, { className: "size-3", strokeWidth: 3 })
22734
+ children: /* @__PURE__ */ jsx35(Check4, { className: "size-3", strokeWidth: 3 })
22819
22735
  }
22820
22736
  ) : null
22821
22737
  ]
@@ -23840,7 +23756,7 @@ function useSectionDrag({
23840
23756
  }
23841
23757
 
23842
23758
  // src/ui/footer-container-chrome.tsx
23843
- import { Plus as Plus4 } from "lucide-react";
23759
+ import { Plus as Plus3 } from "lucide-react";
23844
23760
  import { jsx as jsx36, jsxs as jsxs25 } from "react/jsx-runtime";
23845
23761
  function FooterContainerChrome({
23846
23762
  rect,
@@ -23881,7 +23797,7 @@ function FooterContainerChrome({
23881
23797
  if (addDisabled) return;
23882
23798
  onAdd();
23883
23799
  },
23884
- children: /* @__PURE__ */ jsx36(Plus4, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
23800
+ children: /* @__PURE__ */ jsx36(Plus3, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
23885
23801
  }
23886
23802
  ) }),
23887
23803
  /* @__PURE__ */ jsx36(TooltipContent, { side: "bottom", sideOffset: 9, children: addDisabled ? "Maximum columns reached" : "Add item" })
@@ -24059,104 +23975,8 @@ function deleteSelectedNavFooterItem(deps) {
24059
23975
  return false;
24060
23976
  }
24061
23977
 
24062
- // src/lib/nav-page-sync.ts
24063
- function labelKeyFor(hrefKey) {
24064
- return hrefKey.replace(/-href$/, "-label");
24065
- }
24066
- function isPageLinkKey(key) {
24067
- return isNavbarHrefKey(key) || isFooterHrefKey(key);
24068
- }
24069
- function currentHref(key, anchor, content) {
24070
- const stored = content[key];
24071
- if (typeof stored === "string" && stored.length > 0) return stored;
24072
- return anchor.getAttribute("href") ?? "";
24073
- }
24074
- function normalizePath2(value) {
24075
- if (value.length > 1 && value.endsWith("/")) return value.slice(0, -1);
24076
- return value;
24077
- }
24078
- function pointsAt(href, path) {
24079
- const normalized = normalizePath2(href);
24080
- const target = normalizePath2(path);
24081
- if (normalized === target) return true;
24082
- return (normalized.startsWith(`${target}#`) || normalized.startsWith(`${target}?`)) && target !== "/";
24083
- }
24084
- function retargetHref(href, oldPath, newPath) {
24085
- const normalized = normalizePath2(href);
24086
- const target = normalizePath2(oldPath);
24087
- if (normalized === target) return newPath;
24088
- return `${newPath}${normalized.slice(target.length)}`;
24089
- }
24090
- function setLabelText(key, text) {
24091
- document.querySelectorAll(`[data-ohw-key="${CSS.escape(key)}"]`).forEach((el) => {
24092
- el.textContent = text;
24093
- });
24094
- }
24095
- function commit(delta, deps) {
24096
- if (Object.keys(delta).length === 0) return;
24097
- deps.setEditContent({ ...deps.getEditContent(), ...delta });
24098
- deps.postToParent({
24099
- type: "ow:change",
24100
- nodes: Object.entries(delta).map(([key, text]) => ({ key, text }))
24101
- });
24102
- }
24103
- function navLinksTo(path, content) {
24104
- return Array.from(document.querySelectorAll("[data-ohw-href-key]")).filter((anchor) => {
24105
- const key = anchor.getAttribute("data-ohw-href-key");
24106
- return isNavbarHrefKey(key) && pointsAt(currentHref(key, anchor, content), path);
24107
- }).map((anchor) => anchor.getAttribute("data-ohw-href-key"));
24108
- }
24109
- function addNavLinkForPage(page, deps) {
24110
- const content = deps.getEditContent();
24111
- if (navLinksTo(page.path, content).length > 0) return false;
24112
- const roots = listNavbarRootItems();
24113
- const afterAnchor = roots[roots.length - 1] ?? null;
24114
- let inserted;
24115
- try {
24116
- inserted = insertNavbarItem(page.path, page.title, afterAnchor);
24117
- } catch {
24118
- return false;
24119
- }
24120
- const { hrefKey, labelKey, index } = inserted;
24121
- deps.applyLinkByKey(hrefKey, page.path);
24122
- setLabelText(labelKey, page.title);
24123
- commit(
24124
- {
24125
- [hrefKey]: page.path,
24126
- [labelKey]: page.title,
24127
- [NAV_COUNT_KEY]: String(index + 1),
24128
- [NAV_ORDER_KEY]: getNavbarOrderJson()
24129
- },
24130
- deps
24131
- );
24132
- return true;
24133
- }
24134
- function retargetPageLinks(change, deps) {
24135
- const { oldPath, newPath, oldTitle, newTitle } = change;
24136
- if (!oldPath || !newPath || normalizePath2(oldPath) === normalizePath2(newPath)) return 0;
24137
- const content = deps.getEditContent();
24138
- const delta = {};
24139
- for (const anchor of Array.from(document.querySelectorAll("[data-ohw-href-key]"))) {
24140
- const hrefKey = anchor.getAttribute("data-ohw-href-key");
24141
- if (!isPageLinkKey(hrefKey)) continue;
24142
- const href = currentHref(hrefKey, anchor, content);
24143
- if (!pointsAt(href, oldPath)) continue;
24144
- const nextHref = retargetHref(href, oldPath, newPath);
24145
- deps.applyLinkByKey(hrefKey, nextHref);
24146
- delta[hrefKey] = nextHref;
24147
- if (!oldTitle || !newTitle) continue;
24148
- const labelKey = labelKeyFor(hrefKey);
24149
- const label = content[labelKey] ?? anchor.querySelector('[data-ohw-editable="text"]')?.textContent ?? "";
24150
- if (label.trim() !== oldTitle) continue;
24151
- setLabelText(labelKey, newTitle);
24152
- delta[labelKey] = newTitle;
24153
- }
24154
- commit(delta, deps);
24155
- return Object.keys(delta).filter((key) => key.endsWith("-href")).length;
24156
- }
24157
-
24158
23978
  // src/ui/navbar-container-chrome.tsx
24159
- import { Plus as Plus5 } from "lucide-react";
23979
+ import { Plus as Plus4 } from "lucide-react";
24160
23980
  import { jsx as jsx37 } from "react/jsx-runtime";
24161
23981
  function NavbarContainerChrome({
24162
23982
  rect,
@@ -24192,7 +24012,7 @@ function NavbarContainerChrome({
24192
24012
  e.stopPropagation();
24193
24013
  onAdd();
24194
24014
  },
24195
- children: /* @__PURE__ */ jsx37(Plus5, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
24015
+ children: /* @__PURE__ */ jsx37(Plus4, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
24196
24016
  }
24197
24017
  )
24198
24018
  }
@@ -24264,7 +24084,7 @@ function Badge({ className, variant, ...props }) {
24264
24084
  }
24265
24085
 
24266
24086
  // src/OhhwellsBridge.tsx
24267
- import { Brush as Brush2, Link as Link2, Plus as Plus6, Settings } from "lucide-react";
24087
+ import { Brush as Brush2, Link as Link2, Plus as Plus5, Settings } from "lucide-react";
24268
24088
  import { Fragment as Fragment13, jsx as jsx40, jsxs as jsxs26 } from "react/jsx-runtime";
24269
24089
  var PRIMARY7 = "#0885FE";
24270
24090
  var IMAGE_FADE_MS = 300;
@@ -25042,7 +24862,7 @@ function getNavigationSelectionParent(el) {
25042
24862
  if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
25043
24863
  return resolveFooterLinksContainerFrom(el);
25044
24864
  }
25045
- if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isFooterLinksContainer(el) || isInferredFooterGroup2(el)) {
24865
+ if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
25046
24866
  return getNavigationRoot(el);
25047
24867
  }
25048
24868
  return null;
@@ -25264,6 +25084,7 @@ var ICONS = {
25264
25084
  insertUnorderedList: '<line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/>',
25265
25085
  insertOrderedList: '<line x1="10" y1="6" x2="21" y2="6"/><line x1="10" y1="12" x2="21" y2="12"/><line x1="10" y1="18" x2="21" y2="18"/><path d="M4 6h1v4"/><path d="M4 10h2"/><path d="M6 18H4c0-1 2-2 2-3s-1-1.5-2-1"/>'
25266
25086
  };
25087
+ var HEIGHT_SETTLE_DELAYS = [150, 400, 800];
25267
25088
  var SELECTION_CHROME_GAP2 = 4;
25268
25089
  var TOOLBAR_STROKE_GAP2 = 4;
25269
25090
  var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
@@ -25361,7 +25182,7 @@ function applyVisibleViewport(el, parentScroll) {
25361
25182
  el.style.top = `${top}px`;
25362
25183
  el.style.height = `${height}px`;
25363
25184
  }
25364
- var TOOLBAR_EDGE_MARGIN4 = 4;
25185
+ var TOOLBAR_EDGE_MARGIN3 = 4;
25365
25186
  function clampToolbarToClip(top, transform, rect, clip, approxH, gap) {
25366
25187
  const isAbove = transform.includes("translateY(-100%)");
25367
25188
  let visualTop = isAbove ? top - approxH : top;
@@ -25388,8 +25209,8 @@ function clampToolbarToClip(top, transform, rect, clip, approxH, gap) {
25388
25209
  function clampToolbarLeft(centerX, toolbarWidth) {
25389
25210
  const half = toolbarWidth / 2;
25390
25211
  return Math.max(
25391
- TOOLBAR_EDGE_MARGIN4 + half,
25392
- Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN4 - half)
25212
+ TOOLBAR_EDGE_MARGIN3 + half,
25213
+ Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN3 - half)
25393
25214
  );
25394
25215
  }
25395
25216
  function calcToolbarPos(rect, parentScroll, toolbarW = 306) {
@@ -25790,7 +25611,7 @@ function OhhwellsBridge() {
25790
25611
  }, []);
25791
25612
  const subdomainFromQuery = searchParams.get("subdomain");
25792
25613
  const [subdomain, setSubdomain] = useState18(() => resolveSubdomain(subdomainFromQuery));
25793
- useLayoutEffect7(() => {
25614
+ useLayoutEffect6(() => {
25794
25615
  const next = resolveSubdomain(subdomainFromQuery);
25795
25616
  if (next !== subdomain) setSubdomain(next);
25796
25617
  if (next) return;
@@ -26269,6 +26090,21 @@ function OhhwellsBridge() {
26269
26090
  const [isItemDragging, setIsItemDragging] = useState18(false);
26270
26091
  const [isFooterFrameSelection, setIsFooterFrameSelection] = useState18(false);
26271
26092
  isFooterFrameSelectionRef.current = isFooterFrameSelection;
26093
+ const [floatingPanel, setFloatingPanel] = useState18(null);
26094
+ const floatingPanelOpenRef = useRef14(false);
26095
+ floatingPanelOpenRef.current = floatingPanel !== null;
26096
+ const floatingPanelRef = useRef14(null);
26097
+ floatingPanelRef.current = floatingPanel;
26098
+ const [floatingPanelPos, setFloatingPanelPos] = useState18(null);
26099
+ const buttonCollectionDragRef = useRef14(null);
26100
+ const buttonDropInsertIndexRef = useRef14(null);
26101
+ const [buttonDropSlots, setButtonDropSlots] = useState18([]);
26102
+ const [activeButtonDropIndex, setActiveButtonDropIndex] = useState18(null);
26103
+ const buttonPointerDragRef = useRef14(null);
26104
+ const [buttonStyleTick, setButtonStyleTick] = useState18(0);
26105
+ const [logoSizeDraft, setLogoSizeDraft] = useState18(null);
26106
+ const [editorViewport, setEditorViewport] = useState18("desktop");
26107
+ const [parentScrollSnap, setParentScrollSnap] = useState18(null);
26272
26108
  const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = useState18(null);
26273
26109
  const [footerHeadingVisible, setFooterHeadingVisible] = useState18(null);
26274
26110
  const footerDragRef = useRef14(null);
@@ -26294,21 +26130,6 @@ function OhhwellsBridge() {
26294
26130
  const stylesRef = useRef14("");
26295
26131
  const appliedDrawerRef = useRef14(null);
26296
26132
  const pendingDeleteUndoRef = useRef14(null);
26297
- const [floatingPanel, setFloatingPanel] = useState18(null);
26298
- const floatingPanelOpenRef = useRef14(false);
26299
- const floatingPanelRef = useRef14(null);
26300
- floatingPanelRef.current = floatingPanel;
26301
- const setFloatingPanelRef = useRef14(setFloatingPanel);
26302
- const [floatingPanelPos, setFloatingPanelPos] = useState18(null);
26303
- const buttonCollectionDragRef = useRef14(null);
26304
- const buttonDropInsertIndexRef = useRef14(null);
26305
- const [buttonDropSlots, setButtonDropSlots] = useState18([]);
26306
- const [activeButtonDropIndex, setActiveButtonDropIndex] = useState18(null);
26307
- const buttonPointerDragRef = useRef14(null);
26308
- const [buttonStyleTick, setButtonStyleTick] = useState18(0);
26309
- const [logoSizeDraft, setLogoSizeDraft] = useState18(null);
26310
- const [editorViewport, setEditorViewport] = useState18("desktop");
26311
- const [parentScrollSnap, setParentScrollSnap] = useState18(null);
26312
26133
  const [sitePages, setSitePages] = useState18([]);
26313
26134
  const collectionItemsRef = useRef14(/* @__PURE__ */ new Map());
26314
26135
  const [sectionsByPath, setSectionsByPath] = useState18({});
@@ -26318,18 +26139,7 @@ function OhhwellsBridge() {
26318
26139
  const linkPopoverOpenRef = useRef14(false);
26319
26140
  const linkPopoverGraceUntilRef = useRef14(0);
26320
26141
  setLinkPopoverRef.current = setLinkPopover;
26321
- setFloatingPanelRef.current = setFloatingPanel;
26322
26142
  linkPopoverSessionRef.current = linkPopover;
26323
- floatingPanelOpenRef.current = Boolean(floatingPanel);
26324
- useEffect17(() => {
26325
- const syncViewport = () => {
26326
- const next = window.innerWidth <= 480 ? "mobile" : "desktop";
26327
- setEditorViewport((prev) => prev === next ? prev : next);
26328
- };
26329
- syncViewport();
26330
- window.addEventListener("resize", syncViewport);
26331
- return () => window.removeEventListener("resize", syncViewport);
26332
- }, []);
26333
26143
  useEffect17(() => {
26334
26144
  setSectionOrderPersistHandler((entries) => {
26335
26145
  const currentPath = window.location.pathname;
@@ -26478,43 +26288,6 @@ function OhhwellsBridge() {
26478
26288
  if (!useFixtures) postToParent2({ type: "ow:request-site-pages" });
26479
26289
  return () => window.removeEventListener("message", onSitePages);
26480
26290
  }, [isEditMode, postToParent2]);
26481
- useEffect17(() => {
26482
- if (!isEditMode) return;
26483
- const deps = {
26484
- getEditContent: () => editContentRef.current,
26485
- setEditContent: (next) => {
26486
- editContentRef.current = next;
26487
- },
26488
- applyLinkByKey,
26489
- postToParent: postToParent2
26490
- };
26491
- const onPageMessage = (e) => {
26492
- if (e.data?.type === "ow:page-created") {
26493
- const { path, title } = e.data;
26494
- if (typeof path !== "string" || !path) return;
26495
- if (addNavLinkForPage({ path, title: typeof title === "string" && title ? title : path }, deps)) {
26496
- enforceLinkHrefs();
26497
- }
26498
- return;
26499
- }
26500
- if (e.data?.type === "ow:page-renamed") {
26501
- const { oldPath, newPath, oldTitle, newTitle } = e.data;
26502
- if (typeof oldPath !== "string" || typeof newPath !== "string") return;
26503
- const moved = retargetPageLinks(
26504
- {
26505
- oldPath,
26506
- newPath,
26507
- oldTitle: typeof oldTitle === "string" ? oldTitle : void 0,
26508
- newTitle: typeof newTitle === "string" ? newTitle : void 0
26509
- },
26510
- deps
26511
- );
26512
- if (moved > 0) enforceLinkHrefs();
26513
- }
26514
- };
26515
- window.addEventListener("message", onPageMessage);
26516
- return () => window.removeEventListener("message", onPageMessage);
26517
- }, [isEditMode, postToParent2]);
26518
26291
  useEffect17(() => {
26519
26292
  if (!isEditMode || shouldUseDevFixtures()) return;
26520
26293
  void loadAllSectionsManifest().then((manifest) => {
@@ -28036,7 +27809,7 @@ function OhhwellsBridge() {
28036
27809
  isFooterFrameSelection,
28037
27810
  postToParent2
28038
27811
  ]);
28039
- useLayoutEffect7(() => {
27812
+ useLayoutEffect6(() => {
28040
27813
  if (!subdomain || isEditMode) {
28041
27814
  setFetchState("done");
28042
27815
  return;
@@ -28063,7 +27836,6 @@ function OhhwellsBridge() {
28063
27836
  applyAiFooterFromContent(content);
28064
27837
  if (typeof content[STYLE_STORE_KEY] === "string") {
28065
27838
  stylesRef.current = content[STYLE_STORE_KEY];
28066
- document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
28067
27839
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
28068
27840
  }
28069
27841
  applyBrandChrome(content);
@@ -28074,12 +27846,12 @@ function OhhwellsBridge() {
28074
27846
  if (key === "__ohw_sections") continue;
28075
27847
  if (key === AI_SECTIONS_KEY) continue;
28076
27848
  if (key === AI_FOOTER_KEY) continue;
28077
- if (key === LOGO_PLACEHOLDER_KEY) continue;
28078
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
28079
27849
  if (key === BRAND_KIT_KEY) continue;
28080
27850
  if (key === STYLE_STORE_KEY) continue;
28081
27851
  if (BRAND_CHROME_KEYS.has(key)) continue;
27852
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
28082
27853
  if (key === CUSTOM_CODE_AFTER_HEAD_KEY || key === CUSTOM_CODE_BEFORE_HEAD_KEY) continue;
27854
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
28083
27855
  if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
28084
27856
  continue;
28085
27857
  }
@@ -28310,18 +28082,17 @@ function OhhwellsBridge() {
28310
28082
  }
28311
28083
  applyAiFooterFromContent(content);
28312
28084
  if (typeof content[STYLE_STORE_KEY] === "string") {
28313
- document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
28314
28085
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
28315
28086
  }
28316
28087
  for (const [key, val] of Object.entries(content)) {
28317
28088
  if (key === "__ohw_sections") continue;
28318
28089
  if (key === AI_SECTIONS_KEY) continue;
28319
28090
  if (key === AI_FOOTER_KEY) continue;
28320
- if (key === LOGO_PLACEHOLDER_KEY) continue;
28321
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
28322
28091
  if (key === BRAND_KIT_KEY) continue;
28323
28092
  if (key === STYLE_STORE_KEY) continue;
28324
28093
  if (BRAND_CHROME_KEYS.has(key)) continue;
28094
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
28095
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
28325
28096
  if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
28326
28097
  continue;
28327
28098
  }
@@ -28404,7 +28175,7 @@ function OhhwellsBridge() {
28404
28175
  if (debounceTimer) clearTimeout(debounceTimer);
28405
28176
  };
28406
28177
  }, [subdomain, isEditMode, pathname]);
28407
- useLayoutEffect7(() => {
28178
+ useLayoutEffect6(() => {
28408
28179
  const el = document.getElementById("ohw-loader");
28409
28180
  if (!el) return;
28410
28181
  const visible = Boolean(subdomain) && fetchState !== "done";
@@ -28588,11 +28359,26 @@ function OhhwellsBridge() {
28588
28359
  const t2 = setTimeout(measure, 500);
28589
28360
  const ro = new ResizeObserver(schedule);
28590
28361
  ro.observe(document.body);
28362
+ let lastWidth = window.innerWidth;
28363
+ let resizeTimers = [];
28364
+ const clearResizeTimers = () => {
28365
+ resizeTimers.forEach(clearTimeout);
28366
+ resizeTimers = [];
28367
+ };
28368
+ const handleResize = () => {
28369
+ if (window.innerWidth === lastWidth) return;
28370
+ lastWidth = window.innerWidth;
28371
+ clearResizeTimers();
28372
+ resizeTimers = HEIGHT_SETTLE_DELAYS.map((delay) => setTimeout(measure, delay));
28373
+ };
28374
+ window.addEventListener("resize", handleResize);
28591
28375
  return () => {
28592
28376
  clearTimeout(t1);
28593
28377
  clearTimeout(t2);
28594
28378
  if (raf != null) cancelAnimationFrame(raf);
28595
28379
  ro.disconnect();
28380
+ clearResizeTimers();
28381
+ window.removeEventListener("resize", handleResize);
28596
28382
  };
28597
28383
  }, [pathname, isEditMode, postToParent2]);
28598
28384
  useEffect17(() => {
@@ -28889,13 +28675,6 @@ function OhhwellsBridge() {
28889
28675
  if (target.closest("[data-ohw-state-toggle]")) return;
28890
28676
  if (target.closest("[data-ohw-max-badge]")) return;
28891
28677
  if (isInsideLinkEditor(target)) return;
28892
- if (isInsideFloatingPanel(target)) return;
28893
- if (target.closest("[data-ohw-form-toolbar]")) return;
28894
- if (target.closest(
28895
- '[data-ohw-field-toolbar], [data-ohw-field-type-picker], [data-radix-popper-content-wrapper], [role="menu"], [data-slot="dropdown-menu-content"]'
28896
- )) {
28897
- return;
28898
- }
28899
28678
  if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
28900
28679
  clearMediaSelectionRef.current();
28901
28680
  }
@@ -28919,6 +28698,8 @@ function OhhwellsBridge() {
28919
28698
  return;
28920
28699
  }
28921
28700
  }
28701
+ if (isInsideFloatingPanel(target)) return;
28702
+ if (target.closest("[data-ohw-form-toolbar]")) return;
28922
28703
  const collectionItem = matchCollectionItemClick(target, collectionItemsRef.current);
28923
28704
  if (collectionItem) {
28924
28705
  e.preventDefault();
@@ -28930,6 +28711,11 @@ function OhhwellsBridge() {
28930
28711
  });
28931
28712
  return;
28932
28713
  }
28714
+ if (target.closest(
28715
+ '[data-ohw-field-toolbar], [data-ohw-field-type-picker], [data-radix-popper-content-wrapper], [role="menu"], [data-slot="dropdown-menu-content"]'
28716
+ )) {
28717
+ return;
28718
+ }
28933
28719
  {
28934
28720
  const formEl = getFormElement(target);
28935
28721
  const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
@@ -29083,13 +28869,9 @@ function OhhwellsBridge() {
29083
28869
  if (isIconEditable(editable) && !getSocialItem(editable)) {
29084
28870
  e.preventDefault();
29085
28871
  e.stopPropagation();
28872
+ selectIconElementRef.current(editable);
29086
28873
  const aiIcon = editable.hasAttribute("data-ai-icon");
29087
28874
  const featureIcon = aiIcon && /\.features\.\d+\.icon$/.test(editable.dataset.ohwKey ?? "");
29088
- if (aiIcon) {
29089
- aiSectionApiRef.current?.selectFromElement(editable);
29090
- } else {
29091
- selectIconElementRef.current(editable);
29092
- }
29093
28875
  postToParentRef.current({
29094
28876
  type: "ow:icon-pick",
29095
28877
  key: editable.dataset.ohwKey ?? "",
@@ -29317,9 +29099,6 @@ function OhhwellsBridge() {
29317
29099
  setHoveredItemRect(null);
29318
29100
  hoveredNavContainerRef.current = null;
29319
29101
  setHoveredNavContainerRect(null);
29320
- siblingHintElRef.current = null;
29321
- setSiblingHintRect(null);
29322
- setSiblingHintRects([]);
29323
29102
  return;
29324
29103
  }
29325
29104
  {
@@ -29440,6 +29219,7 @@ function OhhwellsBridge() {
29440
29219
  hoveredNavContainerRef.current = null;
29441
29220
  setHoveredNavContainerRect(null);
29442
29221
  hoveredItemElRef.current = editable;
29222
+ setHoveredItemRect(isSelectedForHoverRef.current(editable) ? null : editable.getBoundingClientRect());
29443
29223
  }
29444
29224
  }
29445
29225
  }
@@ -30149,11 +29929,7 @@ function OhhwellsBridge() {
30149
29929
  };
30150
29930
  const handleMouseMove = (e) => {
30151
29931
  const { clientX, clientY } = e;
30152
- if (pointOwnedByFloatingPanel(clientX, clientY)) {
30153
- dismissImageHover();
30154
- clearImageHover();
30155
- return;
30156
- }
29932
+ if (pointOwnedByFloatingPanel(clientX, clientY)) return;
30157
29933
  if (isNavDrawerOpen() && isPointOutsideOpenDrawer(clientX, clientY)) {
30158
29934
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
30159
29935
  formHoverElRef.current = null;
@@ -30182,12 +29958,7 @@ function OhhwellsBridge() {
30182
29958
  setHoveredItemRect(null);
30183
29959
  hoveredNavContainerRef.current = null;
30184
29960
  setHoveredNavContainerRect(null);
30185
- siblingHintElRef.current = null;
30186
- setSiblingHintRect(null);
30187
- setSiblingHintRects([]);
30188
29961
  dismissImageHover();
30189
- clearImageHover();
30190
- setSectionGap(null);
30191
29962
  return;
30192
29963
  }
30193
29964
  if (probeSocialsRowAt(clientX, clientY)) return;
@@ -30199,11 +29970,7 @@ function OhhwellsBridge() {
30199
29970
  if (e.data?.type !== "ow:pointer-sync") return;
30200
29971
  const { clientX, clientY } = e.data;
30201
29972
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
30202
- if (pointOwnedByFloatingPanel(clientX, clientY)) {
30203
- dismissImageHover();
30204
- clearImageHover();
30205
- return;
30206
- }
29973
+ if (pointOwnedByFloatingPanel(clientX, clientY)) return;
30207
29974
  if (isNavDrawerOpen() && isPointOutsideOpenDrawer(clientX, clientY)) return;
30208
29975
  if (probeSocialsRowAt(clientX, clientY)) return;
30209
29976
  probeSectionGapAt(clientX, clientY);
@@ -30583,7 +30350,6 @@ function OhhwellsBridge() {
30583
30350
  applyAiFooterFromContent(content);
30584
30351
  if (typeof content[STYLE_STORE_KEY] === "string") {
30585
30352
  stylesRef.current = content[STYLE_STORE_KEY];
30586
- document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
30587
30353
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
30588
30354
  }
30589
30355
  applyBrandChrome(content);
@@ -30598,12 +30364,12 @@ function OhhwellsBridge() {
30598
30364
  }
30599
30365
  if (key === AI_SECTIONS_KEY) continue;
30600
30366
  if (key === AI_FOOTER_KEY) continue;
30601
- if (key === LOGO_PLACEHOLDER_KEY) continue;
30602
- if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
30603
30367
  if (key === BRAND_KIT_KEY) continue;
30604
30368
  if (key === STYLE_STORE_KEY) continue;
30605
30369
  if (BRAND_CHROME_KEYS.has(key)) continue;
30370
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
30606
30371
  if (key === CUSTOM_CODE_AFTER_HEAD_KEY || key === CUSTOM_CODE_BEFORE_HEAD_KEY) continue;
30372
+ if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
30607
30373
  if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
30608
30374
  continue;
30609
30375
  }
@@ -31239,12 +31005,6 @@ function OhhwellsBridge() {
31239
31005
  closeLinkPopoverRef.current();
31240
31006
  return;
31241
31007
  }
31242
- if (floatingPanelOpenRef.current) {
31243
- setFloatingPanelRef.current(null);
31244
- deselectRef.current();
31245
- deactivateRef.current();
31246
- return;
31247
- }
31248
31008
  deselectRef.current();
31249
31009
  deactivateRef.current();
31250
31010
  clearMediaSelectionRef.current();
@@ -32080,10 +31840,10 @@ function OhhwellsBridge() {
32080
31840
  window.removeEventListener("message", handleDuplicateSection);
32081
31841
  window.removeEventListener("message", handleDeactivate);
32082
31842
  window.removeEventListener("message", handleSelectNav);
31843
+ document.documentElement.removeAttribute("data-ohw-panel-dragging");
32083
31844
  window.removeEventListener("message", handleToastAction);
32084
31845
  window.removeEventListener("message", handleFormCount);
32085
31846
  window.removeEventListener("message", handleUiEscape);
32086
- document.documentElement.removeAttribute("data-ohw-panel-dragging");
32087
31847
  autoSaveTimers.current.forEach(clearTimeout);
32088
31848
  autoSaveTimers.current.clear();
32089
31849
  if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
@@ -32377,7 +32137,7 @@ function OhhwellsBridge() {
32377
32137
  postToParent2({
32378
32138
  type: "ow:ready",
32379
32139
  version: "1",
32380
- bridgeVersion: "0.1.158-next.424",
32140
+ bridgeVersion: "0.1.159",
32381
32141
  path: pathname,
32382
32142
  nodes: collectEditableNodes(editContentRef.current),
32383
32143
  sections
@@ -33112,7 +32872,7 @@ function OhhwellsBridge() {
33112
32872
  className: "flex h-7 w-7 items-center justify-center rounded-md text-foreground transition-colors hover:bg-muted/80",
33113
32873
  onClick: () => setFieldTypePickerOpen((open) => !open),
33114
32874
  "data-ohw-add-field": "",
33115
- children: /* @__PURE__ */ jsx40(Plus6, { size: 15, "aria-hidden": true })
32875
+ children: /* @__PURE__ */ jsx40(Plus5, { size: 15, "aria-hidden": true })
33116
32876
  }
33117
32877
  ),
33118
32878
  /* @__PURE__ */ jsx40("div", { className: "mx-0.5 h-5 w-px bg-border" }),