@ohhwells/bridge 0.1.158-next.422 → 0.1.158

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -6682,21 +6682,6 @@ function pricingCardFeatureCount(tree, index) {
6682
6682
  const target = findPricingCollection(tree)?.children?.[index];
6683
6683
  return target ? pricingFeatureList(target).length : 0;
6684
6684
  }
6685
- function prevailingFeatureIcon(features) {
6686
- const counts = /* @__PURE__ */ new Map();
6687
- for (const row of features) {
6688
- const key = `${row.icon ?? ""}|${row.iconColor ?? ""}`;
6689
- const entry = counts.get(key) ?? { count: 0, ...row.icon ? { icon: row.icon } : {}, ...row.iconColor ? { iconColor: row.iconColor } : {} };
6690
- entry.count += 1;
6691
- counts.set(key, entry);
6692
- }
6693
- let best = null;
6694
- for (const entry of counts.values()) {
6695
- if (!best || entry.count > best.count) best = entry;
6696
- }
6697
- if (!best) return {};
6698
- return { ...best.icon ? { icon: best.icon } : {}, ...best.iconColor ? { iconColor: best.iconColor } : {} };
6699
- }
6700
6685
  function addPricingCardFeature(tree, index, text) {
6701
6686
  return withPricingCollection(tree, (collection) => {
6702
6687
  const children = collection.children;
@@ -6705,7 +6690,7 @@ function addPricingCardFeature(tree, index, text) {
6705
6690
  const features = pricingFeatureList(target);
6706
6691
  if (features.length >= PRICING_FEATURES_MAX) return false;
6707
6692
  target.slots = target.slots ?? {};
6708
- target.slots.features = [...features, { text, ...prevailingFeatureIcon(features) }];
6693
+ target.slots.features = [...features, { text }];
6709
6694
  return true;
6710
6695
  });
6711
6696
  }
@@ -7905,7 +7890,7 @@ var AI_MOBILE_CSS = [
7905
7890
  // page's first section. Top only (bottom needs no nav clearance); ~96px approximates the
7906
7891
  // template nav's own height (measured ~94px) — there's no shared `--ohw-header-h` var to read
7907
7892
  // an exact figure from (no live measurement exists), so this is a safe approximation, not a
7908
- // pixel-exact fit. Horizontal spacing already comes from `data-ai-section-inner`.
7893
+ // pixel-exact fit. Horizontal spacing already comes from `data-ai-container`.
7909
7894
  '[data-ai-overlay-clearance] [data-ai-overlap-layer="overlay"]{padding-top:96px}',
7910
7895
  // A full-bleed carousel's height is pure arithmetic: it spans the whole window, so 16/9 turns
7911
7896
  // window width straight into height. Measured at 16/9 it came out ~90% of the viewport at EVERY
@@ -7921,10 +7906,8 @@ var AI_MOBILE_CSS = [
7921
7906
  "[data-ai-section]{overflow-x:hidden}",
7922
7907
  // `:not([data-ai-full-bleed])`: this `!important` gutter beat the inline `padding: 0` that a
7923
7908
  // full-bleed section's container sets, so a carousel/media hero that is edge-to-edge on desktop
7924
- // still got a 20px band of section background down both sides on a phone. The footer's own
7925
- // container keeps the unguarded rule below — `data-ai-full-bleed` can never appear on it.
7926
- "[data-ai-section-inner]:not([data-ai-full-bleed]){padding:0 20px !important}",
7927
- "[data-ai-container]{padding:0 20px !important}",
7909
+ // still got a 20px band of section background down both sides on a phone.
7910
+ "[data-ai-container]:not([data-ai-full-bleed]){padding:0 20px !important}",
7928
7911
  "[data-ai-row]{display:flex !important;flex-direction:column !important;align-items:stretch !important}",
7929
7912
  "[data-ai-cell]{width:100%;min-width:0}",
7930
7913
  // The rule above turns every row into a COLUMN flex container on phones, which silently breaks
@@ -8129,31 +8112,6 @@ function pricingCardEmphasis(featured, ctx, hasBg) {
8129
8112
  function textAttrs(ctx, path) {
8130
8113
  return ctx.keyFor ? { "data-ohw-key": ctx.keyFor(path), "data-ohw-editable": "text" } : {};
8131
8114
  }
8132
- var AI_RESPONSIVE_CSS = [
8133
- "@media (max-width: 960px) {",
8134
- " [data-ai-responsive] [data-ai-grid] { grid-template-columns: repeat(2, 1fr) !important; }",
8135
- ' [data-ai-responsive] [data-ai-grid="1"] { grid-template-columns: 1fr !important; }',
8136
- "}",
8137
- "@media (max-width: 640px) {",
8138
- " [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
8139
- " [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
8140
- // Group containers flatten to a column on phones; span placements come along for free.
8141
- // `:not([data-ai-group="overlap"])` for the same reason as the twin pair in AI_MOBILE_CSS — see
8142
- // the comment there. This sheet's copy is the narrower selector, so without the exemption it
8143
- // flattens an `overlap` hero below 640px even though the other rule stands down: measured a 390px
8144
- // viewport with the heading/CTA in flow *under* the image, 219px of section background where the
8145
- // layered text used to be.
8146
- ' [data-ai-responsive] [data-ai-group]:not([data-ai-group="overlap"]) { display: flex !important; flex-direction: column !important; }',
8147
- ' [data-ai-responsive] [data-ai-group]:not([data-ai-group="overlap"]) > * { grid-column: auto !important; }',
8148
- // `:not([data-ai-full-bleed])` for the same reason as the twin rule in AI_MOBILE_CSS: this one
8149
- // is narrower (0,2,0 vs 0,1,0) and also `!important`, so without the guard it wins outright and
8150
- // a full-bleed hero gets its 20px side band back below 640px only — the one width where the
8151
- // other rule had already been taught to stand down.
8152
- " [data-ai-responsive] [data-ai-section-inner]:not([data-ai-full-bleed]) { padding-left: 20px !important; padding-right: 20px !important; }",
8153
- " [data-ai-responsive] { overflow-x: hidden; }",
8154
- " [data-ai-responsive] img { max-width: 100%; }",
8155
- "}"
8156
- ].join("\n");
8157
8115
  var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
8158
8116
  function MediaBox({
8159
8117
  refValue,
@@ -8208,7 +8166,7 @@ function MediaBox({
8208
8166
  position: fill ? "absolute" : void 0,
8209
8167
  inset: fill ? 0 : void 0,
8210
8168
  width: fill ? "100%" : fixedSize ? "auto" : "100%",
8211
- height: fill ? "100%" : fixedSize ? height : void 0,
8169
+ height: fill ? "100%" : void 0,
8212
8170
  aspectRatio: fill ? void 0 : aspectRatio,
8213
8171
  minHeight: fill || fixedSize ? void 0 : height ?? (aspectRatio ? void 0 : 180),
8214
8172
  borderRadius: radius,
@@ -9704,7 +9662,7 @@ function CollectionBlock({ node, ctx, path }) {
9704
9662
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
9705
9663
  "div",
9706
9664
  {
9707
- "data-ai-grid": String(itemsPerRow),
9665
+ "data-ai-grid": "",
9708
9666
  "data-ohw-pricing-collection": isPricing ? "" : void 0,
9709
9667
  style: {
9710
9668
  display: "grid",
@@ -10006,7 +9964,7 @@ function renderNode(node, ctx, path, ambientAlign) {
10006
9964
  },
10007
9965
  children: [
10008
9966
  i > 0 && ctx.heroOverlapFullBleed ? (
10009
- // The section's `data-ai-section-inner` gave up its max-width/gutter so the base
9967
+ // The section's `data-ai-container` gave up its max-width/gutter so the base
10010
9968
  // layer (this group's i===0 sibling) can span edge-to-edge — reintroduced here,
10011
9969
  // scoped to just the overlay text, so a heading/copy/CTA stack doesn't stretch
10012
9970
  // the full section width instead of reading at the usual content width.
@@ -10425,7 +10383,6 @@ function AiTreeRenderer({
10425
10383
  ...fillHeight ? { "data-ai-fill-height": "" } : {},
10426
10384
  ...needsOverlayClearance ? { "data-ai-overlay-clearance": "" } : {},
10427
10385
  ...bgAttrs,
10428
- "data-ai-responsive": "",
10429
10386
  style: {
10430
10387
  position: "relative",
10431
10388
  padding: `${padCss} 0`,
@@ -10439,13 +10396,12 @@ function AiTreeRenderer({
10439
10396
  ...fillHeight ? { display: "grid" } : {}
10440
10397
  },
10441
10398
  children: [
10442
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_RESPONSIVE_CSS }),
10443
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_MOBILE_CSS }),
10444
10399
  isOverlay && backgroundUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
10400
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_MOBILE_CSS }),
10445
10401
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
10446
10402
  "div",
10447
10403
  {
10448
- "data-ai-section-inner": "",
10404
+ "data-ai-container": "",
10449
10405
  ...fullBleedMedia ? { "data-ai-full-bleed": "" } : {},
10450
10406
  style: {
10451
10407
  position: "relative",
@@ -10462,7 +10418,7 @@ function AiTreeRenderer({
10462
10418
  children: tree.rows.map((row, r2) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
10463
10419
  "div",
10464
10420
  {
10465
- "data-ai-columns": "",
10421
+ "data-ai-row": "",
10466
10422
  style: {
10467
10423
  display: "grid",
10468
10424
  gridTemplateColumns: "repeat(12, 1fr)",
@@ -11124,10 +11080,9 @@ function applyAiSectionsToDom(state, options) {
11124
11080
  if (existing && existing.serialized === serialized && existing.container.isConnected) {
11125
11081
  continue;
11126
11082
  }
11083
+ const isNewMount = !existing || !existing.container.isConnected;
11127
11084
  let container = existing?.container;
11128
- let isNewMount = false;
11129
11085
  if (!container || !container.isConnected) {
11130
- isNewMount = true;
11131
11086
  existing?.root.unmount();
11132
11087
  container?.remove();
11133
11088
  container = document.createElement("div");
@@ -17286,7 +17241,7 @@ function apiBase() {
17286
17241
  function bridgeVersion() {
17287
17242
  try {
17288
17243
  if (true) {
17289
- return "0.1.158-next.422";
17244
+ return "0.1.158";
17290
17245
  }
17291
17246
  } catch {
17292
17247
  }
@@ -17300,7 +17255,7 @@ function stampBridgeVersionOnDocument() {
17300
17255
  return;
17301
17256
  }
17302
17257
  try {
17303
- const v = true ? "0.1.158-next.422" : "";
17258
+ const v = true ? "0.1.158" : "";
17304
17259
  if (v) {
17305
17260
  document.documentElement.setAttribute("data-ohw-bridge-version", v);
17306
17261
  window.__OHW_BRIDGE_VERSION__ = v;
@@ -17978,7 +17933,6 @@ function MediaOverlay({
17978
17933
  (prev) => prev && prev.fullWidth === width && prev.height === height ? prev : { fullWidth: width, height }
17979
17934
  );
17980
17935
  }, [isVideo]);
17981
- const replaceLabel = isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image";
17982
17936
  const replaceMode = buttonSize ? replaceButtonMode({ width: rect.width, height: rect.height }, buttonSize) : "none";
17983
17937
  const box = {
17984
17938
  position: "fixed",
@@ -18077,10 +18031,11 @@ function MediaOverlay({
18077
18031
  className: "flex items-center justify-center cursor-pointer",
18078
18032
  style: {
18079
18033
  ...box,
18080
- // Visual only. `pointer-events: auto` on this full-bleed rect covered the navbar
18081
- // (hero bg includes the absolute TopNav) and swallowed CTA press-drag. Text overlay
18082
- // and cueOnly also need clicks to fall through — Replace / video controls opt back in.
18083
- pointerEvents: "none",
18034
+ // When text overlays this media, let clicks fall THROUGH to the text underneath. In the
18035
+ // parent this needed a whole `ow:click-at` round-trip to re-hit-test inside the iframe;
18036
+ // in-document, pointer-events does it natively. The button below opts back in, so
18037
+ // Replace still works.
18038
+ pointerEvents: hover.hasTextOverlap || cueOnly ? "none" : "auto",
18084
18039
  // Selected: a firm component ring with no wash, so the image reads as chosen rather
18085
18040
  // than hovered. Hover keeps the existing tinted preview.
18086
18041
  boxShadow: selected ? "inset 0 0 0 2px var(--color-bridge-primary)" : "inset 0 0 0 1.5px var(--color-bridge-primary)",
@@ -18107,17 +18062,17 @@ function MediaOverlay({
18107
18062
  },
18108
18063
  children: [
18109
18064
  isVideo ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react5.Film, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react5.ImageIcon, { size: 14 }),
18110
- replaceLabel
18065
+ isVideo ? "Replace video" : "Replace image"
18111
18066
  ]
18112
18067
  }
18113
18068
  ),
18114
- showChrome && replaceMode !== "none" && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
18069
+ replaceMode === "none" || cueOnly ? null : /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
18115
18070
  Button,
18116
18071
  {
18117
18072
  "data-ohw-media-overlay": "",
18118
18073
  variant: "outline",
18119
18074
  size: "sm",
18120
- "aria-label": replaceLabel,
18075
+ "aria-label": isVideo ? "Replace video" : "Replace image",
18121
18076
  className: "gap-1.5 cursor-pointer hover:bg-background",
18122
18077
  style: {
18123
18078
  ...OVERLAY_BUTTON_STYLE,
@@ -18140,7 +18095,7 @@ function MediaOverlay({
18140
18095
  },
18141
18096
  children: [
18142
18097
  isVideo ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react5.Film, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react5.ImageIcon, { size: 14 }),
18143
- replaceMode === "full" ? replaceLabel : null
18098
+ replaceMode === "full" ? isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image" : null
18144
18099
  ]
18145
18100
  }
18146
18101
  )
@@ -18650,7 +18605,7 @@ function AiSectionOverlay({
18650
18605
 
18651
18606
  // src/ui/ai-section/AiMediaStripOverlay.tsx
18652
18607
  var import_react10 = require("react");
18653
- var import_lucide_react9 = require("lucide-react");
18608
+ var import_lucide_react8 = require("lucide-react");
18654
18609
 
18655
18610
  // src/lib/ai-media-strip-tree.ts
18656
18611
  var MEDIA_STRIP_ITEM_MIN = 1;
@@ -18764,6 +18719,14 @@ function setMediaStripItemAsset(tree, index, assetId) {
18764
18719
  return true;
18765
18720
  });
18766
18721
  }
18722
+ function setMediaStripItemAlt(tree, index, alt) {
18723
+ return withMediaStrip(tree, (block) => {
18724
+ const items = block.items;
18725
+ if (index < 0 || index >= items.length) return false;
18726
+ items[index] = { ...items[index], alt };
18727
+ return true;
18728
+ });
18729
+ }
18767
18730
  function setMediaStripItemLink(tree, index, link) {
18768
18731
  return withMediaStrip(tree, (block) => {
18769
18732
  const items = block.items;
@@ -18781,117 +18744,11 @@ function setMediaStripItemLink(tree, index, link) {
18781
18744
  });
18782
18745
  }
18783
18746
 
18784
- // src/ui/dialog.tsx
18785
- var React9 = __toESM(require("react"), 1);
18786
- var import_radix_ui6 = require("radix-ui");
18787
- var import_lucide_react8 = require("lucide-react");
18788
- var import_jsx_runtime20 = require("react/jsx-runtime");
18789
- function Dialog2({
18790
- ...props
18791
- }) {
18792
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_radix_ui6.Dialog.Root, { "data-slot": "dialog", ...props });
18793
- }
18794
- function DialogPortal({
18795
- ...props
18796
- }) {
18797
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_radix_ui6.Dialog.Portal, { ...props });
18798
- }
18799
- function DialogOverlay({
18800
- className,
18801
- ...props
18802
- }) {
18803
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18804
- import_radix_ui6.Dialog.Overlay,
18805
- {
18806
- "data-slot": "dialog-overlay",
18807
- "data-ohw-link-modal-root": "",
18808
- className: cn("fixed inset-0 z-[2147483646] bg-black/50", className),
18809
- ...props
18810
- }
18811
- );
18812
- }
18813
- var DialogContent = React9.forwardRef(
18814
- ({ className, children, showCloseButton = true, container, ...props }, ref) => {
18815
- const positionMode = container ? "absolute" : "fixed";
18816
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(DialogPortal, { container: container ?? void 0, children: [
18817
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(DialogOverlay, { className: cn(positionMode, "inset-0") }),
18818
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
18819
- import_radix_ui6.Dialog.Content,
18820
- {
18821
- ref,
18822
- "data-slot": "dialog-content",
18823
- className: cn(
18824
- positionMode,
18825
- "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",
18826
- "rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
18827
- container ? "max-h-[calc(100%-32px)]" : "max-h-[calc(100vh-32px)]",
18828
- className
18829
- ),
18830
- onMouseDown: (e) => e.stopPropagation(),
18831
- ...props,
18832
- children: [
18833
- children,
18834
- showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18835
- import_radix_ui6.Dialog.Close,
18836
- {
18837
- type: "button",
18838
- className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
18839
- "aria-label": "Close",
18840
- children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react8.X, { size: 16, "aria-hidden": true })
18841
- }
18842
- ) : null
18843
- ]
18844
- }
18845
- )
18846
- ] });
18847
- }
18848
- );
18849
- DialogContent.displayName = import_radix_ui6.Dialog.Content.displayName;
18850
- function DialogHeader({
18851
- className,
18852
- ...props
18853
- }) {
18854
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: cn("flex flex-col gap-1.5", className), ...props });
18855
- }
18856
- function DialogFooter({
18857
- className,
18858
- ...props
18859
- }) {
18860
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18861
- "div",
18862
- {
18863
- className: cn("flex items-center justify-end gap-2", className),
18864
- ...props
18865
- }
18866
- );
18867
- }
18868
- var DialogTitle = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18869
- import_radix_ui6.Dialog.Title,
18870
- {
18871
- ref,
18872
- className: cn(
18873
- "text-lg font-semibold leading-none tracking-tight text-card-foreground",
18874
- className
18875
- ),
18876
- ...props
18877
- }
18878
- ));
18879
- DialogTitle.displayName = import_radix_ui6.Dialog.Title.displayName;
18880
- var DialogDescription = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18881
- import_radix_ui6.Dialog.Description,
18882
- {
18883
- ref,
18884
- className: cn("text-sm text-muted-foreground", className),
18885
- ...props
18886
- }
18887
- ));
18888
- DialogDescription.displayName = import_radix_ui6.Dialog.Description.displayName;
18889
- var DialogClose = import_radix_ui6.Dialog.Close;
18890
-
18891
18747
  // src/ui/ai-section/AiMediaStripOverlay.tsx
18892
- var import_jsx_runtime21 = require("react/jsx-runtime");
18893
- var PRIMARY3 = "var(--ohw-primary, #0885FE)";
18894
- 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]";
18748
+ var import_jsx_runtime20 = require("react/jsx-runtime");
18749
+ var PRIMARY3 = "#0885FE";
18750
+ var OHW_BLUE = `var(--ohw-primary, ${PRIMARY3})`;
18751
+ var OHW_BLUE_DARK = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY3}) 60%, #000000)`;
18895
18752
  function findMediaStripItemElement(sectionId, index) {
18896
18753
  const sectionEl = findByInstanceId(sectionId);
18897
18754
  if (!sectionEl) return null;
@@ -18901,35 +18758,14 @@ function findMediaStripItemElement(sectionId, index) {
18901
18758
  }
18902
18759
  return null;
18903
18760
  }
18904
- function itemRefFromTarget(target) {
18905
- if (!(target instanceof Element)) return null;
18906
- if (target.closest(CHROME_SELECTOR)) return null;
18907
- const itemEl = target.closest("[data-ohw-media-strip-item]");
18908
- if (!itemEl) return null;
18909
- const sectionEl = itemEl.closest("[data-ohw-section]");
18910
- const unit = sectionEl ? movableUnit(sectionEl) : null;
18911
- const sectionId = unit ? instanceIdOf(unit) || null : null;
18912
- const index = Number(itemEl.dataset.ohwMediaStripIndex);
18913
- if (!sectionId || Number.isNaN(index)) return null;
18914
- return { sectionId, index };
18915
- }
18916
- function sameRef(a, b) {
18917
- return a?.sectionId === b?.sectionId && a?.index === b?.index;
18918
- }
18919
- function rectOf(el) {
18761
+ function readRect2(ref) {
18762
+ const el = ref && findMediaStripItemElement(ref.sectionId, ref.index);
18920
18763
  if (!el) return null;
18921
18764
  const r2 = el.getBoundingClientRect();
18922
18765
  if (r2.width <= 0 || r2.height <= 0) return null;
18923
18766
  return { top: r2.top, left: r2.left, width: r2.width, height: r2.height };
18924
18767
  }
18925
- function readRect2(ref) {
18926
- return rectOf(ref && findMediaStripItemElement(ref.sectionId, ref.index));
18927
- }
18928
- function readStripRect(ref) {
18929
- const item = ref && findMediaStripItemElement(ref.sectionId, ref.index);
18930
- return rectOf(item?.closest("[data-ohw-media-strip]") ?? item);
18931
- }
18932
- function useLiveItemRect(ref, strip = false) {
18768
+ function useLiveItemRect(ref) {
18933
18769
  const [rect, setRect] = (0, import_react10.useState)(null);
18934
18770
  (0, import_react10.useEffect)(() => {
18935
18771
  if (!ref) {
@@ -18937,7 +18773,7 @@ function useLiveItemRect(ref, strip = false) {
18937
18773
  return;
18938
18774
  }
18939
18775
  const update = () => {
18940
- const next = strip ? readStripRect(ref) : readRect2(ref);
18776
+ const next = readRect2(ref);
18941
18777
  setRect(
18942
18778
  (prev) => prev && next && prev.top === next.top && prev.left === next.left && prev.width === next.width && prev.height === next.height ? prev : next
18943
18779
  );
@@ -18952,49 +18788,9 @@ function useLiveItemRect(ref, strip = false) {
18952
18788
  window.removeEventListener("resize", update);
18953
18789
  clearInterval(interval);
18954
18790
  };
18955
- }, [ref?.sectionId, ref?.index, strip]);
18791
+ }, [ref?.sectionId, ref?.index]);
18956
18792
  return rect;
18957
18793
  }
18958
- var TOOLBAR_STRIP_GAP = 52;
18959
- var TOOLBAR_STRIP_GAP_MOBILE = 42;
18960
- var TOOLBAR_EDGE_MARGIN2 = 4;
18961
- function StripToolbarSlot({ stripRect, children }) {
18962
- const slotRef = (0, import_react10.useRef)(null);
18963
- const [shift, setShift] = (0, import_react10.useState)(0);
18964
- const centerX = stripRect.left + stripRect.width / 2;
18965
- (0, import_react10.useLayoutEffect)(() => {
18966
- const slot = slotRef.current;
18967
- if (!slot) return;
18968
- const clamp = () => {
18969
- const w = slot.offsetWidth;
18970
- if (w <= 0) return;
18971
- const half = w / 2;
18972
- const clamped = Math.max(TOOLBAR_EDGE_MARGIN2 + half, Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN2 - half));
18973
- setShift((prev) => Math.abs(prev - (clamped - centerX)) < 0.5 ? prev : clamped - centerX);
18974
- };
18975
- clamp();
18976
- const ro = new ResizeObserver(clamp);
18977
- ro.observe(slot);
18978
- return () => ro.disconnect();
18979
- }, [centerX, children]);
18980
- const mobile = window.innerWidth <= 480;
18981
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
18982
- "div",
18983
- {
18984
- ref: slotRef,
18985
- "data-ohw-bridge": "",
18986
- "data-ohw-item-interaction": "",
18987
- className: "pointer-events-auto fixed",
18988
- style: {
18989
- top: stripRect.top - (mobile ? TOOLBAR_STRIP_GAP_MOBILE : TOOLBAR_STRIP_GAP),
18990
- left: centerX + shift,
18991
- transform: "translate(-50%, -100%)",
18992
- zIndex: 2147483645
18993
- },
18994
- children
18995
- }
18996
- );
18997
- }
18998
18794
  function requestValidateTree(postToParent2, tree) {
18999
18795
  return new Promise((resolve) => {
19000
18796
  const id = `ai-validate-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
@@ -19031,177 +18827,221 @@ function requestAssetPick(postToParent2) {
19031
18827
  postToParent2({ type: "ow:asset-pick", id });
19032
18828
  });
19033
18829
  }
19034
- function ToolbarAction({
18830
+ function Pill({
19035
18831
  label,
19036
18832
  onClick,
19037
- disabled = false,
19038
- active = false,
19039
- children
18833
+ disabled,
18834
+ active
19040
18835
  }) {
19041
- const button = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(CustomToolbarButton, { type: "button", "aria-label": label, disabled, active, onClick, className: "size-8", children });
19042
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Tooltip, { children: [
19043
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipTrigger, { asChild: true, children: disabled ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "inline-flex", children: button }) : button }),
19044
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: label })
19045
- ] });
19046
- }
19047
- function ToolbarSeparator() {
19048
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { "aria-hidden": true, className: "mx-0.5 block h-6 w-px shrink-0 bg-border" });
18836
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18837
+ "button",
18838
+ {
18839
+ type: "button",
18840
+ onClick,
18841
+ disabled,
18842
+ style: {
18843
+ height: 28,
18844
+ padding: "0 10px",
18845
+ display: "inline-flex",
18846
+ alignItems: "center",
18847
+ gap: 4,
18848
+ borderRadius: 999,
18849
+ border: "none",
18850
+ cursor: disabled ? "default" : "pointer",
18851
+ background: active ? OHW_BLUE_DARK : "#ffffff",
18852
+ color: active ? "#ffffff" : OHW_BLUE_DARK,
18853
+ opacity: disabled ? 0.4 : 1,
18854
+ fontSize: 12,
18855
+ lineHeight: "16px",
18856
+ fontFamily: "Figtree, ui-sans-serif, system-ui, sans-serif",
18857
+ whiteSpace: "nowrap",
18858
+ flex: "0 0 auto"
18859
+ },
18860
+ children: label
18861
+ }
18862
+ );
19049
18863
  }
19050
- function OptionMenu({
19051
- label,
19052
- value,
19053
- options,
19054
- onChange
19055
- }) {
19056
- const current = options.find((o) => o.value === value) ?? options[0];
19057
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
19058
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-xs text-muted-foreground", children: label }),
19059
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(DropdownMenu, { modal: false, children: [
19060
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
19061
- "button",
19062
- {
19063
- type: "button",
19064
- "aria-label": label,
19065
- 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",
19066
- children: [
19067
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "flex-1 text-left", children: current.label }),
19068
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.ChevronDown, { size: 16, style: { pointerEvents: "none" } })
19069
- ]
19070
- }
19071
- ) }),
19072
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DropdownMenuContent, { align: "end", sideOffset: 8, className: "w-[132px] min-w-[132px] p-1", children: options.map((option) => {
19073
- const selected = option.value === value;
19074
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
19075
- DropdownMenuItem,
18864
+ function AltInput({ value, onCommit }) {
18865
+ const [draft, setDraft] = (0, import_react10.useState)(value);
18866
+ (0, import_react10.useEffect)(() => setDraft(value), [value]);
18867
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
18868
+ "div",
18869
+ {
18870
+ style: {
18871
+ display: "flex",
18872
+ alignItems: "center",
18873
+ gap: 6,
18874
+ height: 28,
18875
+ padding: "0 12px",
18876
+ borderRadius: 999,
18877
+ background: "#ffffff",
18878
+ width: "100%",
18879
+ maxWidth: 260,
18880
+ boxSizing: "border-box"
18881
+ },
18882
+ children: [
18883
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18884
+ "span",
19076
18885
  {
19077
- role: "menuitemradio",
19078
- "aria-checked": selected,
19079
- onSelect: () => onChange(option.value),
19080
- className: cn("grid grid-cols-[1fr_16px] gap-2 py-2 pl-2 pr-2 text-[13px]", selected && "bg-muted"),
19081
- children: [
19082
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { children: option.label }),
19083
- selected && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Check, { size: 16, className: "text-bridge-primary" })
19084
- ]
19085
- },
19086
- option.value
19087
- );
19088
- }) })
19089
- ] })
19090
- ] });
19091
- }
19092
- var VARIANTS = [
19093
- { value: "logos", label: "Logos" },
19094
- { value: "reel", label: "Reel" }
19095
- ];
19096
- var ALIGNS = [
19097
- { value: "left", label: "Align left", Icon: import_lucide_react9.AlignLeft },
19098
- { value: "center", label: "Align center", Icon: import_lucide_react9.AlignCenter },
19099
- { value: "right", label: "Align right", Icon: import_lucide_react9.AlignRight }
19100
- ];
19101
- var HOVERS = [
19102
- { value: "none", label: "None" },
19103
- { value: "lift", label: "Lift" },
19104
- { value: "zoom", label: "Zoom" },
19105
- { value: "fade", label: "Fade" }
19106
- ];
19107
- var HTTP_URL_RE = /^https?:\/\/\S+$/i;
19108
- function LinkModal({
19109
- href,
19110
- linkTarget,
19111
- onClose,
19112
- onSave
19113
- }) {
19114
- const [draft, setDraft] = (0, import_react10.useState)(href);
19115
- const [newTab, setNewTab] = (0, import_react10.useState)(linkTarget !== "same-tab");
18886
+ style: {
18887
+ flex: "0 0 auto",
18888
+ color: OHW_BLUE_DARK,
18889
+ fontSize: 10,
18890
+ fontWeight: 700,
18891
+ textTransform: "uppercase",
18892
+ letterSpacing: 0.4
18893
+ },
18894
+ children: "Alt"
18895
+ }
18896
+ ),
18897
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18898
+ "input",
18899
+ {
18900
+ type: "text",
18901
+ value: draft,
18902
+ onChange: (e) => setDraft(e.target.value),
18903
+ onBlur: () => {
18904
+ if (draft !== value) onCommit(draft);
18905
+ },
18906
+ onKeyDown: (e) => {
18907
+ if (e.key === "Enter") e.currentTarget.blur();
18908
+ if (e.key === "Escape") {
18909
+ setDraft(value);
18910
+ e.currentTarget.blur();
18911
+ }
18912
+ },
18913
+ placeholder: "Describe this image",
18914
+ "aria-label": "Image alt text",
18915
+ style: {
18916
+ flex: 1,
18917
+ minWidth: 0,
18918
+ border: "none",
18919
+ outline: "none",
18920
+ background: "transparent",
18921
+ fontSize: 12,
18922
+ lineHeight: "16px",
18923
+ fontFamily: "Figtree, ui-sans-serif, system-ui, sans-serif",
18924
+ color: "#1f2937"
18925
+ }
18926
+ }
18927
+ )
18928
+ ]
18929
+ }
18930
+ );
18931
+ }
18932
+ var VARIANTS = [
18933
+ { value: "logos", label: "Logos" },
18934
+ { value: "reel", label: "Reel" }
18935
+ ];
18936
+ var ALIGNS = [
18937
+ { value: "left", label: "Left" },
18938
+ { value: "center", label: "Center" },
18939
+ { value: "right", label: "Right" }
18940
+ ];
18941
+ var HOVERS = [
18942
+ { value: "none", label: "None" },
18943
+ { value: "lift", label: "Lift" },
18944
+ { value: "zoom", label: "Zoom" },
18945
+ { value: "fade", label: "Fade" }
18946
+ ];
18947
+ var TREATMENTS = [
18948
+ { value: "color", label: "Color" },
18949
+ { value: "grayscale", label: "Grayscale" }
18950
+ ];
18951
+ var HTTP_URL_RE = /^https?:\/\/\S+$/i;
18952
+ function LinkInput({ value, onCommit }) {
18953
+ const [draft, setDraft] = (0, import_react10.useState)(value);
19116
18954
  const [invalid, setInvalid] = (0, import_react10.useState)(false);
19117
- const save = () => {
18955
+ (0, import_react10.useEffect)(() => {
18956
+ setDraft(value);
18957
+ setInvalid(false);
18958
+ }, [value]);
18959
+ const commit = () => {
19118
18960
  const next = draft.trim();
19119
- if (next && !HTTP_URL_RE.test(next)) {
19120
- setInvalid(true);
19121
- return;
19122
- }
19123
- onSave(next, newTab ? "new-tab" : "same-tab");
19124
- onClose();
18961
+ if (next === value) return setInvalid(false);
18962
+ if (next && !HTTP_URL_RE.test(next)) return setInvalid(true);
18963
+ setInvalid(false);
18964
+ onCommit(next);
19125
18965
  };
19126
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Dialog2, { open: true, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(DialogContent, { "data-ohw-link-modal-root": "", className: "max-w-[400px] gap-4 p-5", children: [
19127
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DialogHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DialogTitle, { className: "text-base", children: "Image link" }) }),
19128
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-col gap-3", children: [
19129
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19130
- "input",
19131
- {
19132
- autoFocus: true,
19133
- type: "text",
19134
- inputMode: "url",
19135
- value: draft,
19136
- onChange: (e) => {
19137
- setDraft(e.target.value);
19138
- setInvalid(false);
19139
- },
19140
- onKeyDown: (e) => {
19141
- if (e.key === "Enter") save();
19142
- },
19143
- placeholder: "https://example.com",
19144
- "aria-label": "Image link URL",
19145
- "aria-invalid": invalid,
19146
- className: cn(
19147
- "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",
19148
- invalid && "border-destructive focus:border-destructive"
19149
- )
19150
- }
19151
- ),
19152
- invalid && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { className: "text-xs text-destructive", children: "Enter a full URL starting with http:// or https://" }),
19153
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("label", { className: "flex items-center gap-2 text-[13px] text-foreground", children: [
19154
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("input", { type: "checkbox", checked: newTab, onChange: (e) => setNewTab(e.target.checked) }),
19155
- "Open in new tab"
19156
- ] })
19157
- ] }),
19158
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(DialogFooter, { children: [
19159
- href && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19160
- "button",
19161
- {
19162
- type: "button",
19163
- onClick: () => {
19164
- onSave("", "new-tab");
19165
- onClose();
19166
- },
19167
- className: "mr-auto rounded-[6px] px-3 py-2 text-[13px] font-medium text-destructive transition-colors hover:bg-muted",
19168
- children: "Remove link"
19169
- }
19170
- ),
19171
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19172
- "button",
19173
- {
19174
- type: "button",
19175
- onClick: onClose,
19176
- className: "rounded-[6px] px-3 py-2 text-[13px] font-medium text-foreground transition-colors hover:bg-muted",
19177
- children: "Cancel"
19178
- }
19179
- ),
19180
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19181
- "button",
19182
- {
19183
- type: "button",
19184
- onClick: save,
19185
- className: "rounded-[6px] bg-bridge-primary px-3 py-2 text-[13px] font-medium text-primary-foreground transition-opacity hover:opacity-90",
19186
- children: "Save"
19187
- }
19188
- )
19189
- ] })
19190
- ] }) });
18966
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
18967
+ "div",
18968
+ {
18969
+ style: {
18970
+ display: "flex",
18971
+ alignItems: "center",
18972
+ gap: 6,
18973
+ height: 28,
18974
+ padding: "0 12px",
18975
+ borderRadius: 999,
18976
+ background: "#ffffff",
18977
+ width: "100%",
18978
+ maxWidth: 260,
18979
+ boxSizing: "border-box",
18980
+ boxShadow: invalid ? "inset 0 0 0 1.5px #EF4444" : "none"
18981
+ },
18982
+ children: [
18983
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18984
+ "span",
18985
+ {
18986
+ style: {
18987
+ flex: "0 0 auto",
18988
+ color: OHW_BLUE_DARK,
18989
+ fontSize: 10,
18990
+ fontWeight: 700,
18991
+ textTransform: "uppercase",
18992
+ letterSpacing: 0.4
18993
+ },
18994
+ children: "Link"
18995
+ }
18996
+ ),
18997
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18998
+ "input",
18999
+ {
19000
+ type: "text",
19001
+ inputMode: "url",
19002
+ value: draft,
19003
+ onChange: (e) => setDraft(e.target.value),
19004
+ onBlur: commit,
19005
+ onKeyDown: (e) => {
19006
+ if (e.key === "Enter") e.currentTarget.blur();
19007
+ if (e.key === "Escape") {
19008
+ setDraft(value);
19009
+ setInvalid(false);
19010
+ e.currentTarget.blur();
19011
+ }
19012
+ },
19013
+ placeholder: "https://example.com",
19014
+ "aria-label": "Image link URL",
19015
+ "aria-invalid": invalid,
19016
+ style: {
19017
+ flex: 1,
19018
+ minWidth: 0,
19019
+ border: "none",
19020
+ outline: "none",
19021
+ background: "transparent",
19022
+ fontSize: 12,
19023
+ lineHeight: "16px",
19024
+ fontFamily: "Figtree, ui-sans-serif, system-ui, sans-serif",
19025
+ color: "#1f2937"
19026
+ }
19027
+ }
19028
+ )
19029
+ ]
19030
+ }
19031
+ );
19191
19032
  }
19192
19033
  function MediaStripToolbar({
19193
19034
  entry,
19194
19035
  index,
19195
19036
  pending,
19196
19037
  error,
19197
- itemEditOn,
19198
- onToggleItemEdit,
19199
19038
  onSetVariant,
19200
19039
  onSetAlign,
19201
19040
  onSetHover,
19202
19041
  onSetTreatment,
19203
19042
  onResize,
19204
- onOpenLink,
19043
+ onSetAlt,
19044
+ onSetLink,
19205
19045
  onAddItem,
19206
19046
  onRemoveItem,
19207
19047
  onChangeImage,
@@ -19210,102 +19050,81 @@ function MediaStripToolbar({
19210
19050
  }) {
19211
19051
  const items = mediaStripItems(entry.tree);
19212
19052
  const itemCount = items.length;
19053
+ const currentAlt = items[index]?.alt ?? "";
19054
+ const currentHref = items[index]?.href ?? "";
19055
+ const currentTarget = items[index]?.linkTarget === "same-tab" ? "same-tab" : "new-tab";
19213
19056
  const slots = findMediaStripSlots(entry.tree);
19214
19057
  const variant = slots?.variant === "reel" ? "reel" : "logos";
19215
19058
  const align = slots?.align === "left" || slots?.align === "right" ? slots.align : "center";
19216
19059
  const hover = slots?.hover === "none" || slots?.hover === "lift" || slots?.hover === "zoom" || slots?.hover === "fade" ? slots.hover : "lift";
19217
19060
  const treatment = slots?.treatment === "color" || slots?.treatment === "grayscale" ? slots.treatment : variant === "logos" ? "grayscale" : "color";
19218
19061
  const sizeValue = variant === "logos" ? typeof slots?.logoMaxHeight === "number" ? slots.logoMaxHeight : 48 : typeof slots?.cardWidth === "number" ? slots.cardWidth : 240;
19219
- const mobile = window.innerWidth <= 480;
19220
- const gatedLabel = (label) => itemEditOn ? label : "Turn on the star to edit this image";
19221
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
19062
+ const isMobile = window.innerWidth <= 480;
19063
+ const rowStyle = isMobile ? {
19064
+ display: "flex",
19065
+ gap: 6,
19066
+ flexWrap: "nowrap",
19067
+ justifyContent: "center",
19068
+ overflowX: "auto",
19069
+ WebkitOverflowScrolling: "touch",
19070
+ width: "100%",
19071
+ scrollbarWidth: "none"
19072
+ } : { display: "flex", gap: 6, flexWrap: "nowrap", justifyContent: "center" };
19073
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
19222
19074
  "div",
19223
19075
  {
19224
19076
  "data-ohw-bridge": "",
19225
19077
  "data-ohw-toolbar": "",
19226
19078
  onMouseDown: (e) => e.stopPropagation(),
19227
- className: cn("flex flex-col items-center gap-1.5 font-sans", pending && "pointer-events-none opacity-60"),
19228
- style: { maxWidth: mobile ? "calc(100vw - 24px)" : void 0 },
19079
+ style: {
19080
+ display: "flex",
19081
+ flexDirection: "column",
19082
+ gap: 6,
19083
+ alignItems: "center",
19084
+ background: OHW_BLUE,
19085
+ padding: 8,
19086
+ borderRadius: 12,
19087
+ boxShadow: "0 8px 24px rgba(0,0,0,0.25)",
19088
+ pointerEvents: pending ? "none" : "auto",
19089
+ opacity: pending ? 0.6 : 1,
19090
+ maxWidth: isMobile ? "calc(100vw - 24px)" : "none"
19091
+ },
19229
19092
  children: [
19230
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
19231
- "div",
19232
- {
19233
- className: cn(
19234
- "flex items-center gap-1 rounded-[10px] border border-border bg-background p-1 whitespace-nowrap",
19235
- "shadow-[0px_10px_15px_-3px_rgba(0,0,0,0.1),0px_4px_6px_-4px_rgba(0,0,0,0.1)]",
19236
- mobile ? "flex-wrap justify-center" : "flex-nowrap"
19237
- ),
19238
- children: [
19239
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Tooltip, { children: [
19240
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19241
- CustomToolbarButton,
19242
- {
19243
- type: "button",
19244
- "aria-label": itemEditOn ? "Done editing this image" : "Edit this image",
19245
- "aria-pressed": itemEditOn,
19246
- onClick: onToggleItemEdit,
19247
- className: "group size-8",
19248
- children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19249
- import_lucide_react9.Star,
19250
- {
19251
- size: 16,
19252
- fill: "currentColor",
19253
- strokeWidth: 0,
19254
- className: cn(
19255
- "transition-colors",
19256
- itemEditOn ? "text-[#f59e0b] group-hover:text-[#d97706]" : "text-[#d6d3d1] group-hover:text-[#fbbf24]"
19257
- ),
19258
- style: { pointerEvents: "none" }
19259
- }
19260
- )
19261
- }
19262
- ) }),
19263
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: itemEditOn ? "Done editing this image" : "Edit this image" })
19264
- ] }),
19265
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
19266
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: gatedLabel("Change image"), onClick: onChangeImage, disabled: !itemEditOn, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Image, { size: 16, style: { pointerEvents: "none" } }) }),
19267
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: gatedLabel("Link"), onClick: onOpenLink, disabled: !itemEditOn, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Link, { size: 16, style: { pointerEvents: "none" } }) }),
19268
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: gatedLabel("Move left"), onClick: onMoveLeft, disabled: !itemEditOn || index <= 0, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.ArrowLeft, { size: 16, style: { pointerEvents: "none" } }) }),
19269
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19270
- ToolbarAction,
19271
- {
19272
- label: gatedLabel("Move right"),
19273
- onClick: onMoveRight,
19274
- disabled: !itemEditOn || index >= itemCount - 1,
19275
- children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.ArrowRight, { size: 16, style: { pointerEvents: "none" } })
19276
- }
19277
- ),
19278
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: gatedLabel("Remove image"), onClick: onRemoveItem, disabled: !itemEditOn || itemCount <= 1, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.ImageMinus, { size: 16, style: { pointerEvents: "none" } }) }),
19279
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
19280
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(OptionMenu, { label: "Layout", value: variant, options: VARIANTS, onChange: onSetVariant }),
19281
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(OptionMenu, { label: "Hover", value: hover, options: HOVERS, onChange: onSetHover }),
19282
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19283
- ToolbarAction,
19284
- {
19285
- label: treatment === "grayscale" ? "Show in color" : "Show grayscale",
19286
- active: treatment === "grayscale",
19287
- onClick: () => onSetTreatment(treatment === "grayscale" ? "color" : "grayscale"),
19288
- children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Palette, { size: 16, style: { pointerEvents: "none" } })
19289
- }
19290
- ),
19291
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
19292
- ALIGNS.map(({ value, label, Icon }) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label, active: align === value, onClick: () => onSetAlign(value), children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Icon, { size: 16, style: { pointerEvents: "none" } }) }, value)),
19293
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
19294
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: "Smaller", onClick: () => onResize(-8), children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Minus, { size: 16, style: { pointerEvents: "none" } }) }),
19295
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("span", { className: "min-w-[40px] text-center text-xs tabular-nums text-muted-foreground", children: [
19296
- sizeValue,
19297
- "px"
19298
- ] }),
19299
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: "Larger", onClick: () => onResize(8), children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Plus, { size: 16, style: { pointerEvents: "none" } }) }),
19300
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
19301
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: "Add image", onClick: onAddItem, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.ImagePlus, { size: 16, style: { pointerEvents: "none" } }) })
19302
- ]
19303
- }
19304
- ),
19305
- error && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "max-w-[260px] rounded-md bg-background px-2 py-1 text-center text-[11px] text-destructive shadow-sm", children: error })
19093
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: rowStyle, children: [
19094
+ VARIANTS.map((v) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: v.label, active: variant === v.value, onClick: () => onSetVariant(v.value) }, v.value)),
19095
+ ALIGNS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: a.label, active: align === a.value, onClick: () => onSetAlign(a.value) }, a.value))
19096
+ ] }),
19097
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: rowStyle, children: [
19098
+ HOVERS.map((h) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: h.label, active: hover === h.value, onClick: () => onSetHover(h.value) }, h.value)),
19099
+ TREATMENTS.map((t) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: t.label, active: treatment === t.value, onClick: () => onSetTreatment(t.value) }, t.value))
19100
+ ] }),
19101
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { display: "flex", gap: 6, alignItems: "center" }, children: [
19102
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: "\u2212", onClick: () => onResize(-8) }),
19103
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { style: { color: "#ffffff", fontSize: 12, minWidth: 34, textAlign: "center" }, children: [
19104
+ sizeValue,
19105
+ "px"
19106
+ ] }),
19107
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: "+", onClick: () => onResize(8) })
19108
+ ] }),
19109
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { display: "flex", justifyContent: "center", width: "100%" }, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(AltInput, { value: currentAlt, onCommit: onSetAlt }) }),
19110
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { display: "flex", justifyContent: "center", width: "100%" }, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(LinkInput, { value: currentHref, onCommit: (href) => onSetLink(href, currentTarget) }) }),
19111
+ currentHref && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: rowStyle, children: [
19112
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: "New tab", active: currentTarget === "new-tab", onClick: () => onSetLink(currentHref, "new-tab") }),
19113
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: "Same tab", active: currentTarget === "same-tab", onClick: () => onSetLink(currentHref, "same-tab") })
19114
+ ] }),
19115
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: rowStyle, children: [
19116
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: "Change Image", onClick: onChangeImage }),
19117
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: "+ Item", onClick: onAddItem }),
19118
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: "\u2212 Item", onClick: onRemoveItem, disabled: itemCount <= 1 })
19119
+ ] }),
19120
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: rowStyle, children: [
19121
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: "\u2190 Move", onClick: onMoveLeft, disabled: index <= 0 }),
19122
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: "\u2192 Move", onClick: onMoveRight, disabled: index >= itemCount - 1 })
19123
+ ] }),
19124
+ error && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
19306
19125
  ]
19307
19126
  }
19308
- ) });
19127
+ );
19309
19128
  }
19310
19129
  function findMediaStripSlots(tree) {
19311
19130
  const stack = tree.rows.flatMap((row) => row.blocks);
@@ -19324,23 +19143,31 @@ function AiMediaStripOverlay({
19324
19143
  }) {
19325
19144
  const [hover, setHover] = (0, import_react10.useState)(null);
19326
19145
  const [active, setActive] = (0, import_react10.useState)(null);
19327
- const [itemEditOn, setItemEditOn] = (0, import_react10.useState)(false);
19328
- const [linkOpen, setLinkOpen] = (0, import_react10.useState)(false);
19329
19146
  const [pending, setPending] = (0, import_react10.useState)(false);
19330
19147
  const [error, setError] = (0, import_react10.useState)(null);
19331
19148
  const activeRef = (0, import_react10.useRef)(null);
19332
19149
  activeRef.current = active;
19333
- const linkOpenRef = (0, import_react10.useRef)(false);
19334
- linkOpenRef.current = linkOpen;
19335
19150
  (0, import_react10.useEffect)(() => {
19336
19151
  const onMove = (e) => {
19337
- const ref = itemRefFromTarget(e.target);
19338
19152
  const t = e.target;
19339
- const overEditable = Boolean(t?.closest?.("[data-ohw-editable]"));
19340
- setHover((prev) => {
19341
- const next = ref && !overEditable ? ref : null;
19342
- return sameRef(prev, next) ? prev : next;
19343
- });
19153
+ if (!(t instanceof HTMLElement)) return;
19154
+ 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]")) {
19155
+ return;
19156
+ }
19157
+ const itemEl = t.closest("[data-ohw-media-strip-item]");
19158
+ if (!itemEl) {
19159
+ setHover(null);
19160
+ return;
19161
+ }
19162
+ const sectionEl = itemEl.closest("[data-ohw-section]");
19163
+ const unit = sectionEl ? movableUnit(sectionEl) : null;
19164
+ const sectionId = unit ? instanceIdOf(unit) || null : null;
19165
+ const index = Number(itemEl.dataset.ohwMediaStripIndex);
19166
+ if (!sectionId || Number.isNaN(index)) {
19167
+ setHover(null);
19168
+ return;
19169
+ }
19170
+ setHover({ sectionId, index });
19344
19171
  };
19345
19172
  const onLeave = () => setHover(null);
19346
19173
  document.addEventListener("mousemove", onMove, { passive: true });
@@ -19352,25 +19179,23 @@ function AiMediaStripOverlay({
19352
19179
  }, []);
19353
19180
  (0, import_react10.useEffect)(() => {
19354
19181
  const onClick = (e) => {
19182
+ const current = activeRef.current;
19183
+ if (!current) return;
19355
19184
  const t = e.target;
19356
- if (!(t instanceof Element)) return;
19357
- if (t.closest(CHROME_SELECTOR)) return;
19358
- const ref = itemRefFromTarget(t);
19359
- if (sameRef(activeRef.current, ref)) return;
19360
- if (ref) clearInteractionChromeRef.current();
19361
- setItemEditOn(false);
19185
+ if (!(t instanceof HTMLElement)) return;
19186
+ if (t.closest("[data-ohw-item-interaction]")) return;
19187
+ const itemEl = findMediaStripItemElement(current.sectionId, current.index);
19188
+ if (itemEl && itemEl.contains(t)) return;
19189
+ setActive(null);
19362
19190
  setError(null);
19363
- setActive(ref);
19364
19191
  };
19365
19192
  document.addEventListener("click", onClick, true);
19366
19193
  return () => document.removeEventListener("click", onClick, true);
19367
- }, [clearInteractionChromeRef]);
19194
+ }, []);
19368
19195
  (0, import_react10.useEffect)(() => {
19369
19196
  const onBlur = () => {
19370
19197
  if (!activeRef.current) return;
19371
19198
  setActive(null);
19372
- setItemEditOn(false);
19373
- setLinkOpen(false);
19374
19199
  setError(null);
19375
19200
  };
19376
19201
  window.addEventListener("blur", onBlur);
@@ -19380,10 +19205,7 @@ function AiMediaStripOverlay({
19380
19205
  const onKeyDown = (e) => {
19381
19206
  if (e.key !== "Escape") return;
19382
19207
  if (!activeRef.current) return;
19383
- if (linkOpenRef.current) return;
19384
- if (document.querySelector('[data-ohw-more-menu][data-state="open"]')) return;
19385
19208
  setActive(null);
19386
- setItemEditOn(false);
19387
19209
  setError(null);
19388
19210
  };
19389
19211
  document.addEventListener("keydown", onKeyDown);
@@ -19417,133 +19239,137 @@ function AiMediaStripOverlay({
19417
19239
  tree: nextTree,
19418
19240
  label: entry.label,
19419
19241
  media: extraMedia ? { ...entry.media, ...extraMedia } : entry.media,
19420
- ...entry.brand ? { brand: entry.brand } : {},
19421
- // The user is already looking at the strip they just edited — recentering the viewport
19422
- // on every edit reads as the section jumping around.
19423
- skipScrollIntoView: true
19242
+ ...entry.brand ? { brand: entry.brand } : {}
19424
19243
  });
19425
19244
  return true;
19426
19245
  },
19427
19246
  [findEntry, postToParent2, applyTreeLocalRef]
19428
19247
  );
19429
19248
  const activeEntry = active ? findEntry(active.sectionId) : null;
19249
+ const hoverHandleRect = useLiveItemRect(!active && hover ? hover : null);
19430
19250
  const activeItemRect = useLiveItemRect(active);
19431
- const activeStripRect = useLiveItemRect(active, true);
19432
- const hoverRect = useLiveItemRect(hover && !sameRef(hover, active) ? hover : null);
19433
- const activeItems = activeEntry ? mediaStripItems(activeEntry.tree) : [];
19434
- const activeItem = active ? activeItems[active.index] : void 0;
19435
- const activeHref = activeItem?.href ?? "";
19436
- const activeTarget = activeItem?.linkTarget === "same-tab" ? "same-tab" : "new-tab";
19437
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
19438
- hoverRect && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ItemInteractionLayer, { rect: hoverRect, state: "hover" }),
19439
- activeItemRect && activeStripRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
19440
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ItemInteractionLayer, { rect: activeItemRect, state: "active-top" }),
19441
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(StripToolbarSlot, { stripRect: activeStripRect, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19442
- MediaStripToolbar,
19443
- {
19444
- entry: activeEntry,
19445
- index: active.index,
19446
- pending,
19447
- error,
19448
- itemEditOn,
19449
- onToggleItemEdit: () => setItemEditOn((on) => !on),
19450
- onSetVariant: (variant) => runMutation(active.sectionId, (tree) => setMediaStripVariant(tree, variant)),
19451
- onSetAlign: (align) => runMutation(active.sectionId, (tree) => setMediaStripAlign(tree, align)),
19452
- onSetHover: (hoverPreset) => runMutation(active.sectionId, (tree) => setMediaStripHover(tree, hoverPreset)),
19453
- onSetTreatment: (treatment) => runMutation(active.sectionId, (tree) => setMediaStripTreatment(tree, treatment)),
19454
- onResize: (delta) => {
19455
- const slots = findMediaStripSlots(activeEntry.tree);
19456
- const variant = slots?.variant === "reel" ? "reel" : "logos";
19457
- const current = variant === "logos" ? typeof slots?.logoMaxHeight === "number" ? slots.logoMaxHeight : 48 : typeof slots?.cardWidth === "number" ? slots.cardWidth : 240;
19458
- void runMutation(active.sectionId, (tree) => setMediaStripSizing(tree, current + delta));
19459
- },
19460
- onOpenLink: () => setLinkOpen(true),
19461
- onAddItem: () => {
19462
- void requestAssetPick(postToParent2).then((picked) => {
19463
- if (!picked) return;
19464
- const item = { assetId: picked.assetId, alt: "Logo" };
19251
+ const TOOLBAR_APPROX_H = 170;
19252
+ const toolbarState = activeItemRect && activeItemRect.top < TOOLBAR_APPROX_H ? "active-bottom" : "active-top";
19253
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
19254
+ hoverHandleRect && hover && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
19255
+ "button",
19256
+ {
19257
+ type: "button",
19258
+ "data-ohw-toolbar": "",
19259
+ onClick: () => {
19260
+ clearInteractionChromeRef.current();
19261
+ setActive(hover);
19262
+ },
19263
+ "aria-label": "Configure media item",
19264
+ style: {
19265
+ position: "fixed",
19266
+ top: hoverHandleRect.top + 8,
19267
+ left: hoverHandleRect.left + hoverHandleRect.width - 36,
19268
+ width: 28,
19269
+ height: 28,
19270
+ borderRadius: 8,
19271
+ border: `1.5px dashed ${PRIMARY3}`,
19272
+ background: "#ffffff",
19273
+ color: PRIMARY3,
19274
+ display: "flex",
19275
+ alignItems: "center",
19276
+ justifyContent: "center",
19277
+ cursor: "pointer",
19278
+ zIndex: 2147483643,
19279
+ fontSize: 14,
19280
+ fontWeight: 700
19281
+ },
19282
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react8.Star, { size: 14, style: { pointerEvents: "none" } })
19283
+ }
19284
+ ),
19285
+ activeItemRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
19286
+ ItemInteractionLayer,
19287
+ {
19288
+ rect: activeItemRect,
19289
+ state: toolbarState,
19290
+ toolbar: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
19291
+ MediaStripToolbar,
19292
+ {
19293
+ entry: activeEntry,
19294
+ index: active.index,
19295
+ pending,
19296
+ error,
19297
+ onSetVariant: (variant) => runMutation(active.sectionId, (tree) => setMediaStripVariant(tree, variant)),
19298
+ onSetAlign: (align) => runMutation(active.sectionId, (tree) => setMediaStripAlign(tree, align)),
19299
+ onSetHover: (hoverPreset) => runMutation(active.sectionId, (tree) => setMediaStripHover(tree, hoverPreset)),
19300
+ onSetTreatment: (treatment) => runMutation(active.sectionId, (tree) => setMediaStripTreatment(tree, treatment)),
19301
+ onResize: (delta) => {
19302
+ const slots = findMediaStripSlots(activeEntry.tree);
19303
+ const variant = slots?.variant === "reel" ? "reel" : "logos";
19304
+ const current = variant === "logos" ? typeof slots?.logoMaxHeight === "number" ? slots.logoMaxHeight : 48 : typeof slots?.cardWidth === "number" ? slots.cardWidth : 240;
19305
+ void runMutation(active.sectionId, (tree) => setMediaStripSizing(tree, current + delta));
19306
+ },
19307
+ onSetAlt: (alt) => {
19308
+ void runMutation(active.sectionId, (tree) => setMediaStripItemAlt(tree, active.index, alt));
19309
+ },
19310
+ onSetLink: (href, linkTarget) => {
19465
19311
  void runMutation(
19466
19312
  active.sectionId,
19467
- (tree) => addMediaStripItem(tree, item, active.index),
19468
- { [picked.assetId]: picked.url }
19469
- ).then((applied) => {
19470
- if (applied) setActive({ sectionId: active.sectionId, index: active.index + 1 });
19313
+ (tree) => setMediaStripItemLink(tree, active.index, href ? { href, linkTarget } : {})
19314
+ );
19315
+ },
19316
+ onAddItem: () => {
19317
+ void requestAssetPick(postToParent2).then((picked) => {
19318
+ if (!picked) return;
19319
+ const item = { assetId: picked.assetId, alt: "Logo" };
19320
+ void runMutation(
19321
+ active.sectionId,
19322
+ (tree) => addMediaStripItem(tree, item, active.index),
19323
+ { [picked.assetId]: picked.url }
19324
+ ).then((applied) => {
19325
+ if (applied) setActive({ sectionId: active.sectionId, index: active.index + 1 });
19326
+ });
19471
19327
  });
19472
- });
19473
- },
19474
- onChangeImage: () => {
19475
- void requestAssetPick(postToParent2).then((picked) => {
19476
- if (!picked) return;
19477
- void runMutation(
19478
- active.sectionId,
19479
- (tree) => setMediaStripItemAsset(tree, active.index, picked.assetId),
19480
- { [picked.assetId]: picked.url }
19328
+ },
19329
+ onChangeImage: () => {
19330
+ void requestAssetPick(postToParent2).then((picked) => {
19331
+ if (!picked) return;
19332
+ void runMutation(
19333
+ active.sectionId,
19334
+ (tree) => setMediaStripItemAsset(tree, active.index, picked.assetId),
19335
+ { [picked.assetId]: picked.url }
19336
+ );
19337
+ });
19338
+ },
19339
+ onRemoveItem: () => {
19340
+ void runMutation(active.sectionId, (tree) => removeMediaStripItem(tree, active.index)).then(
19341
+ (applied) => {
19342
+ if (applied) setActive(null);
19343
+ }
19481
19344
  );
19482
- });
19483
- },
19484
- onRemoveItem: () => {
19485
- void runMutation(active.sectionId, (tree) => removeMediaStripItem(tree, active.index)).then(
19486
- (applied) => {
19487
- if (applied) setActive(null);
19488
- }
19489
- );
19490
- },
19491
- onMoveLeft: () => {
19492
- void runMutation(active.sectionId, (tree) => reorderMediaStripItem(tree, active.index, -1)).then(
19493
- (applied) => {
19494
- if (applied) setActive({ sectionId: active.sectionId, index: active.index - 1 });
19495
- }
19496
- );
19497
- },
19498
- onMoveRight: () => {
19499
- void runMutation(active.sectionId, (tree) => reorderMediaStripItem(tree, active.index, 1)).then(
19500
- (applied) => {
19501
- if (applied) setActive({ sectionId: active.sectionId, index: active.index + 1 });
19502
- }
19503
- );
19504
- }
19505
- }
19506
- ) }),
19507
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19508
- "div",
19509
- {
19510
- "data-ohw-bridge": "",
19511
- "data-ohw-item-interaction": "",
19512
- "aria-hidden": true,
19513
- className: "pointer-events-none fixed rounded-[4px] px-[7px] py-[3px] font-sans text-[11px] font-medium leading-none text-primary-foreground whitespace-nowrap",
19514
- style: {
19515
- top: activeItemRect.top + activeItemRect.height + 10,
19516
- left: activeItemRect.left - 5,
19517
- background: PRIMARY3,
19518
- zIndex: 2147483644
19519
- },
19520
- children: "Media item"
19521
- }
19522
- ),
19523
- linkOpen && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19524
- LinkModal,
19525
- {
19526
- href: activeHref,
19527
- linkTarget: activeTarget,
19528
- onClose: () => setLinkOpen(false),
19529
- onSave: (href, linkTarget) => {
19530
- void runMutation(
19531
- active.sectionId,
19532
- (tree) => setMediaStripItemLink(tree, active.index, href ? { href, linkTarget } : {})
19533
- );
19345
+ },
19346
+ onMoveLeft: () => {
19347
+ void runMutation(active.sectionId, (tree) => reorderMediaStripItem(tree, active.index, -1)).then(
19348
+ (applied) => {
19349
+ if (applied) setActive({ sectionId: active.sectionId, index: active.index - 1 });
19350
+ }
19351
+ );
19352
+ },
19353
+ onMoveRight: () => {
19354
+ void runMutation(active.sectionId, (tree) => reorderMediaStripItem(tree, active.index, 1)).then(
19355
+ (applied) => {
19356
+ if (applied) setActive({ sectionId: active.sectionId, index: active.index + 1 });
19357
+ }
19358
+ );
19359
+ }
19534
19360
  }
19535
- }
19536
- )
19537
- ] })
19361
+ )
19362
+ }
19363
+ )
19538
19364
  ] });
19539
19365
  }
19540
19366
 
19541
19367
  // src/ui/ai-section/AiPricingCardOverlay.tsx
19542
19368
  var import_react11 = require("react");
19543
- var import_lucide_react10 = require("lucide-react");
19544
- var import_jsx_runtime22 = require("react/jsx-runtime");
19369
+ var import_lucide_react9 = require("lucide-react");
19370
+ var import_jsx_runtime21 = require("react/jsx-runtime");
19545
19371
  var PRIMARY4 = "var(--ohw-primary, #0885FE)";
19546
- 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]";
19372
+ 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]";
19547
19373
  function findPricingCardElement(sectionId, index) {
19548
19374
  const sectionEl = findByInstanceId(sectionId);
19549
19375
  if (!sectionEl) return null;
@@ -19555,7 +19381,7 @@ function findPricingCardElement(sectionId, index) {
19555
19381
  }
19556
19382
  function cardRefFromTarget(target) {
19557
19383
  if (!(target instanceof Element)) return null;
19558
- if (target.closest(CHROME_SELECTOR2)) return null;
19384
+ if (target.closest(CHROME_SELECTOR)) return null;
19559
19385
  const cardEl = target.closest("[data-ohw-pricing-card]");
19560
19386
  if (!cardEl) return null;
19561
19387
  const sectionEl = cardEl.closest("[data-ohw-section]");
@@ -19565,21 +19391,21 @@ function cardRefFromTarget(target) {
19565
19391
  if (!sectionId || Number.isNaN(index)) return null;
19566
19392
  return { sectionId, index };
19567
19393
  }
19568
- function sameRef2(a, b) {
19394
+ function sameRef(a, b) {
19569
19395
  return a?.sectionId === b?.sectionId && a?.index === b?.index;
19570
19396
  }
19571
- function rectOf2(el) {
19397
+ function rectOf(el) {
19572
19398
  if (!el) return null;
19573
19399
  const r2 = el.getBoundingClientRect();
19574
19400
  if (r2.width <= 0 || r2.height <= 0) return null;
19575
19401
  return { top: r2.top, left: r2.left, width: r2.width, height: r2.height };
19576
19402
  }
19577
19403
  function readRect3(ref) {
19578
- return rectOf2(ref && findPricingCardElement(ref.sectionId, ref.index));
19404
+ return rectOf(ref && findPricingCardElement(ref.sectionId, ref.index));
19579
19405
  }
19580
19406
  function readGridRect(ref) {
19581
19407
  const card = ref && findPricingCardElement(ref.sectionId, ref.index);
19582
- return rectOf2(card?.closest("[data-ohw-pricing-collection]") ?? card);
19408
+ return rectOf(card?.closest("[data-ohw-pricing-collection]") ?? card);
19583
19409
  }
19584
19410
  function useLiveCardRect(ref, grid = false) {
19585
19411
  const [rect, setRect] = (0, import_react11.useState)(null);
@@ -19609,7 +19435,7 @@ function useLiveCardRect(ref, grid = false) {
19609
19435
  }
19610
19436
  var TOOLBAR_GRID_GAP = 52;
19611
19437
  var TOOLBAR_GRID_GAP_MOBILE = 42;
19612
- var TOOLBAR_EDGE_MARGIN3 = 4;
19438
+ var TOOLBAR_EDGE_MARGIN2 = 4;
19613
19439
  function GridToolbarSlot({ gridRect, children }) {
19614
19440
  const slotRef = (0, import_react11.useRef)(null);
19615
19441
  const [shift, setShift] = (0, import_react11.useState)(0);
@@ -19621,7 +19447,7 @@ function GridToolbarSlot({ gridRect, children }) {
19621
19447
  const w = slot.offsetWidth;
19622
19448
  if (w <= 0) return;
19623
19449
  const half = w / 2;
19624
- const clamped = Math.max(TOOLBAR_EDGE_MARGIN3 + half, Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN3 - half));
19450
+ const clamped = Math.max(TOOLBAR_EDGE_MARGIN2 + half, Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN2 - half));
19625
19451
  setShift((prev) => Math.abs(prev - (clamped - centerX)) < 0.5 ? prev : clamped - centerX);
19626
19452
  };
19627
19453
  clamp();
@@ -19630,7 +19456,7 @@ function GridToolbarSlot({ gridRect, children }) {
19630
19456
  return () => ro.disconnect();
19631
19457
  }, [centerX, children]);
19632
19458
  const mobile = window.innerWidth <= 480;
19633
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19459
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19634
19460
  "div",
19635
19461
  {
19636
19462
  ref: slotRef,
@@ -19665,7 +19491,7 @@ function requestValidateTree2(postToParent2, tree) {
19665
19491
  });
19666
19492
  }
19667
19493
  function StyleSwatch({ style }) {
19668
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19494
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19669
19495
  "span",
19670
19496
  {
19671
19497
  "aria-hidden": true,
@@ -19679,35 +19505,35 @@ function StyleSwatch({ style }) {
19679
19505
  );
19680
19506
  }
19681
19507
  function ButtonGlyph() {
19682
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("svg", { width: "18", height: "12", viewBox: "0 0 18 12", fill: "none", "aria-hidden": true, style: { pointerEvents: "none" }, children: [
19683
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("rect", { x: "0.75", y: "0.75", width: "16.5", height: "10.5", rx: "3", stroke: "currentColor", strokeWidth: "1.5" }),
19684
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M5.5 6H12.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
19508
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("svg", { width: "18", height: "12", viewBox: "0 0 18 12", fill: "none", "aria-hidden": true, style: { pointerEvents: "none" }, children: [
19509
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("rect", { x: "0.75", y: "0.75", width: "16.5", height: "10.5", rx: "3", stroke: "currentColor", strokeWidth: "1.5" }),
19510
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("path", { d: "M5.5 6H12.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
19685
19511
  ] });
19686
19512
  }
19687
19513
  function FeatureGlyph({ mode }) {
19688
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("svg", { width: "18", height: "18", viewBox: "0 0 18 18", fill: "none", "aria-hidden": true, style: { pointerEvents: "none" }, children: [
19689
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("circle", { cx: "8", cy: "8", r: "7.25", stroke: "currentColor", strokeWidth: "1.5" }),
19690
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M5 8.2L7.2 10.3L11 6", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }),
19691
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("circle", { cx: "13.5", cy: "13.5", r: "4.25", fill: "var(--ohw-background, #ffffff)", stroke: "var(--ohw-background, #ffffff)", strokeWidth: "1" }),
19692
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("circle", { cx: "13.5", cy: "13.5", r: "3.75", fill: "var(--ohw-background, #ffffff)", stroke: PRIMARY4, strokeWidth: "1" }),
19693
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M11.5 13.5H15.5", stroke: PRIMARY4, strokeWidth: "1.5", strokeLinecap: "round" }),
19694
- mode === "add" && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M13.5 11.5V15.5", stroke: PRIMARY4, strokeWidth: "1.5", strokeLinecap: "round" })
19514
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("svg", { width: "18", height: "18", viewBox: "0 0 18 18", fill: "none", "aria-hidden": true, style: { pointerEvents: "none" }, children: [
19515
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("circle", { cx: "8", cy: "8", r: "7.25", stroke: "currentColor", strokeWidth: "1.5" }),
19516
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("path", { d: "M5 8.2L7.2 10.3L11 6", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }),
19517
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("circle", { cx: "13.5", cy: "13.5", r: "4.25", fill: "var(--ohw-background, #ffffff)", stroke: "var(--ohw-background, #ffffff)", strokeWidth: "1" }),
19518
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("circle", { cx: "13.5", cy: "13.5", r: "3.75", fill: "var(--ohw-background, #ffffff)", stroke: PRIMARY4, strokeWidth: "1" }),
19519
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("path", { d: "M11.5 13.5H15.5", stroke: PRIMARY4, strokeWidth: "1.5", strokeLinecap: "round" }),
19520
+ mode === "add" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("path", { d: "M13.5 11.5V15.5", stroke: PRIMARY4, strokeWidth: "1.5", strokeLinecap: "round" })
19695
19521
  ] });
19696
19522
  }
19697
- function ToolbarAction2({
19523
+ function ToolbarAction({
19698
19524
  label,
19699
19525
  onClick,
19700
19526
  disabled = false,
19701
19527
  children
19702
19528
  }) {
19703
- const button = /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(CustomToolbarButton, { type: "button", "aria-label": label, disabled, onClick, className: "size-8", children });
19704
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Tooltip, { children: [
19705
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipTrigger, { asChild: true, children: disabled ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "inline-flex", children: button }) : button }),
19706
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: label })
19529
+ const button = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(CustomToolbarButton, { type: "button", "aria-label": label, disabled, onClick, className: "size-8", children });
19530
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Tooltip, { children: [
19531
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipTrigger, { asChild: true, children: disabled ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "inline-flex", children: button }) : button }),
19532
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: label })
19707
19533
  ] });
19708
19534
  }
19709
- function ToolbarSeparator2() {
19710
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { "aria-hidden": true, className: "mx-0.5 block h-6 w-px shrink-0 bg-border" });
19535
+ function ToolbarSeparator() {
19536
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { "aria-hidden": true, className: "mx-0.5 block h-6 w-px shrink-0 bg-border" });
19711
19537
  }
19712
19538
  function CardStyleMenu({
19713
19539
  value,
@@ -19715,7 +19541,7 @@ function CardStyleMenu({
19715
19541
  disabled = false
19716
19542
  }) {
19717
19543
  const current = PRICING_CARD_STYLES.find((s) => s.value === value) ?? PRICING_CARD_STYLES[0];
19718
- const trigger = /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
19544
+ const trigger = /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
19719
19545
  "button",
19720
19546
  {
19721
19547
  type: "button",
@@ -19726,28 +19552,28 @@ function CardStyleMenu({
19726
19552
  disabled && "cursor-not-allowed opacity-40 hover:bg-transparent"
19727
19553
  ),
19728
19554
  children: [
19729
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(StyleSwatch, { style: current.value }),
19730
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "flex-1 text-left", children: current.label }),
19731
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react10.ChevronDown, { size: 16, style: { pointerEvents: "none" } })
19555
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(StyleSwatch, { style: current.value }),
19556
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "flex-1 text-left", children: current.label }),
19557
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.ChevronDown, { size: 16, style: { pointerEvents: "none" } })
19732
19558
  ]
19733
19559
  }
19734
19560
  );
19735
19561
  if (disabled) {
19736
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
19737
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "text-xs text-muted-foreground opacity-50", children: "Card style" }),
19738
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Tooltip, { children: [
19739
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "inline-flex", children: trigger }) }),
19740
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: "Mark as featured to edit card style" })
19562
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
19563
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-xs text-muted-foreground opacity-50", children: "Card style" }),
19564
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Tooltip, { children: [
19565
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "inline-flex", children: trigger }) }),
19566
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: "Mark as featured to edit card style" })
19741
19567
  ] })
19742
19568
  ] });
19743
19569
  }
19744
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
19745
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "text-xs text-muted-foreground", children: "Card style" }),
19746
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(DropdownMenu, { modal: false, children: [
19747
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(DropdownMenuTrigger, { asChild: true, children: trigger }),
19748
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(DropdownMenuContent, { align: "end", sideOffset: 8, className: "w-[152px] min-w-[152px] p-1", children: PRICING_CARD_STYLES.map((style) => {
19570
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
19571
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-xs text-muted-foreground", children: "Card style" }),
19572
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(DropdownMenu, { modal: false, children: [
19573
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DropdownMenuTrigger, { asChild: true, children: trigger }),
19574
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DropdownMenuContent, { align: "end", sideOffset: 8, className: "w-[152px] min-w-[152px] p-1", children: PRICING_CARD_STYLES.map((style) => {
19749
19575
  const selected = style.value === value;
19750
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
19576
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
19751
19577
  DropdownMenuItem,
19752
19578
  {
19753
19579
  role: "menuitemradio",
@@ -19755,9 +19581,9 @@ function CardStyleMenu({
19755
19581
  onSelect: () => onChange(style.value),
19756
19582
  className: cn("grid grid-cols-[18px_1fr_16px] gap-2 py-2 pl-2 pr-2 text-[13px]", selected && "bg-muted"),
19757
19583
  children: [
19758
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(StyleSwatch, { style: style.value }),
19759
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { children: style.label }),
19760
- selected && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react10.Check, { size: 16, className: "text-bridge-primary" })
19584
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(StyleSwatch, { style: style.value }),
19585
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { children: style.label }),
19586
+ selected && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Check, { size: 16, className: "text-bridge-primary" })
19761
19587
  ]
19762
19588
  },
19763
19589
  style.value
@@ -19787,7 +19613,7 @@ function PricingCardToolbar({
19787
19613
  const style = pricingCardStyle(entry.tree, index);
19788
19614
  const featuredTag = pricingCardFeaturedTag(entry.tree, index);
19789
19615
  const mobile = window.innerWidth <= 480;
19790
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
19616
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
19791
19617
  "div",
19792
19618
  {
19793
19619
  "data-ohw-bridge": "",
@@ -19799,7 +19625,7 @@ function PricingCardToolbar({
19799
19625
  ),
19800
19626
  style: { maxWidth: mobile ? "calc(100vw - 24px)" : void 0 },
19801
19627
  children: [
19802
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
19628
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
19803
19629
  "div",
19804
19630
  {
19805
19631
  className: cn(
@@ -19808,8 +19634,8 @@ function PricingCardToolbar({
19808
19634
  mobile ? "flex-wrap justify-center" : "flex-nowrap"
19809
19635
  ),
19810
19636
  children: [
19811
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Tooltip, { children: [
19812
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19637
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Tooltip, { children: [
19638
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19813
19639
  CustomToolbarButton,
19814
19640
  {
19815
19641
  type: "button",
@@ -19817,8 +19643,8 @@ function PricingCardToolbar({
19817
19643
  "aria-pressed": featuredTag,
19818
19644
  onClick: () => onSetFeaturedTag(!featuredTag),
19819
19645
  className: "group size-8",
19820
- children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19821
- import_lucide_react10.Star,
19646
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19647
+ import_lucide_react9.Star,
19822
19648
  {
19823
19649
  size: 16,
19824
19650
  fill: "currentColor",
@@ -19832,25 +19658,25 @@ function PricingCardToolbar({
19832
19658
  )
19833
19659
  }
19834
19660
  ) }),
19835
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: featuredTag ? "Unmark featured" : "Mark as featured" })
19661
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: featuredTag ? "Unmark featured" : "Mark as featured" })
19836
19662
  ] }),
19837
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarSeparator2, {}),
19838
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(CardStyleMenu, { value: style, onChange: onSetStyle, disabled: !featuredTag }),
19839
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarSeparator2, {}),
19840
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarAction2, { label: "Add button", onClick: onAddButton, disabled: buttonCount >= 2, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ButtonGlyph, {}) }),
19841
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarAction2, { label: "Remove button", onClick: onRemoveButton, disabled: buttonCount === 0, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("span", { className: "relative inline-flex", children: [
19842
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ButtonGlyph, {}),
19843
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react10.Minus, { size: 10, strokeWidth: 2.5, className: "absolute -right-1.5 -top-1.5 rounded-full bg-background text-bridge-primary" })
19663
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
19664
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(CardStyleMenu, { value: style, onChange: onSetStyle, disabled: !featuredTag }),
19665
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
19666
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: "Add button", onClick: onAddButton, disabled: buttonCount >= 2, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ButtonGlyph, {}) }),
19667
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: "Remove button", onClick: onRemoveButton, disabled: buttonCount === 0, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("span", { className: "relative inline-flex", children: [
19668
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ButtonGlyph, {}),
19669
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Minus, { size: 10, strokeWidth: 2.5, className: "absolute -right-1.5 -top-1.5 rounded-full bg-background text-bridge-primary" })
19844
19670
  ] }) }),
19845
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarAction2, { label: "Add feature", onClick: onAddFeature, disabled: featureCount >= 10, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(FeatureGlyph, { mode: "add" }) }),
19846
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarAction2, { label: "Remove feature", onClick: onRemoveFeature, disabled: featureCount <= 1, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(FeatureGlyph, { mode: "remove" }) }),
19847
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarSeparator2, {}),
19848
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarAction2, { label: "Add tier", onClick: onAddTier, disabled: collectionCount >= 4, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react10.Plus, { size: 16, style: { pointerEvents: "none" } }) }),
19849
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarAction2, { label: "Remove tier", onClick: onRemoveTier, disabled: collectionCount <= 1, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react10.Minus, { size: 16, style: { pointerEvents: "none" } }) })
19671
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: "Add feature", onClick: onAddFeature, disabled: featureCount >= 10, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(FeatureGlyph, { mode: "add" }) }),
19672
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: "Remove feature", onClick: onRemoveFeature, disabled: featureCount <= 1, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(FeatureGlyph, { mode: "remove" }) }),
19673
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
19674
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: "Add tier", onClick: onAddTier, disabled: collectionCount >= 4, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Plus, { size: 16, style: { pointerEvents: "none" } }) }),
19675
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: "Remove tier", onClick: onRemoveTier, disabled: collectionCount <= 1, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Minus, { size: 16, style: { pointerEvents: "none" } }) })
19850
19676
  ]
19851
19677
  }
19852
19678
  ),
19853
- error && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "max-w-[260px] rounded-md bg-background px-2 py-1 text-center text-[11px] text-destructive shadow-sm", children: error })
19679
+ error && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "max-w-[260px] rounded-md bg-background px-2 py-1 text-center text-[11px] text-destructive shadow-sm", children: error })
19854
19680
  ]
19855
19681
  }
19856
19682
  ) });
@@ -19884,7 +19710,7 @@ function AiPricingCardOverlay({
19884
19710
  const overEditable = Boolean(t?.closest?.("[data-ohw-editable], [data-ohw-role], a, button"));
19885
19711
  setHover((prev) => {
19886
19712
  const next = ref && !overEditable ? ref : null;
19887
- return sameRef2(prev, next) ? prev : next;
19713
+ return sameRef(prev, next) ? prev : next;
19888
19714
  });
19889
19715
  };
19890
19716
  const onLeave = () => setHover(null);
@@ -19899,10 +19725,10 @@ function AiPricingCardOverlay({
19899
19725
  const onClick = (e) => {
19900
19726
  const t = e.target;
19901
19727
  if (!(t instanceof Element)) return;
19902
- if (t.closest(CHROME_SELECTOR2)) return;
19728
+ if (t.closest(CHROME_SELECTOR)) return;
19903
19729
  const ref = cardRefFromTarget(t);
19904
- setActive((prev) => sameRef2(prev, ref) ? prev : ref);
19905
- if (!ref || !sameRef2(activeRef.current, ref)) setError(null);
19730
+ setActive((prev) => sameRef(prev, ref) ? prev : ref);
19731
+ if (!ref || !sameRef(activeRef.current, ref)) setError(null);
19906
19732
  };
19907
19733
  document.addEventListener("click", onClick, true);
19908
19734
  return () => document.removeEventListener("click", onClick, true);
@@ -19967,12 +19793,12 @@ function AiPricingCardOverlay({
19967
19793
  const activeEntry = active ? findEntry(active.sectionId) : null;
19968
19794
  const activeCardRect = useLiveCardRect(active);
19969
19795
  const activeGridRect = useLiveCardRect(active, true);
19970
- const hoverRect = useLiveCardRect(hover && !sameRef2(hover, active) ? hover : null);
19971
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
19972
- hoverRect && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ItemInteractionLayer, { rect: hoverRect, state: "hover" }),
19973
- activeCardRect && activeGridRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
19974
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ItemInteractionLayer, { rect: activeCardRect, state: "active-top" }),
19975
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(GridToolbarSlot, { gridRect: activeGridRect, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19796
+ const hoverRect = useLiveCardRect(hover && !sameRef(hover, active) ? hover : null);
19797
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
19798
+ hoverRect && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ItemInteractionLayer, { rect: hoverRect, state: "hover" }),
19799
+ activeCardRect && activeGridRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
19800
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ItemInteractionLayer, { rect: activeCardRect, state: "active-top" }),
19801
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(GridToolbarSlot, { gridRect: activeGridRect, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19976
19802
  PricingCardToolbar,
19977
19803
  {
19978
19804
  entry: activeEntry,
@@ -20000,7 +19826,7 @@ function AiPricingCardOverlay({
20000
19826
  onRemoveFeature: () => runMutation(active.sectionId, (tree) => removePricingCardFeature(tree, active.index))
20001
19827
  }
20002
19828
  ) }),
20003
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19829
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
20004
19830
  "div",
20005
19831
  {
20006
19832
  "data-ohw-bridge": "",
@@ -20022,7 +19848,7 @@ function AiPricingCardOverlay({
20022
19848
 
20023
19849
  // src/ui/ai-section/AiVideoOverlay.tsx
20024
19850
  var import_react12 = require("react");
20025
- var import_lucide_react11 = require("lucide-react");
19851
+ var import_lucide_react10 = require("lucide-react");
20026
19852
 
20027
19853
  // src/lib/ai-video-tree.ts
20028
19854
  function resolveVideoBlock(tree, path) {
@@ -20093,10 +19919,10 @@ function extractYouTubeId(input) {
20093
19919
  }
20094
19920
 
20095
19921
  // src/ui/ai-section/AiVideoOverlay.tsx
20096
- var import_jsx_runtime23 = require("react/jsx-runtime");
19922
+ var import_jsx_runtime22 = require("react/jsx-runtime");
20097
19923
  var PRIMARY5 = "#0885FE";
20098
- var OHW_BLUE = `var(--ohw-primary, ${PRIMARY5})`;
20099
- var OHW_BLUE_DARK = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY5}) 60%, #000000)`;
19924
+ var OHW_BLUE2 = `var(--ohw-primary, ${PRIMARY5})`;
19925
+ var OHW_BLUE_DARK2 = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY5}) 60%, #000000)`;
20100
19926
  function findVideoBlockElement(sectionId, path) {
20101
19927
  const sectionEl = findByInstanceId(sectionId);
20102
19928
  if (!sectionEl) return null;
@@ -20156,12 +19982,12 @@ function requestValidateTree3(postToParent2, tree) {
20156
19982
  postToParent2({ type: "ow:ai-validate-tree-request", id, tree });
20157
19983
  });
20158
19984
  }
20159
- function Pill({
19985
+ function Pill2({
20160
19986
  label,
20161
19987
  onClick,
20162
19988
  disabled
20163
19989
  }) {
20164
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
19990
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
20165
19991
  "button",
20166
19992
  {
20167
19993
  type: "button",
@@ -20177,7 +20003,7 @@ function Pill({
20177
20003
  border: "none",
20178
20004
  cursor: disabled ? "default" : "pointer",
20179
20005
  background: "#ffffff",
20180
- color: OHW_BLUE_DARK,
20006
+ color: OHW_BLUE_DARK2,
20181
20007
  opacity: disabled ? 0.4 : 1,
20182
20008
  fontSize: 12,
20183
20009
  lineHeight: "16px",
@@ -20199,7 +20025,7 @@ function VideoUrlInput({ value, onCommit }) {
20199
20025
  setDraft(value ? watchUrl(value) : "");
20200
20026
  setInvalid(false);
20201
20027
  }, [value]);
20202
- const commit2 = () => {
20028
+ const commit = () => {
20203
20029
  const next = draft.trim();
20204
20030
  if (!next) {
20205
20031
  setInvalid(false);
@@ -20220,8 +20046,8 @@ function VideoUrlInput({ value, onCommit }) {
20220
20046
  onCommit(id);
20221
20047
  }
20222
20048
  };
20223
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 4, width: "100%", maxWidth: 280 }, children: [
20224
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
20049
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 4, width: "100%", maxWidth: 280 }, children: [
20050
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
20225
20051
  "div",
20226
20052
  {
20227
20053
  style: {
@@ -20237,12 +20063,12 @@ function VideoUrlInput({ value, onCommit }) {
20237
20063
  boxShadow: invalid ? "inset 0 0 0 1.5px #EF4444" : "none"
20238
20064
  },
20239
20065
  children: [
20240
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20066
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
20241
20067
  "span",
20242
20068
  {
20243
20069
  style: {
20244
20070
  flex: "0 0 auto",
20245
- color: OHW_BLUE_DARK,
20071
+ color: OHW_BLUE_DARK2,
20246
20072
  fontSize: 10,
20247
20073
  lineHeight: "16px",
20248
20074
  fontWeight: 700,
@@ -20252,14 +20078,14 @@ function VideoUrlInput({ value, onCommit }) {
20252
20078
  children: "Video"
20253
20079
  }
20254
20080
  ),
20255
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20081
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
20256
20082
  "input",
20257
20083
  {
20258
20084
  type: "text",
20259
20085
  inputMode: "url",
20260
20086
  value: draft,
20261
20087
  onChange: (e) => handleChange(e.target.value),
20262
- onBlur: commit2,
20088
+ onBlur: commit,
20263
20089
  onKeyDown: (e) => {
20264
20090
  if (e.key === "Enter") e.currentTarget.blur();
20265
20091
  if (e.key === "Escape") {
@@ -20289,7 +20115,7 @@ function VideoUrlInput({ value, onCommit }) {
20289
20115
  ]
20290
20116
  }
20291
20117
  ),
20292
- invalid && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, textAlign: "center" }, children: "That doesn't look like a YouTube link \u2014 try pasting the video URL again." })
20118
+ invalid && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, textAlign: "center" }, children: "That doesn't look like a YouTube link \u2014 try pasting the video URL again." })
20293
20119
  ] });
20294
20120
  }
20295
20121
  function VideoToolbar({
@@ -20299,7 +20125,7 @@ function VideoToolbar({
20299
20125
  onSetVideoId,
20300
20126
  onRemove
20301
20127
  }) {
20302
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
20128
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
20303
20129
  "div",
20304
20130
  {
20305
20131
  "data-ohw-bridge": "",
@@ -20311,7 +20137,7 @@ function VideoToolbar({
20311
20137
  gap: 6,
20312
20138
  alignItems: "center",
20313
20139
  width: 280,
20314
- background: OHW_BLUE,
20140
+ background: OHW_BLUE2,
20315
20141
  padding: 8,
20316
20142
  borderRadius: 12,
20317
20143
  boxShadow: "0 8px 24px rgba(0,0,0,0.25)",
@@ -20319,9 +20145,9 @@ function VideoToolbar({
20319
20145
  opacity: pending ? 0.6 : 1
20320
20146
  },
20321
20147
  children: [
20322
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(VideoUrlInput, { value: videoId, onCommit: onSetVideoId }),
20323
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Pill, { label: "Remove video", onClick: onRemove, disabled: !videoId }),
20324
- error && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
20148
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(VideoUrlInput, { value: videoId, onCommit: onSetVideoId }),
20149
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Pill2, { label: "Remove video", onClick: onRemove, disabled: !videoId }),
20150
+ error && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
20325
20151
  ]
20326
20152
  }
20327
20153
  );
@@ -20441,8 +20267,8 @@ function AiVideoOverlay({
20441
20267
  const activeBlockRect = useLiveBlockRect(active);
20442
20268
  const TOOLBAR_APPROX_H = 90;
20443
20269
  const toolbarState = activeBlockRect && activeBlockRect.top < TOOLBAR_APPROX_H ? "active-bottom" : "active-top";
20444
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
20445
- hoverHandleRect && hover && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20270
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
20271
+ hoverHandleRect && hover && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
20446
20272
  "button",
20447
20273
  {
20448
20274
  type: "button",
@@ -20470,15 +20296,15 @@ function AiVideoOverlay({
20470
20296
  fontSize: 14,
20471
20297
  fontWeight: 700
20472
20298
  },
20473
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react11.Star, { size: 14, style: { pointerEvents: "none" } })
20299
+ children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react10.Star, { size: 14, style: { pointerEvents: "none" } })
20474
20300
  }
20475
20301
  ),
20476
- activeBlockRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20302
+ activeBlockRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
20477
20303
  ItemInteractionLayer,
20478
20304
  {
20479
20305
  rect: activeBlockRect,
20480
20306
  state: toolbarState,
20481
- toolbar: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20307
+ toolbar: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
20482
20308
  VideoToolbar,
20483
20309
  {
20484
20310
  videoId: videoIdAt(activeEntry.tree, active.path),
@@ -20502,7 +20328,7 @@ function AiVideoOverlay({
20502
20328
 
20503
20329
  // src/ui/ai-section/AiFadeCarouselOverlay.tsx
20504
20330
  var import_react13 = require("react");
20505
- var import_lucide_react12 = require("lucide-react");
20331
+ var import_lucide_react11 = require("lucide-react");
20506
20332
 
20507
20333
  // src/lib/ai-carousel-tree.ts
20508
20334
  function isCarouselBlock(node) {
@@ -20553,10 +20379,10 @@ function setCarouselAutoplay(tree, on) {
20553
20379
  }
20554
20380
 
20555
20381
  // src/ui/ai-section/AiFadeCarouselOverlay.tsx
20556
- var import_jsx_runtime24 = require("react/jsx-runtime");
20382
+ var import_jsx_runtime23 = require("react/jsx-runtime");
20557
20383
  var PRIMARY6 = "#0885FE";
20558
- var OHW_BLUE2 = `var(--ohw-primary, ${PRIMARY6})`;
20559
- var OHW_BLUE_DARK2 = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY6}) 60%, #000000)`;
20384
+ var OHW_BLUE3 = `var(--ohw-primary, ${PRIMARY6})`;
20385
+ var OHW_BLUE_DARK3 = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY6}) 60%, #000000)`;
20560
20386
  function findCarouselItemElement(sectionId, index) {
20561
20387
  const sectionEl = findByInstanceId(sectionId);
20562
20388
  if (!sectionEl) return null;
@@ -20635,8 +20461,8 @@ function requestAssetPick2(postToParent2) {
20635
20461
  postToParent2({ type: "ow:asset-pick", id });
20636
20462
  });
20637
20463
  }
20638
- function Pill2({ label, onClick, disabled }) {
20639
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
20464
+ function Pill3({ label, onClick, disabled }) {
20465
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20640
20466
  "button",
20641
20467
  {
20642
20468
  type: "button",
@@ -20652,7 +20478,7 @@ function Pill2({ label, onClick, disabled }) {
20652
20478
  border: "none",
20653
20479
  cursor: disabled ? "default" : "pointer",
20654
20480
  background: "#ffffff",
20655
- color: OHW_BLUE_DARK2,
20481
+ color: OHW_BLUE_DARK3,
20656
20482
  opacity: disabled ? 0.4 : 1,
20657
20483
  fontSize: 12,
20658
20484
  lineHeight: "16px",
@@ -20670,7 +20496,7 @@ function TogglePill({
20670
20496
  onToggle,
20671
20497
  disabled
20672
20498
  }) {
20673
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
20499
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
20674
20500
  "button",
20675
20501
  {
20676
20502
  type: "button",
@@ -20689,7 +20515,7 @@ function TogglePill({
20689
20515
  border: "none",
20690
20516
  cursor: disabled ? "default" : "pointer",
20691
20517
  background: "#ffffff",
20692
- color: OHW_BLUE_DARK2,
20518
+ color: OHW_BLUE_DARK3,
20693
20519
  opacity: disabled ? 0.4 : 1,
20694
20520
  fontSize: 12,
20695
20521
  lineHeight: "16px",
@@ -20699,7 +20525,7 @@ function TogglePill({
20699
20525
  },
20700
20526
  children: [
20701
20527
  label,
20702
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
20528
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20703
20529
  "span",
20704
20530
  {
20705
20531
  "aria-hidden": true,
@@ -20712,10 +20538,10 @@ function TogglePill({
20712
20538
  // Off is a grey track, not a washed-out blue: the pill's own white background already
20713
20539
  // carries the brand colour in the text, and a pale blue track next to it read as
20714
20540
  // "disabled control" rather than "switch that is off".
20715
- background: on ? OHW_BLUE_DARK2 : "#CBD5E1",
20541
+ background: on ? OHW_BLUE_DARK3 : "#CBD5E1",
20716
20542
  transition: "background 120ms ease"
20717
20543
  },
20718
- children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
20544
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20719
20545
  "span",
20720
20546
  {
20721
20547
  style: {
@@ -20744,7 +20570,7 @@ function CarouselSlideToolbar({
20744
20570
  onChangeImage,
20745
20571
  onToggleAutoplay
20746
20572
  }) {
20747
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
20573
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
20748
20574
  "div",
20749
20575
  {
20750
20576
  "data-ohw-bridge": "",
@@ -20755,7 +20581,7 @@ function CarouselSlideToolbar({
20755
20581
  flexDirection: "column",
20756
20582
  gap: 6,
20757
20583
  alignItems: "center",
20758
- background: OHW_BLUE2,
20584
+ background: OHW_BLUE3,
20759
20585
  padding: 8,
20760
20586
  borderRadius: 12,
20761
20587
  boxShadow: "0 8px 24px rgba(0,0,0,0.25)",
@@ -20767,11 +20593,11 @@ function CarouselSlideToolbar({
20767
20593
  pointerEvents: pending ? "none" : "auto"
20768
20594
  },
20769
20595
  children: [
20770
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { display: "flex", gap: 6, alignItems: "center" }, children: [
20771
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Pill2, { label: "Change Image", onClick: onChangeImage }),
20772
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(TogglePill, { label: "Autoplay", on: autoplay, onToggle: onToggleAutoplay })
20596
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { style: { display: "flex", gap: 6, alignItems: "center" }, children: [
20597
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Pill3, { label: "Change Image", onClick: onChangeImage }),
20598
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(TogglePill, { label: "Autoplay", on: autoplay, onToggle: onToggleAutoplay })
20773
20599
  ] }),
20774
- error && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
20600
+ error && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
20775
20601
  ]
20776
20602
  }
20777
20603
  );
@@ -20911,8 +20737,8 @@ function AiFadeCarouselOverlay({
20911
20737
  const activeItemRect = useLiveItemRect2(active);
20912
20738
  const TOOLBAR_APPROX_H = 90;
20913
20739
  const toolbarState = activeItemRect && activeItemRect.top < TOOLBAR_APPROX_H ? "active-bottom" : "active-top";
20914
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
20915
- hoverHandleRect && hover && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
20740
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
20741
+ hoverHandleRect && hover && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20916
20742
  "button",
20917
20743
  {
20918
20744
  type: "button",
@@ -20941,10 +20767,10 @@ function AiFadeCarouselOverlay({
20941
20767
  fontSize: 14,
20942
20768
  fontWeight: 700
20943
20769
  },
20944
- children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react12.Star, { size: 14, style: { pointerEvents: "none" } })
20770
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react11.Star, { size: 14, style: { pointerEvents: "none" } })
20945
20771
  }
20946
20772
  ),
20947
- swapping && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
20773
+ swapping && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20948
20774
  "div",
20949
20775
  {
20950
20776
  ref: skeletonRef,
@@ -20962,15 +20788,15 @@ function AiFadeCarouselOverlay({
20962
20788
  zIndex: 2147483646,
20963
20789
  pointerEvents: "none"
20964
20790
  },
20965
- children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("style", { children: MEDIA_SKELETON_CSS })
20791
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("style", { children: MEDIA_SKELETON_CSS })
20966
20792
  }
20967
20793
  ),
20968
- activeItemRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
20794
+ activeItemRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20969
20795
  ItemInteractionLayer,
20970
20796
  {
20971
20797
  rect: activeItemRect,
20972
20798
  state: toolbarState,
20973
- toolbar: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
20799
+ toolbar: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20974
20800
  CarouselSlideToolbar,
20975
20801
  {
20976
20802
  pending,
@@ -21188,6 +21014,113 @@ function scrollToHashSectionWhenReady(behavior = "smooth") {
21188
21014
  // src/ui/link-modal/LinkPopover.tsx
21189
21015
  var import_react17 = require("react");
21190
21016
 
21017
+ // src/ui/dialog.tsx
21018
+ var React9 = __toESM(require("react"), 1);
21019
+ var import_radix_ui6 = require("radix-ui");
21020
+ var import_lucide_react12 = require("lucide-react");
21021
+ var import_jsx_runtime24 = require("react/jsx-runtime");
21022
+ function Dialog2({
21023
+ ...props
21024
+ }) {
21025
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_radix_ui6.Dialog.Root, { "data-slot": "dialog", ...props });
21026
+ }
21027
+ function DialogPortal({
21028
+ ...props
21029
+ }) {
21030
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_radix_ui6.Dialog.Portal, { ...props });
21031
+ }
21032
+ function DialogOverlay({
21033
+ className,
21034
+ ...props
21035
+ }) {
21036
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
21037
+ import_radix_ui6.Dialog.Overlay,
21038
+ {
21039
+ "data-slot": "dialog-overlay",
21040
+ "data-ohw-link-modal-root": "",
21041
+ className: cn("fixed inset-0 z-[2147483646] bg-black/50", className),
21042
+ ...props
21043
+ }
21044
+ );
21045
+ }
21046
+ var DialogContent = React9.forwardRef(
21047
+ ({ className, children, showCloseButton = true, container, ...props }, ref) => {
21048
+ const positionMode = container ? "absolute" : "fixed";
21049
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(DialogPortal, { container: container ?? void 0, children: [
21050
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(DialogOverlay, { className: cn(positionMode, "inset-0") }),
21051
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
21052
+ import_radix_ui6.Dialog.Content,
21053
+ {
21054
+ ref,
21055
+ "data-slot": "dialog-content",
21056
+ className: cn(
21057
+ positionMode,
21058
+ "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",
21059
+ "rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
21060
+ container ? "max-h-[calc(100%-32px)]" : "max-h-[calc(100vh-32px)]",
21061
+ className
21062
+ ),
21063
+ onMouseDown: (e) => e.stopPropagation(),
21064
+ ...props,
21065
+ children: [
21066
+ children,
21067
+ showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
21068
+ import_radix_ui6.Dialog.Close,
21069
+ {
21070
+ type: "button",
21071
+ className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
21072
+ "aria-label": "Close",
21073
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react12.X, { size: 16, "aria-hidden": true })
21074
+ }
21075
+ ) : null
21076
+ ]
21077
+ }
21078
+ )
21079
+ ] });
21080
+ }
21081
+ );
21082
+ DialogContent.displayName = import_radix_ui6.Dialog.Content.displayName;
21083
+ function DialogHeader({
21084
+ className,
21085
+ ...props
21086
+ }) {
21087
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: cn("flex flex-col gap-1.5", className), ...props });
21088
+ }
21089
+ function DialogFooter({
21090
+ className,
21091
+ ...props
21092
+ }) {
21093
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
21094
+ "div",
21095
+ {
21096
+ className: cn("flex items-center justify-end gap-2", className),
21097
+ ...props
21098
+ }
21099
+ );
21100
+ }
21101
+ var DialogTitle = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
21102
+ import_radix_ui6.Dialog.Title,
21103
+ {
21104
+ ref,
21105
+ className: cn(
21106
+ "text-lg font-semibold leading-none tracking-tight text-card-foreground",
21107
+ className
21108
+ ),
21109
+ ...props
21110
+ }
21111
+ ));
21112
+ DialogTitle.displayName = import_radix_ui6.Dialog.Title.displayName;
21113
+ var DialogDescription = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
21114
+ import_radix_ui6.Dialog.Description,
21115
+ {
21116
+ ref,
21117
+ className: cn("text-sm text-muted-foreground", className),
21118
+ ...props
21119
+ }
21120
+ ));
21121
+ DialogDescription.displayName = import_radix_ui6.Dialog.Description.displayName;
21122
+ var DialogClose = import_radix_ui6.Dialog.Close;
21123
+
21191
21124
  // src/ui/link-modal/LinkEditorPanel.tsx
21192
21125
  var import_lucide_react16 = require("lucide-react");
21193
21126
 
@@ -22291,7 +22224,6 @@ function shouldUseDevFixtures() {
22291
22224
  function getLogoElement(el) {
22292
22225
  const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
22293
22226
  if (marked) return marked;
22294
- if (el.closest('[data-ohw-editable="icon"]')) return null;
22295
22227
  const root = el.closest("nav, [data-ohw-nav-root], footer");
22296
22228
  if (!root) return null;
22297
22229
  const anchor = el.closest("a");
@@ -23986,102 +23918,6 @@ function deleteSelectedNavFooterItem(deps) {
23986
23918
  return false;
23987
23919
  }
23988
23920
 
23989
- // src/lib/nav-page-sync.ts
23990
- function labelKeyFor(hrefKey) {
23991
- return hrefKey.replace(/-href$/, "-label");
23992
- }
23993
- function isPageLinkKey(key) {
23994
- return isNavbarHrefKey(key) || isFooterHrefKey(key);
23995
- }
23996
- function currentHref(key, anchor, content) {
23997
- const stored = content[key];
23998
- if (typeof stored === "string" && stored.length > 0) return stored;
23999
- return anchor.getAttribute("href") ?? "";
24000
- }
24001
- function normalizePath2(value) {
24002
- if (value.length > 1 && value.endsWith("/")) return value.slice(0, -1);
24003
- return value;
24004
- }
24005
- function pointsAt(href, path) {
24006
- const normalized = normalizePath2(href);
24007
- const target = normalizePath2(path);
24008
- if (normalized === target) return true;
24009
- return (normalized.startsWith(`${target}#`) || normalized.startsWith(`${target}?`)) && target !== "/";
24010
- }
24011
- function retargetHref(href, oldPath, newPath) {
24012
- const normalized = normalizePath2(href);
24013
- const target = normalizePath2(oldPath);
24014
- if (normalized === target) return newPath;
24015
- return `${newPath}${normalized.slice(target.length)}`;
24016
- }
24017
- function setLabelText(key, text) {
24018
- document.querySelectorAll(`[data-ohw-key="${CSS.escape(key)}"]`).forEach((el) => {
24019
- el.textContent = text;
24020
- });
24021
- }
24022
- function commit(delta, deps) {
24023
- if (Object.keys(delta).length === 0) return;
24024
- deps.setEditContent({ ...deps.getEditContent(), ...delta });
24025
- deps.postToParent({
24026
- type: "ow:change",
24027
- nodes: Object.entries(delta).map(([key, text]) => ({ key, text }))
24028
- });
24029
- }
24030
- function navLinksTo(path, content) {
24031
- return Array.from(document.querySelectorAll("[data-ohw-href-key]")).filter((anchor) => {
24032
- const key = anchor.getAttribute("data-ohw-href-key");
24033
- return isNavbarHrefKey(key) && pointsAt(currentHref(key, anchor, content), path);
24034
- }).map((anchor) => anchor.getAttribute("data-ohw-href-key"));
24035
- }
24036
- function addNavLinkForPage(page, deps) {
24037
- const content = deps.getEditContent();
24038
- if (navLinksTo(page.path, content).length > 0) return false;
24039
- const roots = listNavbarRootItems();
24040
- const afterAnchor = roots[roots.length - 1] ?? null;
24041
- let inserted;
24042
- try {
24043
- inserted = insertNavbarItem(page.path, page.title, afterAnchor);
24044
- } catch {
24045
- return false;
24046
- }
24047
- const { hrefKey, labelKey, index } = inserted;
24048
- deps.applyLinkByKey(hrefKey, page.path);
24049
- setLabelText(labelKey, page.title);
24050
- commit(
24051
- {
24052
- [hrefKey]: page.path,
24053
- [labelKey]: page.title,
24054
- [NAV_COUNT_KEY]: String(index + 1),
24055
- [NAV_ORDER_KEY]: getNavbarOrderJson()
24056
- },
24057
- deps
24058
- );
24059
- return true;
24060
- }
24061
- function retargetPageLinks(change, deps) {
24062
- const { oldPath, newPath, oldTitle, newTitle } = change;
24063
- if (!oldPath || !newPath || normalizePath2(oldPath) === normalizePath2(newPath)) return 0;
24064
- const content = deps.getEditContent();
24065
- const delta = {};
24066
- for (const anchor of Array.from(document.querySelectorAll("[data-ohw-href-key]"))) {
24067
- const hrefKey = anchor.getAttribute("data-ohw-href-key");
24068
- if (!isPageLinkKey(hrefKey)) continue;
24069
- const href = currentHref(hrefKey, anchor, content);
24070
- if (!pointsAt(href, oldPath)) continue;
24071
- const nextHref = retargetHref(href, oldPath, newPath);
24072
- deps.applyLinkByKey(hrefKey, nextHref);
24073
- delta[hrefKey] = nextHref;
24074
- if (!oldTitle || !newTitle) continue;
24075
- const labelKey = labelKeyFor(hrefKey);
24076
- const label = content[labelKey] ?? anchor.querySelector('[data-ohw-editable="text"]')?.textContent ?? "";
24077
- if (label.trim() !== oldTitle) continue;
24078
- setLabelText(labelKey, newTitle);
24079
- delta[labelKey] = newTitle;
24080
- }
24081
- commit(delta, deps);
24082
- return Object.keys(delta).filter((key) => key.endsWith("-href")).length;
24083
- }
24084
-
24085
23921
  // src/ui/navbar-container-chrome.tsx
24086
23922
  var import_lucide_react21 = require("lucide-react");
24087
23923
  var import_jsx_runtime37 = require("react/jsx-runtime");
@@ -24969,7 +24805,7 @@ function getNavigationSelectionParent(el) {
24969
24805
  if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
24970
24806
  return resolveFooterLinksContainerFrom(el);
24971
24807
  }
24972
- 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)) {
24808
+ if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
24973
24809
  return getNavigationRoot(el);
24974
24810
  }
24975
24811
  return null;
@@ -25191,6 +25027,7 @@ var ICONS = {
25191
25027
  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"/>',
25192
25028
  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"/>'
25193
25029
  };
25030
+ var HEIGHT_SETTLE_DELAYS = [150, 400, 800];
25194
25031
  var SELECTION_CHROME_GAP2 = 4;
25195
25032
  var TOOLBAR_STROKE_GAP2 = 4;
25196
25033
  var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
@@ -25288,7 +25125,7 @@ function applyVisibleViewport(el, parentScroll) {
25288
25125
  el.style.top = `${top}px`;
25289
25126
  el.style.height = `${height}px`;
25290
25127
  }
25291
- var TOOLBAR_EDGE_MARGIN4 = 4;
25128
+ var TOOLBAR_EDGE_MARGIN3 = 4;
25292
25129
  function clampToolbarToClip(top, transform, rect, clip, approxH, gap) {
25293
25130
  const isAbove = transform.includes("translateY(-100%)");
25294
25131
  let visualTop = isAbove ? top - approxH : top;
@@ -25315,8 +25152,8 @@ function clampToolbarToClip(top, transform, rect, clip, approxH, gap) {
25315
25152
  function clampToolbarLeft(centerX, toolbarWidth) {
25316
25153
  const half = toolbarWidth / 2;
25317
25154
  return Math.max(
25318
- TOOLBAR_EDGE_MARGIN4 + half,
25319
- Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN4 - half)
25155
+ TOOLBAR_EDGE_MARGIN3 + half,
25156
+ Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN3 - half)
25320
25157
  );
25321
25158
  }
25322
25159
  function calcToolbarPos(rect, parentScroll, toolbarW = 306) {
@@ -26196,6 +26033,21 @@ function OhhwellsBridge() {
26196
26033
  const [isItemDragging, setIsItemDragging] = (0, import_react21.useState)(false);
26197
26034
  const [isFooterFrameSelection, setIsFooterFrameSelection] = (0, import_react21.useState)(false);
26198
26035
  isFooterFrameSelectionRef.current = isFooterFrameSelection;
26036
+ const [floatingPanel, setFloatingPanel] = (0, import_react21.useState)(null);
26037
+ const floatingPanelOpenRef = (0, import_react21.useRef)(false);
26038
+ floatingPanelOpenRef.current = floatingPanel !== null;
26039
+ const floatingPanelRef = (0, import_react21.useRef)(null);
26040
+ floatingPanelRef.current = floatingPanel;
26041
+ const [floatingPanelPos, setFloatingPanelPos] = (0, import_react21.useState)(null);
26042
+ const buttonCollectionDragRef = (0, import_react21.useRef)(null);
26043
+ const buttonDropInsertIndexRef = (0, import_react21.useRef)(null);
26044
+ const [buttonDropSlots, setButtonDropSlots] = (0, import_react21.useState)([]);
26045
+ const [activeButtonDropIndex, setActiveButtonDropIndex] = (0, import_react21.useState)(null);
26046
+ const buttonPointerDragRef = (0, import_react21.useRef)(null);
26047
+ const [buttonStyleTick, setButtonStyleTick] = (0, import_react21.useState)(0);
26048
+ const [logoSizeDraft, setLogoSizeDraft] = (0, import_react21.useState)(null);
26049
+ const [editorViewport, setEditorViewport] = (0, import_react21.useState)("desktop");
26050
+ const [parentScrollSnap, setParentScrollSnap] = (0, import_react21.useState)(null);
26199
26051
  const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = (0, import_react21.useState)(null);
26200
26052
  const [footerHeadingVisible, setFooterHeadingVisible] = (0, import_react21.useState)(null);
26201
26053
  const footerDragRef = (0, import_react21.useRef)(null);
@@ -26221,21 +26073,6 @@ function OhhwellsBridge() {
26221
26073
  const stylesRef = (0, import_react21.useRef)("");
26222
26074
  const appliedDrawerRef = (0, import_react21.useRef)(null);
26223
26075
  const pendingDeleteUndoRef = (0, import_react21.useRef)(null);
26224
- const [floatingPanel, setFloatingPanel] = (0, import_react21.useState)(null);
26225
- const floatingPanelOpenRef = (0, import_react21.useRef)(false);
26226
- const floatingPanelRef = (0, import_react21.useRef)(null);
26227
- floatingPanelRef.current = floatingPanel;
26228
- const setFloatingPanelRef = (0, import_react21.useRef)(setFloatingPanel);
26229
- const [floatingPanelPos, setFloatingPanelPos] = (0, import_react21.useState)(null);
26230
- const buttonCollectionDragRef = (0, import_react21.useRef)(null);
26231
- const buttonDropInsertIndexRef = (0, import_react21.useRef)(null);
26232
- const [buttonDropSlots, setButtonDropSlots] = (0, import_react21.useState)([]);
26233
- const [activeButtonDropIndex, setActiveButtonDropIndex] = (0, import_react21.useState)(null);
26234
- const buttonPointerDragRef = (0, import_react21.useRef)(null);
26235
- const [buttonStyleTick, setButtonStyleTick] = (0, import_react21.useState)(0);
26236
- const [logoSizeDraft, setLogoSizeDraft] = (0, import_react21.useState)(null);
26237
- const [editorViewport, setEditorViewport] = (0, import_react21.useState)("desktop");
26238
- const [parentScrollSnap, setParentScrollSnap] = (0, import_react21.useState)(null);
26239
26076
  const [sitePages, setSitePages] = (0, import_react21.useState)([]);
26240
26077
  const collectionItemsRef = (0, import_react21.useRef)(/* @__PURE__ */ new Map());
26241
26078
  const [sectionsByPath, setSectionsByPath] = (0, import_react21.useState)({});
@@ -26245,18 +26082,7 @@ function OhhwellsBridge() {
26245
26082
  const linkPopoverOpenRef = (0, import_react21.useRef)(false);
26246
26083
  const linkPopoverGraceUntilRef = (0, import_react21.useRef)(0);
26247
26084
  setLinkPopoverRef.current = setLinkPopover;
26248
- setFloatingPanelRef.current = setFloatingPanel;
26249
26085
  linkPopoverSessionRef.current = linkPopover;
26250
- floatingPanelOpenRef.current = Boolean(floatingPanel);
26251
- (0, import_react21.useEffect)(() => {
26252
- const syncViewport = () => {
26253
- const next = window.innerWidth <= 480 ? "mobile" : "desktop";
26254
- setEditorViewport((prev) => prev === next ? prev : next);
26255
- };
26256
- syncViewport();
26257
- window.addEventListener("resize", syncViewport);
26258
- return () => window.removeEventListener("resize", syncViewport);
26259
- }, []);
26260
26086
  (0, import_react21.useEffect)(() => {
26261
26087
  setSectionOrderPersistHandler((entries) => {
26262
26088
  const currentPath = window.location.pathname;
@@ -26405,43 +26231,6 @@ function OhhwellsBridge() {
26405
26231
  if (!useFixtures) postToParent2({ type: "ow:request-site-pages" });
26406
26232
  return () => window.removeEventListener("message", onSitePages);
26407
26233
  }, [isEditMode, postToParent2]);
26408
- (0, import_react21.useEffect)(() => {
26409
- if (!isEditMode) return;
26410
- const deps = {
26411
- getEditContent: () => editContentRef.current,
26412
- setEditContent: (next) => {
26413
- editContentRef.current = next;
26414
- },
26415
- applyLinkByKey,
26416
- postToParent: postToParent2
26417
- };
26418
- const onPageMessage = (e) => {
26419
- if (e.data?.type === "ow:page-created") {
26420
- const { path, title } = e.data;
26421
- if (typeof path !== "string" || !path) return;
26422
- if (addNavLinkForPage({ path, title: typeof title === "string" && title ? title : path }, deps)) {
26423
- enforceLinkHrefs();
26424
- }
26425
- return;
26426
- }
26427
- if (e.data?.type === "ow:page-renamed") {
26428
- const { oldPath, newPath, oldTitle, newTitle } = e.data;
26429
- if (typeof oldPath !== "string" || typeof newPath !== "string") return;
26430
- const moved = retargetPageLinks(
26431
- {
26432
- oldPath,
26433
- newPath,
26434
- oldTitle: typeof oldTitle === "string" ? oldTitle : void 0,
26435
- newTitle: typeof newTitle === "string" ? newTitle : void 0
26436
- },
26437
- deps
26438
- );
26439
- if (moved > 0) enforceLinkHrefs();
26440
- }
26441
- };
26442
- window.addEventListener("message", onPageMessage);
26443
- return () => window.removeEventListener("message", onPageMessage);
26444
- }, [isEditMode, postToParent2]);
26445
26234
  (0, import_react21.useEffect)(() => {
26446
26235
  if (!isEditMode || shouldUseDevFixtures()) return;
26447
26236
  void loadAllSectionsManifest().then((manifest) => {
@@ -27990,7 +27779,6 @@ function OhhwellsBridge() {
27990
27779
  applyAiFooterFromContent(content);
27991
27780
  if (typeof content[STYLE_STORE_KEY] === "string") {
27992
27781
  stylesRef.current = content[STYLE_STORE_KEY];
27993
- document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
27994
27782
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
27995
27783
  }
27996
27784
  applyBrandChrome(content);
@@ -28001,12 +27789,12 @@ function OhhwellsBridge() {
28001
27789
  if (key === "__ohw_sections") continue;
28002
27790
  if (key === AI_SECTIONS_KEY) continue;
28003
27791
  if (key === AI_FOOTER_KEY) continue;
28004
- if (key === LOGO_PLACEHOLDER_KEY) continue;
28005
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
28006
27792
  if (key === BRAND_KIT_KEY) continue;
28007
27793
  if (key === STYLE_STORE_KEY) continue;
28008
27794
  if (BRAND_CHROME_KEYS.has(key)) continue;
27795
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
28009
27796
  if (key === CUSTOM_CODE_AFTER_HEAD_KEY || key === CUSTOM_CODE_BEFORE_HEAD_KEY) continue;
27797
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
28010
27798
  if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
28011
27799
  continue;
28012
27800
  }
@@ -28237,18 +28025,17 @@ function OhhwellsBridge() {
28237
28025
  }
28238
28026
  applyAiFooterFromContent(content);
28239
28027
  if (typeof content[STYLE_STORE_KEY] === "string") {
28240
- document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
28241
28028
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
28242
28029
  }
28243
28030
  for (const [key, val] of Object.entries(content)) {
28244
28031
  if (key === "__ohw_sections") continue;
28245
28032
  if (key === AI_SECTIONS_KEY) continue;
28246
28033
  if (key === AI_FOOTER_KEY) continue;
28247
- if (key === LOGO_PLACEHOLDER_KEY) continue;
28248
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
28249
28034
  if (key === BRAND_KIT_KEY) continue;
28250
28035
  if (key === STYLE_STORE_KEY) continue;
28251
28036
  if (BRAND_CHROME_KEYS.has(key)) continue;
28037
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
28038
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
28252
28039
  if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
28253
28040
  continue;
28254
28041
  }
@@ -28515,11 +28302,26 @@ function OhhwellsBridge() {
28515
28302
  const t2 = setTimeout(measure, 500);
28516
28303
  const ro = new ResizeObserver(schedule);
28517
28304
  ro.observe(document.body);
28305
+ let lastWidth = window.innerWidth;
28306
+ let resizeTimers = [];
28307
+ const clearResizeTimers = () => {
28308
+ resizeTimers.forEach(clearTimeout);
28309
+ resizeTimers = [];
28310
+ };
28311
+ const handleResize = () => {
28312
+ if (window.innerWidth === lastWidth) return;
28313
+ lastWidth = window.innerWidth;
28314
+ clearResizeTimers();
28315
+ resizeTimers = HEIGHT_SETTLE_DELAYS.map((delay) => setTimeout(measure, delay));
28316
+ };
28317
+ window.addEventListener("resize", handleResize);
28518
28318
  return () => {
28519
28319
  clearTimeout(t1);
28520
28320
  clearTimeout(t2);
28521
28321
  if (raf != null) cancelAnimationFrame(raf);
28522
28322
  ro.disconnect();
28323
+ clearResizeTimers();
28324
+ window.removeEventListener("resize", handleResize);
28523
28325
  };
28524
28326
  }, [pathname, isEditMode, postToParent2]);
28525
28327
  (0, import_react21.useEffect)(() => {
@@ -28816,13 +28618,6 @@ function OhhwellsBridge() {
28816
28618
  if (target.closest("[data-ohw-state-toggle]")) return;
28817
28619
  if (target.closest("[data-ohw-max-badge]")) return;
28818
28620
  if (isInsideLinkEditor(target)) return;
28819
- if (isInsideFloatingPanel(target)) return;
28820
- if (target.closest("[data-ohw-form-toolbar]")) return;
28821
- if (target.closest(
28822
- '[data-ohw-field-toolbar], [data-ohw-field-type-picker], [data-radix-popper-content-wrapper], [role="menu"], [data-slot="dropdown-menu-content"]'
28823
- )) {
28824
- return;
28825
- }
28826
28621
  if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
28827
28622
  clearMediaSelectionRef.current();
28828
28623
  }
@@ -28846,6 +28641,8 @@ function OhhwellsBridge() {
28846
28641
  return;
28847
28642
  }
28848
28643
  }
28644
+ if (isInsideFloatingPanel(target)) return;
28645
+ if (target.closest("[data-ohw-form-toolbar]")) return;
28849
28646
  const collectionItem = matchCollectionItemClick(target, collectionItemsRef.current);
28850
28647
  if (collectionItem) {
28851
28648
  e.preventDefault();
@@ -28857,6 +28654,11 @@ function OhhwellsBridge() {
28857
28654
  });
28858
28655
  return;
28859
28656
  }
28657
+ if (target.closest(
28658
+ '[data-ohw-field-toolbar], [data-ohw-field-type-picker], [data-radix-popper-content-wrapper], [role="menu"], [data-slot="dropdown-menu-content"]'
28659
+ )) {
28660
+ return;
28661
+ }
28860
28662
  {
28861
28663
  const formEl = getFormElement(target);
28862
28664
  const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
@@ -29010,13 +28812,9 @@ function OhhwellsBridge() {
29010
28812
  if (isIconEditable(editable) && !getSocialItem(editable)) {
29011
28813
  e.preventDefault();
29012
28814
  e.stopPropagation();
28815
+ selectIconElementRef.current(editable);
29013
28816
  const aiIcon = editable.hasAttribute("data-ai-icon");
29014
28817
  const featureIcon = aiIcon && /\.features\.\d+\.icon$/.test(editable.dataset.ohwKey ?? "");
29015
- if (aiIcon) {
29016
- aiSectionApiRef.current?.selectFromElement(editable);
29017
- } else {
29018
- selectIconElementRef.current(editable);
29019
- }
29020
28818
  postToParentRef.current({
29021
28819
  type: "ow:icon-pick",
29022
28820
  key: editable.dataset.ohwKey ?? "",
@@ -29244,9 +29042,6 @@ function OhhwellsBridge() {
29244
29042
  setHoveredItemRect(null);
29245
29043
  hoveredNavContainerRef.current = null;
29246
29044
  setHoveredNavContainerRect(null);
29247
- siblingHintElRef.current = null;
29248
- setSiblingHintRect(null);
29249
- setSiblingHintRects([]);
29250
29045
  return;
29251
29046
  }
29252
29047
  {
@@ -29367,6 +29162,7 @@ function OhhwellsBridge() {
29367
29162
  hoveredNavContainerRef.current = null;
29368
29163
  setHoveredNavContainerRect(null);
29369
29164
  hoveredItemElRef.current = editable;
29165
+ setHoveredItemRect(isSelectedForHoverRef.current(editable) ? null : editable.getBoundingClientRect());
29370
29166
  }
29371
29167
  }
29372
29168
  }
@@ -30076,11 +29872,7 @@ function OhhwellsBridge() {
30076
29872
  };
30077
29873
  const handleMouseMove = (e) => {
30078
29874
  const { clientX, clientY } = e;
30079
- if (pointOwnedByFloatingPanel(clientX, clientY)) {
30080
- dismissImageHover();
30081
- clearImageHover();
30082
- return;
30083
- }
29875
+ if (pointOwnedByFloatingPanel(clientX, clientY)) return;
30084
29876
  if (isNavDrawerOpen() && isPointOutsideOpenDrawer(clientX, clientY)) {
30085
29877
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
30086
29878
  formHoverElRef.current = null;
@@ -30109,12 +29901,7 @@ function OhhwellsBridge() {
30109
29901
  setHoveredItemRect(null);
30110
29902
  hoveredNavContainerRef.current = null;
30111
29903
  setHoveredNavContainerRect(null);
30112
- siblingHintElRef.current = null;
30113
- setSiblingHintRect(null);
30114
- setSiblingHintRects([]);
30115
29904
  dismissImageHover();
30116
- clearImageHover();
30117
- setSectionGap(null);
30118
29905
  return;
30119
29906
  }
30120
29907
  if (probeSocialsRowAt(clientX, clientY)) return;
@@ -30126,11 +29913,7 @@ function OhhwellsBridge() {
30126
29913
  if (e.data?.type !== "ow:pointer-sync") return;
30127
29914
  const { clientX, clientY } = e.data;
30128
29915
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
30129
- if (pointOwnedByFloatingPanel(clientX, clientY)) {
30130
- dismissImageHover();
30131
- clearImageHover();
30132
- return;
30133
- }
29916
+ if (pointOwnedByFloatingPanel(clientX, clientY)) return;
30134
29917
  if (isNavDrawerOpen() && isPointOutsideOpenDrawer(clientX, clientY)) return;
30135
29918
  if (probeSocialsRowAt(clientX, clientY)) return;
30136
29919
  probeSectionGapAt(clientX, clientY);
@@ -30510,7 +30293,6 @@ function OhhwellsBridge() {
30510
30293
  applyAiFooterFromContent(content);
30511
30294
  if (typeof content[STYLE_STORE_KEY] === "string") {
30512
30295
  stylesRef.current = content[STYLE_STORE_KEY];
30513
- document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
30514
30296
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
30515
30297
  }
30516
30298
  applyBrandChrome(content);
@@ -30525,12 +30307,12 @@ function OhhwellsBridge() {
30525
30307
  }
30526
30308
  if (key === AI_SECTIONS_KEY) continue;
30527
30309
  if (key === AI_FOOTER_KEY) continue;
30528
- if (key === LOGO_PLACEHOLDER_KEY) continue;
30529
- if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
30530
30310
  if (key === BRAND_KIT_KEY) continue;
30531
30311
  if (key === STYLE_STORE_KEY) continue;
30532
30312
  if (BRAND_CHROME_KEYS.has(key)) continue;
30313
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
30533
30314
  if (key === CUSTOM_CODE_AFTER_HEAD_KEY || key === CUSTOM_CODE_BEFORE_HEAD_KEY) continue;
30315
+ if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
30534
30316
  if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
30535
30317
  continue;
30536
30318
  }
@@ -31166,12 +30948,6 @@ function OhhwellsBridge() {
31166
30948
  closeLinkPopoverRef.current();
31167
30949
  return;
31168
30950
  }
31169
- if (floatingPanelOpenRef.current) {
31170
- setFloatingPanelRef.current(null);
31171
- deselectRef.current();
31172
- deactivateRef.current();
31173
- return;
31174
- }
31175
30951
  deselectRef.current();
31176
30952
  deactivateRef.current();
31177
30953
  clearMediaSelectionRef.current();
@@ -32001,10 +31777,10 @@ function OhhwellsBridge() {
32001
31777
  window.removeEventListener("message", handleDuplicateSection);
32002
31778
  window.removeEventListener("message", handleDeactivate);
32003
31779
  window.removeEventListener("message", handleSelectNav);
31780
+ document.documentElement.removeAttribute("data-ohw-panel-dragging");
32004
31781
  window.removeEventListener("message", handleToastAction);
32005
31782
  window.removeEventListener("message", handleFormCount);
32006
31783
  window.removeEventListener("message", handleUiEscape);
32007
- document.documentElement.removeAttribute("data-ohw-panel-dragging");
32008
31784
  autoSaveTimers.current.forEach(clearTimeout);
32009
31785
  autoSaveTimers.current.clear();
32010
31786
  if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
@@ -32298,7 +32074,7 @@ function OhhwellsBridge() {
32298
32074
  postToParent2({
32299
32075
  type: "ow:ready",
32300
32076
  version: "1",
32301
- bridgeVersion: "0.1.158-next.422",
32077
+ bridgeVersion: "0.1.158",
32302
32078
  path: pathname,
32303
32079
  nodes: collectEditableNodes(editContentRef.current),
32304
32080
  sections