@ohhwells/bridge 0.1.158-next.424 → 0.1.159

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -6763,21 +6763,6 @@ function pricingCardFeatureCount(tree, index) {
6763
6763
  const target = findPricingCollection(tree)?.children?.[index];
6764
6764
  return target ? pricingFeatureList(target).length : 0;
6765
6765
  }
6766
- function prevailingFeatureIcon(features) {
6767
- const counts = /* @__PURE__ */ new Map();
6768
- for (const row of features) {
6769
- const key = `${row.icon ?? ""}|${row.iconColor ?? ""}`;
6770
- const entry = counts.get(key) ?? { count: 0, ...row.icon ? { icon: row.icon } : {}, ...row.iconColor ? { iconColor: row.iconColor } : {} };
6771
- entry.count += 1;
6772
- counts.set(key, entry);
6773
- }
6774
- let best = null;
6775
- for (const entry of counts.values()) {
6776
- if (!best || entry.count > best.count) best = entry;
6777
- }
6778
- if (!best) return {};
6779
- return { ...best.icon ? { icon: best.icon } : {}, ...best.iconColor ? { iconColor: best.iconColor } : {} };
6780
- }
6781
6766
  function addPricingCardFeature(tree, index, text) {
6782
6767
  return withPricingCollection(tree, (collection) => {
6783
6768
  const children = collection.children;
@@ -6786,7 +6771,7 @@ function addPricingCardFeature(tree, index, text) {
6786
6771
  const features = pricingFeatureList(target);
6787
6772
  if (features.length >= PRICING_FEATURES_MAX) return false;
6788
6773
  target.slots = target.slots ?? {};
6789
- target.slots.features = [...features, { text, ...prevailingFeatureIcon(features) }];
6774
+ target.slots.features = [...features, { text }];
6790
6775
  return true;
6791
6776
  });
6792
6777
  }
@@ -7986,7 +7971,7 @@ var AI_MOBILE_CSS = [
7986
7971
  // page's first section. Top only (bottom needs no nav clearance); ~96px approximates the
7987
7972
  // template nav's own height (measured ~94px) — there's no shared `--ohw-header-h` var to read
7988
7973
  // an exact figure from (no live measurement exists), so this is a safe approximation, not a
7989
- // pixel-exact fit. Horizontal spacing already comes from `data-ai-section-inner`.
7974
+ // pixel-exact fit. Horizontal spacing already comes from `data-ai-container`.
7990
7975
  '[data-ai-overlay-clearance] [data-ai-overlap-layer="overlay"]{padding-top:96px}',
7991
7976
  // A full-bleed carousel's height is pure arithmetic: it spans the whole window, so 16/9 turns
7992
7977
  // window width straight into height. Measured at 16/9 it came out ~90% of the viewport at EVERY
@@ -8002,10 +7987,8 @@ var AI_MOBILE_CSS = [
8002
7987
  "[data-ai-section]{overflow-x:hidden}",
8003
7988
  // `:not([data-ai-full-bleed])`: this `!important` gutter beat the inline `padding: 0` that a
8004
7989
  // full-bleed section's container sets, so a carousel/media hero that is edge-to-edge on desktop
8005
- // still got a 20px band of section background down both sides on a phone. The footer's own
8006
- // container keeps the unguarded rule below — `data-ai-full-bleed` can never appear on it.
8007
- "[data-ai-section-inner]:not([data-ai-full-bleed]){padding:0 20px !important}",
8008
- "[data-ai-container]{padding:0 20px !important}",
7990
+ // still got a 20px band of section background down both sides on a phone.
7991
+ "[data-ai-container]:not([data-ai-full-bleed]){padding:0 20px !important}",
8009
7992
  "[data-ai-row]{display:flex !important;flex-direction:column !important;align-items:stretch !important}",
8010
7993
  "[data-ai-cell]{width:100%;min-width:0}",
8011
7994
  // The rule above turns every row into a COLUMN flex container on phones, which silently breaks
@@ -8210,31 +8193,6 @@ function pricingCardEmphasis(featured, ctx, hasBg) {
8210
8193
  function textAttrs(ctx, path) {
8211
8194
  return ctx.keyFor ? { "data-ohw-key": ctx.keyFor(path), "data-ohw-editable": "text" } : {};
8212
8195
  }
8213
- var AI_RESPONSIVE_CSS = [
8214
- "@media (max-width: 960px) {",
8215
- " [data-ai-responsive] [data-ai-grid] { grid-template-columns: repeat(2, 1fr) !important; }",
8216
- ' [data-ai-responsive] [data-ai-grid="1"] { grid-template-columns: 1fr !important; }',
8217
- "}",
8218
- "@media (max-width: 640px) {",
8219
- " [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
8220
- " [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
8221
- // Group containers flatten to a column on phones; span placements come along for free.
8222
- // `:not([data-ai-group="overlap"])` for the same reason as the twin pair in AI_MOBILE_CSS — see
8223
- // the comment there. This sheet's copy is the narrower selector, so without the exemption it
8224
- // flattens an `overlap` hero below 640px even though the other rule stands down: measured a 390px
8225
- // viewport with the heading/CTA in flow *under* the image, 219px of section background where the
8226
- // layered text used to be.
8227
- ' [data-ai-responsive] [data-ai-group]:not([data-ai-group="overlap"]) { display: flex !important; flex-direction: column !important; }',
8228
- ' [data-ai-responsive] [data-ai-group]:not([data-ai-group="overlap"]) > * { grid-column: auto !important; }',
8229
- // `:not([data-ai-full-bleed])` for the same reason as the twin rule in AI_MOBILE_CSS: this one
8230
- // is narrower (0,2,0 vs 0,1,0) and also `!important`, so without the guard it wins outright and
8231
- // a full-bleed hero gets its 20px side band back below 640px only — the one width where the
8232
- // other rule had already been taught to stand down.
8233
- " [data-ai-responsive] [data-ai-section-inner]:not([data-ai-full-bleed]) { padding-left: 20px !important; padding-right: 20px !important; }",
8234
- " [data-ai-responsive] { overflow-x: hidden; }",
8235
- " [data-ai-responsive] img { max-width: 100%; }",
8236
- "}"
8237
- ].join("\n");
8238
8196
  var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
8239
8197
  function MediaBox({
8240
8198
  refValue,
@@ -8289,7 +8247,7 @@ function MediaBox({
8289
8247
  position: fill ? "absolute" : void 0,
8290
8248
  inset: fill ? 0 : void 0,
8291
8249
  width: fill ? "100%" : fixedSize ? "auto" : "100%",
8292
- height: fill ? "100%" : fixedSize ? height : void 0,
8250
+ height: fill ? "100%" : void 0,
8293
8251
  aspectRatio: fill ? void 0 : aspectRatio,
8294
8252
  minHeight: fill || fixedSize ? void 0 : height ?? (aspectRatio ? void 0 : 180),
8295
8253
  borderRadius: radius,
@@ -9785,7 +9743,7 @@ function CollectionBlock({ node, ctx, path }) {
9785
9743
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
9786
9744
  "div",
9787
9745
  {
9788
- "data-ai-grid": String(itemsPerRow),
9746
+ "data-ai-grid": "",
9789
9747
  "data-ohw-pricing-collection": isPricing ? "" : void 0,
9790
9748
  style: {
9791
9749
  display: "grid",
@@ -10087,7 +10045,7 @@ function renderNode(node, ctx, path, ambientAlign) {
10087
10045
  },
10088
10046
  children: [
10089
10047
  i > 0 && ctx.heroOverlapFullBleed ? (
10090
- // The section's `data-ai-section-inner` gave up its max-width/gutter so the base
10048
+ // The section's `data-ai-container` gave up its max-width/gutter so the base
10091
10049
  // layer (this group's i===0 sibling) can span edge-to-edge — reintroduced here,
10092
10050
  // scoped to just the overlay text, so a heading/copy/CTA stack doesn't stretch
10093
10051
  // the full section width instead of reading at the usual content width.
@@ -10506,7 +10464,6 @@ function AiTreeRenderer({
10506
10464
  ...fillHeight ? { "data-ai-fill-height": "" } : {},
10507
10465
  ...needsOverlayClearance ? { "data-ai-overlay-clearance": "" } : {},
10508
10466
  ...bgAttrs,
10509
- "data-ai-responsive": "",
10510
10467
  style: {
10511
10468
  position: "relative",
10512
10469
  padding: `${padCss} 0`,
@@ -10520,13 +10477,12 @@ function AiTreeRenderer({
10520
10477
  ...fillHeight ? { display: "grid" } : {}
10521
10478
  },
10522
10479
  children: [
10523
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_RESPONSIVE_CSS }),
10524
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_MOBILE_CSS }),
10525
10480
  isOverlay && backgroundUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
10481
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: AI_MOBILE_CSS }),
10526
10482
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
10527
10483
  "div",
10528
10484
  {
10529
- "data-ai-section-inner": "",
10485
+ "data-ai-container": "",
10530
10486
  ...fullBleedMedia ? { "data-ai-full-bleed": "" } : {},
10531
10487
  style: {
10532
10488
  position: "relative",
@@ -10543,7 +10499,7 @@ function AiTreeRenderer({
10543
10499
  children: tree.rows.map((row, r2) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
10544
10500
  "div",
10545
10501
  {
10546
- "data-ai-columns": "",
10502
+ "data-ai-row": "",
10547
10503
  style: {
10548
10504
  display: "grid",
10549
10505
  gridTemplateColumns: "repeat(12, 1fr)",
@@ -11205,10 +11161,9 @@ function applyAiSectionsToDom(state, options) {
11205
11161
  if (existing && existing.serialized === serialized && existing.container.isConnected) {
11206
11162
  continue;
11207
11163
  }
11164
+ const isNewMount = !existing || !existing.container.isConnected;
11208
11165
  let container = existing?.container;
11209
- let isNewMount = false;
11210
11166
  if (!container || !container.isConnected) {
11211
- isNewMount = true;
11212
11167
  existing?.root.unmount();
11213
11168
  container?.remove();
11214
11169
  container = document.createElement("div");
@@ -17367,7 +17322,7 @@ function apiBase() {
17367
17322
  function bridgeVersion() {
17368
17323
  try {
17369
17324
  if (true) {
17370
- return "0.1.158-next.424";
17325
+ return "0.1.159";
17371
17326
  }
17372
17327
  } catch {
17373
17328
  }
@@ -17381,7 +17336,7 @@ function stampBridgeVersionOnDocument() {
17381
17336
  return;
17382
17337
  }
17383
17338
  try {
17384
- const v = true ? "0.1.158-next.424" : "";
17339
+ const v = true ? "0.1.159" : "";
17385
17340
  if (v) {
17386
17341
  document.documentElement.setAttribute("data-ohw-bridge-version", v);
17387
17342
  window.__OHW_BRIDGE_VERSION__ = v;
@@ -18059,7 +18014,6 @@ function MediaOverlay({
18059
18014
  (prev) => prev && prev.fullWidth === width && prev.height === height ? prev : { fullWidth: width, height }
18060
18015
  );
18061
18016
  }, [isVideo]);
18062
- const replaceLabel = isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image";
18063
18017
  const replaceMode = buttonSize ? replaceButtonMode({ width: rect.width, height: rect.height }, buttonSize) : "none";
18064
18018
  const box = {
18065
18019
  position: "fixed",
@@ -18158,10 +18112,11 @@ function MediaOverlay({
18158
18112
  className: "flex items-center justify-center cursor-pointer",
18159
18113
  style: {
18160
18114
  ...box,
18161
- // Visual only. `pointer-events: auto` on this full-bleed rect covered the navbar
18162
- // (hero bg includes the absolute TopNav) and swallowed CTA press-drag. Text overlay
18163
- // and cueOnly also need clicks to fall through — Replace / video controls opt back in.
18164
- pointerEvents: "none",
18115
+ // When text overlays this media, let clicks fall THROUGH to the text underneath. In the
18116
+ // parent this needed a whole `ow:click-at` round-trip to re-hit-test inside the iframe;
18117
+ // in-document, pointer-events does it natively. The button below opts back in, so
18118
+ // Replace still works.
18119
+ pointerEvents: hover.hasTextOverlap || cueOnly ? "none" : "auto",
18165
18120
  // Selected: a firm component ring with no wash, so the image reads as chosen rather
18166
18121
  // than hovered. Hover keeps the existing tinted preview.
18167
18122
  boxShadow: selected ? "inset 0 0 0 2px var(--color-bridge-primary)" : "inset 0 0 0 1.5px var(--color-bridge-primary)",
@@ -18188,17 +18143,17 @@ function MediaOverlay({
18188
18143
  },
18189
18144
  children: [
18190
18145
  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 }),
18191
- replaceLabel
18146
+ isVideo ? "Replace video" : "Replace image"
18192
18147
  ]
18193
18148
  }
18194
18149
  ),
18195
- showChrome && replaceMode !== "none" && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
18150
+ replaceMode === "none" || cueOnly ? null : /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
18196
18151
  Button,
18197
18152
  {
18198
18153
  "data-ohw-media-overlay": "",
18199
18154
  variant: "outline",
18200
18155
  size: "sm",
18201
- "aria-label": replaceLabel,
18156
+ "aria-label": isVideo ? "Replace video" : "Replace image",
18202
18157
  className: "gap-1.5 cursor-pointer hover:bg-background",
18203
18158
  style: {
18204
18159
  ...OVERLAY_BUTTON_STYLE,
@@ -18221,7 +18176,7 @@ function MediaOverlay({
18221
18176
  },
18222
18177
  children: [
18223
18178
  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 }),
18224
- replaceMode === "full" ? replaceLabel : null
18179
+ replaceMode === "full" ? isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image" : null
18225
18180
  ]
18226
18181
  }
18227
18182
  )
@@ -18815,7 +18770,7 @@ function AiSectionOverlay({
18815
18770
 
18816
18771
  // src/ui/ai-section/AiMediaStripOverlay.tsx
18817
18772
  var import_react10 = require("react");
18818
- var import_lucide_react9 = require("lucide-react");
18773
+ var import_lucide_react8 = require("lucide-react");
18819
18774
 
18820
18775
  // src/lib/ai-media-strip-tree.ts
18821
18776
  var MEDIA_STRIP_ITEM_MIN = 1;
@@ -18929,6 +18884,14 @@ function setMediaStripItemAsset(tree, index, assetId) {
18929
18884
  return true;
18930
18885
  });
18931
18886
  }
18887
+ function setMediaStripItemAlt(tree, index, alt) {
18888
+ return withMediaStrip(tree, (block) => {
18889
+ const items = block.items;
18890
+ if (index < 0 || index >= items.length) return false;
18891
+ items[index] = { ...items[index], alt };
18892
+ return true;
18893
+ });
18894
+ }
18932
18895
  function setMediaStripItemLink(tree, index, link) {
18933
18896
  return withMediaStrip(tree, (block) => {
18934
18897
  const items = block.items;
@@ -18946,117 +18909,11 @@ function setMediaStripItemLink(tree, index, link) {
18946
18909
  });
18947
18910
  }
18948
18911
 
18949
- // src/ui/dialog.tsx
18950
- var React9 = __toESM(require("react"), 1);
18951
- var import_radix_ui6 = require("radix-ui");
18952
- var import_lucide_react8 = require("lucide-react");
18953
- var import_jsx_runtime20 = require("react/jsx-runtime");
18954
- function Dialog2({
18955
- ...props
18956
- }) {
18957
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_radix_ui6.Dialog.Root, { "data-slot": "dialog", ...props });
18958
- }
18959
- function DialogPortal({
18960
- ...props
18961
- }) {
18962
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_radix_ui6.Dialog.Portal, { ...props });
18963
- }
18964
- function DialogOverlay({
18965
- className,
18966
- ...props
18967
- }) {
18968
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
18969
- import_radix_ui6.Dialog.Overlay,
18970
- {
18971
- "data-slot": "dialog-overlay",
18972
- "data-ohw-link-modal-root": "",
18973
- className: cn("fixed inset-0 z-[2147483646] bg-black/50", className),
18974
- ...props
18975
- }
18976
- );
18977
- }
18978
- var DialogContent = React9.forwardRef(
18979
- ({ className, children, showCloseButton = true, container, ...props }, ref) => {
18980
- const positionMode = container ? "absolute" : "fixed";
18981
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(DialogPortal, { container: container ?? void 0, children: [
18982
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(DialogOverlay, { className: cn(positionMode, "inset-0") }),
18983
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
18984
- import_radix_ui6.Dialog.Content,
18985
- {
18986
- ref,
18987
- "data-slot": "dialog-content",
18988
- className: cn(
18989
- positionMode,
18990
- "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",
18991
- "rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
18992
- container ? "max-h-[calc(100%-32px)]" : "max-h-[calc(100vh-32px)]",
18993
- className
18994
- ),
18995
- onMouseDown: (e) => e.stopPropagation(),
18996
- ...props,
18997
- children: [
18998
- children,
18999
- showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
19000
- import_radix_ui6.Dialog.Close,
19001
- {
19002
- type: "button",
19003
- className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
19004
- "aria-label": "Close",
19005
- children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react8.X, { size: 16, "aria-hidden": true })
19006
- }
19007
- ) : null
19008
- ]
19009
- }
19010
- )
19011
- ] });
19012
- }
19013
- );
19014
- DialogContent.displayName = import_radix_ui6.Dialog.Content.displayName;
19015
- function DialogHeader({
19016
- className,
19017
- ...props
19018
- }) {
19019
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: cn("flex flex-col gap-1.5", className), ...props });
19020
- }
19021
- function DialogFooter({
19022
- className,
19023
- ...props
19024
- }) {
19025
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
19026
- "div",
19027
- {
19028
- className: cn("flex items-center justify-end gap-2", className),
19029
- ...props
19030
- }
19031
- );
19032
- }
19033
- var DialogTitle = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
19034
- import_radix_ui6.Dialog.Title,
19035
- {
19036
- ref,
19037
- className: cn(
19038
- "text-lg font-semibold leading-none tracking-tight text-card-foreground",
19039
- className
19040
- ),
19041
- ...props
19042
- }
19043
- ));
19044
- DialogTitle.displayName = import_radix_ui6.Dialog.Title.displayName;
19045
- var DialogDescription = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
19046
- import_radix_ui6.Dialog.Description,
19047
- {
19048
- ref,
19049
- className: cn("text-sm text-muted-foreground", className),
19050
- ...props
19051
- }
19052
- ));
19053
- DialogDescription.displayName = import_radix_ui6.Dialog.Description.displayName;
19054
- var DialogClose = import_radix_ui6.Dialog.Close;
19055
-
19056
18912
  // src/ui/ai-section/AiMediaStripOverlay.tsx
19057
- var import_jsx_runtime21 = require("react/jsx-runtime");
19058
- var PRIMARY3 = "var(--ohw-primary, #0885FE)";
19059
- 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]";
18913
+ var import_jsx_runtime20 = require("react/jsx-runtime");
18914
+ var PRIMARY3 = "#0885FE";
18915
+ var OHW_BLUE = `var(--ohw-primary, ${PRIMARY3})`;
18916
+ var OHW_BLUE_DARK = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY3}) 60%, #000000)`;
19060
18917
  function findMediaStripItemElement(sectionId, index) {
19061
18918
  const sectionEl = findByInstanceId(sectionId);
19062
18919
  if (!sectionEl) return null;
@@ -19066,35 +18923,14 @@ function findMediaStripItemElement(sectionId, index) {
19066
18923
  }
19067
18924
  return null;
19068
18925
  }
19069
- function itemRefFromTarget(target) {
19070
- if (!(target instanceof Element)) return null;
19071
- if (target.closest(CHROME_SELECTOR)) return null;
19072
- const itemEl = target.closest("[data-ohw-media-strip-item]");
19073
- if (!itemEl) return null;
19074
- const sectionEl = itemEl.closest("[data-ohw-section]");
19075
- const unit = sectionEl ? movableUnit(sectionEl) : null;
19076
- const sectionId = unit ? instanceIdOf(unit) || null : null;
19077
- const index = Number(itemEl.dataset.ohwMediaStripIndex);
19078
- if (!sectionId || Number.isNaN(index)) return null;
19079
- return { sectionId, index };
19080
- }
19081
- function sameRef(a, b) {
19082
- return a?.sectionId === b?.sectionId && a?.index === b?.index;
19083
- }
19084
- function rectOf(el) {
18926
+ function readRect2(ref) {
18927
+ const el = ref && findMediaStripItemElement(ref.sectionId, ref.index);
19085
18928
  if (!el) return null;
19086
18929
  const r2 = el.getBoundingClientRect();
19087
18930
  if (r2.width <= 0 || r2.height <= 0) return null;
19088
18931
  return { top: r2.top, left: r2.left, width: r2.width, height: r2.height };
19089
18932
  }
19090
- function readRect2(ref) {
19091
- return rectOf(ref && findMediaStripItemElement(ref.sectionId, ref.index));
19092
- }
19093
- function readStripRect(ref) {
19094
- const item = ref && findMediaStripItemElement(ref.sectionId, ref.index);
19095
- return rectOf(item?.closest("[data-ohw-media-strip]") ?? item);
19096
- }
19097
- function useLiveItemRect(ref, strip = false) {
18933
+ function useLiveItemRect(ref) {
19098
18934
  const [rect, setRect] = (0, import_react10.useState)(null);
19099
18935
  (0, import_react10.useEffect)(() => {
19100
18936
  if (!ref) {
@@ -19102,7 +18938,7 @@ function useLiveItemRect(ref, strip = false) {
19102
18938
  return;
19103
18939
  }
19104
18940
  const update = () => {
19105
- const next = strip ? readStripRect(ref) : readRect2(ref);
18941
+ const next = readRect2(ref);
19106
18942
  setRect(
19107
18943
  (prev) => prev && next && prev.top === next.top && prev.left === next.left && prev.width === next.width && prev.height === next.height ? prev : next
19108
18944
  );
@@ -19117,49 +18953,9 @@ function useLiveItemRect(ref, strip = false) {
19117
18953
  window.removeEventListener("resize", update);
19118
18954
  clearInterval(interval);
19119
18955
  };
19120
- }, [ref?.sectionId, ref?.index, strip]);
18956
+ }, [ref?.sectionId, ref?.index]);
19121
18957
  return rect;
19122
18958
  }
19123
- var TOOLBAR_STRIP_GAP = 52;
19124
- var TOOLBAR_STRIP_GAP_MOBILE = 42;
19125
- var TOOLBAR_EDGE_MARGIN2 = 4;
19126
- function StripToolbarSlot({ stripRect, children }) {
19127
- const slotRef = (0, import_react10.useRef)(null);
19128
- const [shift, setShift] = (0, import_react10.useState)(0);
19129
- const centerX = stripRect.left + stripRect.width / 2;
19130
- (0, import_react10.useLayoutEffect)(() => {
19131
- const slot = slotRef.current;
19132
- if (!slot) return;
19133
- const clamp = () => {
19134
- const w = slot.offsetWidth;
19135
- if (w <= 0) return;
19136
- const half = w / 2;
19137
- const clamped = Math.max(TOOLBAR_EDGE_MARGIN2 + half, Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN2 - half));
19138
- setShift((prev) => Math.abs(prev - (clamped - centerX)) < 0.5 ? prev : clamped - centerX);
19139
- };
19140
- clamp();
19141
- const ro = new ResizeObserver(clamp);
19142
- ro.observe(slot);
19143
- return () => ro.disconnect();
19144
- }, [centerX, children]);
19145
- const mobile = window.innerWidth <= 480;
19146
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19147
- "div",
19148
- {
19149
- ref: slotRef,
19150
- "data-ohw-bridge": "",
19151
- "data-ohw-item-interaction": "",
19152
- className: "pointer-events-auto fixed",
19153
- style: {
19154
- top: stripRect.top - (mobile ? TOOLBAR_STRIP_GAP_MOBILE : TOOLBAR_STRIP_GAP),
19155
- left: centerX + shift,
19156
- transform: "translate(-50%, -100%)",
19157
- zIndex: 2147483645
19158
- },
19159
- children
19160
- }
19161
- );
19162
- }
19163
18959
  function requestValidateTree(postToParent2, tree) {
19164
18960
  return new Promise((resolve) => {
19165
18961
  const id = `ai-validate-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
@@ -19196,177 +18992,221 @@ function requestAssetPick(postToParent2) {
19196
18992
  postToParent2({ type: "ow:asset-pick", id });
19197
18993
  });
19198
18994
  }
19199
- function ToolbarAction({
18995
+ function Pill({
19200
18996
  label,
19201
18997
  onClick,
19202
- disabled = false,
19203
- active = false,
19204
- children
18998
+ disabled,
18999
+ active
19205
19000
  }) {
19206
- const button = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(CustomToolbarButton, { type: "button", "aria-label": label, disabled, active, onClick, className: "size-8", children });
19207
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Tooltip, { children: [
19208
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipTrigger, { asChild: true, children: disabled ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "inline-flex", children: button }) : button }),
19209
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: label })
19210
- ] });
19211
- }
19212
- function ToolbarSeparator() {
19213
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { "aria-hidden": true, className: "mx-0.5 block h-6 w-px shrink-0 bg-border" });
19001
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
19002
+ "button",
19003
+ {
19004
+ type: "button",
19005
+ onClick,
19006
+ disabled,
19007
+ style: {
19008
+ height: 28,
19009
+ padding: "0 10px",
19010
+ display: "inline-flex",
19011
+ alignItems: "center",
19012
+ gap: 4,
19013
+ borderRadius: 999,
19014
+ border: "none",
19015
+ cursor: disabled ? "default" : "pointer",
19016
+ background: active ? OHW_BLUE_DARK : "#ffffff",
19017
+ color: active ? "#ffffff" : OHW_BLUE_DARK,
19018
+ opacity: disabled ? 0.4 : 1,
19019
+ fontSize: 12,
19020
+ lineHeight: "16px",
19021
+ fontFamily: "Figtree, ui-sans-serif, system-ui, sans-serif",
19022
+ whiteSpace: "nowrap",
19023
+ flex: "0 0 auto"
19024
+ },
19025
+ children: label
19026
+ }
19027
+ );
19214
19028
  }
19215
- function OptionMenu({
19216
- label,
19217
- value,
19218
- options,
19219
- onChange
19220
- }) {
19221
- const current = options.find((o) => o.value === value) ?? options[0];
19222
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
19223
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-xs text-muted-foreground", children: label }),
19224
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(DropdownMenu, { modal: false, children: [
19225
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
19226
- "button",
19227
- {
19228
- type: "button",
19229
- "aria-label": label,
19230
- 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",
19231
- children: [
19232
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "flex-1 text-left", children: current.label }),
19233
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.ChevronDown, { size: 16, style: { pointerEvents: "none" } })
19234
- ]
19235
- }
19236
- ) }),
19237
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DropdownMenuContent, { align: "end", sideOffset: 8, className: "w-[132px] min-w-[132px] p-1", children: options.map((option) => {
19238
- const selected = option.value === value;
19239
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
19240
- DropdownMenuItem,
19029
+ function AltInput({ value, onCommit }) {
19030
+ const [draft, setDraft] = (0, import_react10.useState)(value);
19031
+ (0, import_react10.useEffect)(() => setDraft(value), [value]);
19032
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
19033
+ "div",
19034
+ {
19035
+ style: {
19036
+ display: "flex",
19037
+ alignItems: "center",
19038
+ gap: 6,
19039
+ height: 28,
19040
+ padding: "0 12px",
19041
+ borderRadius: 999,
19042
+ background: "#ffffff",
19043
+ width: "100%",
19044
+ maxWidth: 260,
19045
+ boxSizing: "border-box"
19046
+ },
19047
+ children: [
19048
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
19049
+ "span",
19241
19050
  {
19242
- role: "menuitemradio",
19243
- "aria-checked": selected,
19244
- onSelect: () => onChange(option.value),
19245
- className: cn("grid grid-cols-[1fr_16px] gap-2 py-2 pl-2 pr-2 text-[13px]", selected && "bg-muted"),
19246
- children: [
19247
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { children: option.label }),
19248
- selected && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Check, { size: 16, className: "text-bridge-primary" })
19249
- ]
19250
- },
19251
- option.value
19252
- );
19253
- }) })
19254
- ] })
19255
- ] });
19256
- }
19257
- var VARIANTS = [
19258
- { value: "logos", label: "Logos" },
19259
- { value: "reel", label: "Reel" }
19260
- ];
19261
- var ALIGNS = [
19262
- { value: "left", label: "Align left", Icon: import_lucide_react9.AlignLeft },
19263
- { value: "center", label: "Align center", Icon: import_lucide_react9.AlignCenter },
19264
- { value: "right", label: "Align right", Icon: import_lucide_react9.AlignRight }
19265
- ];
19266
- var HOVERS = [
19267
- { value: "none", label: "None" },
19268
- { value: "lift", label: "Lift" },
19269
- { value: "zoom", label: "Zoom" },
19270
- { value: "fade", label: "Fade" }
19271
- ];
19272
- var HTTP_URL_RE = /^https?:\/\/\S+$/i;
19273
- function LinkModal({
19274
- href,
19275
- linkTarget,
19276
- onClose,
19277
- onSave
19278
- }) {
19279
- const [draft, setDraft] = (0, import_react10.useState)(href);
19280
- const [newTab, setNewTab] = (0, import_react10.useState)(linkTarget !== "same-tab");
19051
+ style: {
19052
+ flex: "0 0 auto",
19053
+ color: OHW_BLUE_DARK,
19054
+ fontSize: 10,
19055
+ fontWeight: 700,
19056
+ textTransform: "uppercase",
19057
+ letterSpacing: 0.4
19058
+ },
19059
+ children: "Alt"
19060
+ }
19061
+ ),
19062
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
19063
+ "input",
19064
+ {
19065
+ type: "text",
19066
+ value: draft,
19067
+ onChange: (e) => setDraft(e.target.value),
19068
+ onBlur: () => {
19069
+ if (draft !== value) onCommit(draft);
19070
+ },
19071
+ onKeyDown: (e) => {
19072
+ if (e.key === "Enter") e.currentTarget.blur();
19073
+ if (e.key === "Escape") {
19074
+ setDraft(value);
19075
+ e.currentTarget.blur();
19076
+ }
19077
+ },
19078
+ placeholder: "Describe this image",
19079
+ "aria-label": "Image alt text",
19080
+ style: {
19081
+ flex: 1,
19082
+ minWidth: 0,
19083
+ border: "none",
19084
+ outline: "none",
19085
+ background: "transparent",
19086
+ fontSize: 12,
19087
+ lineHeight: "16px",
19088
+ fontFamily: "Figtree, ui-sans-serif, system-ui, sans-serif",
19089
+ color: "#1f2937"
19090
+ }
19091
+ }
19092
+ )
19093
+ ]
19094
+ }
19095
+ );
19096
+ }
19097
+ var VARIANTS = [
19098
+ { value: "logos", label: "Logos" },
19099
+ { value: "reel", label: "Reel" }
19100
+ ];
19101
+ var ALIGNS = [
19102
+ { value: "left", label: "Left" },
19103
+ { value: "center", label: "Center" },
19104
+ { value: "right", label: "Right" }
19105
+ ];
19106
+ var HOVERS = [
19107
+ { value: "none", label: "None" },
19108
+ { value: "lift", label: "Lift" },
19109
+ { value: "zoom", label: "Zoom" },
19110
+ { value: "fade", label: "Fade" }
19111
+ ];
19112
+ var TREATMENTS = [
19113
+ { value: "color", label: "Color" },
19114
+ { value: "grayscale", label: "Grayscale" }
19115
+ ];
19116
+ var HTTP_URL_RE = /^https?:\/\/\S+$/i;
19117
+ function LinkInput({ value, onCommit }) {
19118
+ const [draft, setDraft] = (0, import_react10.useState)(value);
19281
19119
  const [invalid, setInvalid] = (0, import_react10.useState)(false);
19282
- const save = () => {
19120
+ (0, import_react10.useEffect)(() => {
19121
+ setDraft(value);
19122
+ setInvalid(false);
19123
+ }, [value]);
19124
+ const commit = () => {
19283
19125
  const next = draft.trim();
19284
- if (next && !HTTP_URL_RE.test(next)) {
19285
- setInvalid(true);
19286
- return;
19287
- }
19288
- onSave(next, newTab ? "new-tab" : "same-tab");
19289
- onClose();
19126
+ if (next === value) return setInvalid(false);
19127
+ if (next && !HTTP_URL_RE.test(next)) return setInvalid(true);
19128
+ setInvalid(false);
19129
+ onCommit(next);
19290
19130
  };
19291
- 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: [
19292
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DialogHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DialogTitle, { className: "text-base", children: "Image link" }) }),
19293
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-col gap-3", children: [
19294
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19295
- "input",
19296
- {
19297
- autoFocus: true,
19298
- type: "text",
19299
- inputMode: "url",
19300
- value: draft,
19301
- onChange: (e) => {
19302
- setDraft(e.target.value);
19303
- setInvalid(false);
19304
- },
19305
- onKeyDown: (e) => {
19306
- if (e.key === "Enter") save();
19307
- },
19308
- placeholder: "https://example.com",
19309
- "aria-label": "Image link URL",
19310
- "aria-invalid": invalid,
19311
- className: cn(
19312
- "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",
19313
- invalid && "border-destructive focus:border-destructive"
19314
- )
19315
- }
19316
- ),
19317
- invalid && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { className: "text-xs text-destructive", children: "Enter a full URL starting with http:// or https://" }),
19318
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("label", { className: "flex items-center gap-2 text-[13px] text-foreground", children: [
19319
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("input", { type: "checkbox", checked: newTab, onChange: (e) => setNewTab(e.target.checked) }),
19320
- "Open in new tab"
19321
- ] })
19322
- ] }),
19323
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(DialogFooter, { children: [
19324
- href && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19325
- "button",
19326
- {
19327
- type: "button",
19328
- onClick: () => {
19329
- onSave("", "new-tab");
19330
- onClose();
19331
- },
19332
- className: "mr-auto rounded-[6px] px-3 py-2 text-[13px] font-medium text-destructive transition-colors hover:bg-muted",
19333
- children: "Remove link"
19334
- }
19335
- ),
19336
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19337
- "button",
19338
- {
19339
- type: "button",
19340
- onClick: onClose,
19341
- className: "rounded-[6px] px-3 py-2 text-[13px] font-medium text-foreground transition-colors hover:bg-muted",
19342
- children: "Cancel"
19343
- }
19344
- ),
19345
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19346
- "button",
19347
- {
19348
- type: "button",
19349
- onClick: save,
19350
- className: "rounded-[6px] bg-bridge-primary px-3 py-2 text-[13px] font-medium text-primary-foreground transition-opacity hover:opacity-90",
19351
- children: "Save"
19352
- }
19353
- )
19354
- ] })
19355
- ] }) });
19131
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
19132
+ "div",
19133
+ {
19134
+ style: {
19135
+ display: "flex",
19136
+ alignItems: "center",
19137
+ gap: 6,
19138
+ height: 28,
19139
+ padding: "0 12px",
19140
+ borderRadius: 999,
19141
+ background: "#ffffff",
19142
+ width: "100%",
19143
+ maxWidth: 260,
19144
+ boxSizing: "border-box",
19145
+ boxShadow: invalid ? "inset 0 0 0 1.5px #EF4444" : "none"
19146
+ },
19147
+ children: [
19148
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
19149
+ "span",
19150
+ {
19151
+ style: {
19152
+ flex: "0 0 auto",
19153
+ color: OHW_BLUE_DARK,
19154
+ fontSize: 10,
19155
+ fontWeight: 700,
19156
+ textTransform: "uppercase",
19157
+ letterSpacing: 0.4
19158
+ },
19159
+ children: "Link"
19160
+ }
19161
+ ),
19162
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
19163
+ "input",
19164
+ {
19165
+ type: "text",
19166
+ inputMode: "url",
19167
+ value: draft,
19168
+ onChange: (e) => setDraft(e.target.value),
19169
+ onBlur: commit,
19170
+ onKeyDown: (e) => {
19171
+ if (e.key === "Enter") e.currentTarget.blur();
19172
+ if (e.key === "Escape") {
19173
+ setDraft(value);
19174
+ setInvalid(false);
19175
+ e.currentTarget.blur();
19176
+ }
19177
+ },
19178
+ placeholder: "https://example.com",
19179
+ "aria-label": "Image link URL",
19180
+ "aria-invalid": invalid,
19181
+ style: {
19182
+ flex: 1,
19183
+ minWidth: 0,
19184
+ border: "none",
19185
+ outline: "none",
19186
+ background: "transparent",
19187
+ fontSize: 12,
19188
+ lineHeight: "16px",
19189
+ fontFamily: "Figtree, ui-sans-serif, system-ui, sans-serif",
19190
+ color: "#1f2937"
19191
+ }
19192
+ }
19193
+ )
19194
+ ]
19195
+ }
19196
+ );
19356
19197
  }
19357
19198
  function MediaStripToolbar({
19358
19199
  entry,
19359
19200
  index,
19360
19201
  pending,
19361
19202
  error,
19362
- itemEditOn,
19363
- onToggleItemEdit,
19364
19203
  onSetVariant,
19365
19204
  onSetAlign,
19366
19205
  onSetHover,
19367
19206
  onSetTreatment,
19368
19207
  onResize,
19369
- onOpenLink,
19208
+ onSetAlt,
19209
+ onSetLink,
19370
19210
  onAddItem,
19371
19211
  onRemoveItem,
19372
19212
  onChangeImage,
@@ -19375,102 +19215,81 @@ function MediaStripToolbar({
19375
19215
  }) {
19376
19216
  const items = mediaStripItems(entry.tree);
19377
19217
  const itemCount = items.length;
19218
+ const currentAlt = items[index]?.alt ?? "";
19219
+ const currentHref = items[index]?.href ?? "";
19220
+ const currentTarget = items[index]?.linkTarget === "same-tab" ? "same-tab" : "new-tab";
19378
19221
  const slots = findMediaStripSlots(entry.tree);
19379
19222
  const variant = slots?.variant === "reel" ? "reel" : "logos";
19380
19223
  const align = slots?.align === "left" || slots?.align === "right" ? slots.align : "center";
19381
19224
  const hover = slots?.hover === "none" || slots?.hover === "lift" || slots?.hover === "zoom" || slots?.hover === "fade" ? slots.hover : "lift";
19382
19225
  const treatment = slots?.treatment === "color" || slots?.treatment === "grayscale" ? slots.treatment : variant === "logos" ? "grayscale" : "color";
19383
19226
  const sizeValue = variant === "logos" ? typeof slots?.logoMaxHeight === "number" ? slots.logoMaxHeight : 48 : typeof slots?.cardWidth === "number" ? slots.cardWidth : 240;
19384
- const mobile = window.innerWidth <= 480;
19385
- const gatedLabel = (label) => itemEditOn ? label : "Turn on the star to edit this image";
19386
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
19227
+ const isMobile = window.innerWidth <= 480;
19228
+ const rowStyle = isMobile ? {
19229
+ display: "flex",
19230
+ gap: 6,
19231
+ flexWrap: "nowrap",
19232
+ justifyContent: "center",
19233
+ overflowX: "auto",
19234
+ WebkitOverflowScrolling: "touch",
19235
+ width: "100%",
19236
+ scrollbarWidth: "none"
19237
+ } : { display: "flex", gap: 6, flexWrap: "nowrap", justifyContent: "center" };
19238
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
19387
19239
  "div",
19388
19240
  {
19389
19241
  "data-ohw-bridge": "",
19390
19242
  "data-ohw-toolbar": "",
19391
19243
  onMouseDown: (e) => e.stopPropagation(),
19392
- className: cn("flex flex-col items-center gap-1.5 font-sans", pending && "pointer-events-none opacity-60"),
19393
- style: { maxWidth: mobile ? "calc(100vw - 24px)" : void 0 },
19244
+ style: {
19245
+ display: "flex",
19246
+ flexDirection: "column",
19247
+ gap: 6,
19248
+ alignItems: "center",
19249
+ background: OHW_BLUE,
19250
+ padding: 8,
19251
+ borderRadius: 12,
19252
+ boxShadow: "0 8px 24px rgba(0,0,0,0.25)",
19253
+ pointerEvents: pending ? "none" : "auto",
19254
+ opacity: pending ? 0.6 : 1,
19255
+ maxWidth: isMobile ? "calc(100vw - 24px)" : "none"
19256
+ },
19394
19257
  children: [
19395
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
19396
- "div",
19397
- {
19398
- className: cn(
19399
- "flex items-center gap-1 rounded-[10px] border border-border bg-background p-1 whitespace-nowrap",
19400
- "shadow-[0px_10px_15px_-3px_rgba(0,0,0,0.1),0px_4px_6px_-4px_rgba(0,0,0,0.1)]",
19401
- mobile ? "flex-wrap justify-center" : "flex-nowrap"
19402
- ),
19403
- children: [
19404
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Tooltip, { children: [
19405
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19406
- CustomToolbarButton,
19407
- {
19408
- type: "button",
19409
- "aria-label": itemEditOn ? "Done editing this image" : "Edit this image",
19410
- "aria-pressed": itemEditOn,
19411
- onClick: onToggleItemEdit,
19412
- className: "group size-8",
19413
- children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19414
- import_lucide_react9.Star,
19415
- {
19416
- size: 16,
19417
- fill: "currentColor",
19418
- strokeWidth: 0,
19419
- className: cn(
19420
- "transition-colors",
19421
- itemEditOn ? "text-[#f59e0b] group-hover:text-[#d97706]" : "text-[#d6d3d1] group-hover:text-[#fbbf24]"
19422
- ),
19423
- style: { pointerEvents: "none" }
19424
- }
19425
- )
19426
- }
19427
- ) }),
19428
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: itemEditOn ? "Done editing this image" : "Edit this image" })
19429
- ] }),
19430
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
19431
- /* @__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" } }) }),
19432
- /* @__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" } }) }),
19433
- /* @__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" } }) }),
19434
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19435
- ToolbarAction,
19436
- {
19437
- label: gatedLabel("Move right"),
19438
- onClick: onMoveRight,
19439
- disabled: !itemEditOn || index >= itemCount - 1,
19440
- children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.ArrowRight, { size: 16, style: { pointerEvents: "none" } })
19441
- }
19442
- ),
19443
- /* @__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" } }) }),
19444
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
19445
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(OptionMenu, { label: "Layout", value: variant, options: VARIANTS, onChange: onSetVariant }),
19446
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(OptionMenu, { label: "Hover", value: hover, options: HOVERS, onChange: onSetHover }),
19447
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19448
- ToolbarAction,
19449
- {
19450
- label: treatment === "grayscale" ? "Show in color" : "Show grayscale",
19451
- active: treatment === "grayscale",
19452
- onClick: () => onSetTreatment(treatment === "grayscale" ? "color" : "grayscale"),
19453
- children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Palette, { size: 16, style: { pointerEvents: "none" } })
19454
- }
19455
- ),
19456
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
19457
- 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)),
19458
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
19459
- /* @__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" } }) }),
19460
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("span", { className: "min-w-[40px] text-center text-xs tabular-nums text-muted-foreground", children: [
19461
- sizeValue,
19462
- "px"
19463
- ] }),
19464
- /* @__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" } }) }),
19465
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
19466
- /* @__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" } }) })
19467
- ]
19468
- }
19469
- ),
19470
- 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 })
19258
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: rowStyle, children: [
19259
+ VARIANTS.map((v) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: v.label, active: variant === v.value, onClick: () => onSetVariant(v.value) }, v.value)),
19260
+ ALIGNS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: a.label, active: align === a.value, onClick: () => onSetAlign(a.value) }, a.value))
19261
+ ] }),
19262
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: rowStyle, children: [
19263
+ HOVERS.map((h) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: h.label, active: hover === h.value, onClick: () => onSetHover(h.value) }, h.value)),
19264
+ TREATMENTS.map((t) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: t.label, active: treatment === t.value, onClick: () => onSetTreatment(t.value) }, t.value))
19265
+ ] }),
19266
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { display: "flex", gap: 6, alignItems: "center" }, children: [
19267
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: "\u2212", onClick: () => onResize(-8) }),
19268
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { style: { color: "#ffffff", fontSize: 12, minWidth: 34, textAlign: "center" }, children: [
19269
+ sizeValue,
19270
+ "px"
19271
+ ] }),
19272
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: "+", onClick: () => onResize(8) })
19273
+ ] }),
19274
+ /* @__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 }) }),
19275
+ /* @__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) }) }),
19276
+ currentHref && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: rowStyle, children: [
19277
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: "New tab", active: currentTarget === "new-tab", onClick: () => onSetLink(currentHref, "new-tab") }),
19278
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: "Same tab", active: currentTarget === "same-tab", onClick: () => onSetLink(currentHref, "same-tab") })
19279
+ ] }),
19280
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: rowStyle, children: [
19281
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: "Change Image", onClick: onChangeImage }),
19282
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: "+ Item", onClick: onAddItem }),
19283
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: "\u2212 Item", onClick: onRemoveItem, disabled: itemCount <= 1 })
19284
+ ] }),
19285
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: rowStyle, children: [
19286
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: "\u2190 Move", onClick: onMoveLeft, disabled: index <= 0 }),
19287
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Pill, { label: "\u2192 Move", onClick: onMoveRight, disabled: index >= itemCount - 1 })
19288
+ ] }),
19289
+ error && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
19471
19290
  ]
19472
19291
  }
19473
- ) });
19292
+ );
19474
19293
  }
19475
19294
  function findMediaStripSlots(tree) {
19476
19295
  const stack = tree.rows.flatMap((row) => row.blocks);
@@ -19489,23 +19308,31 @@ function AiMediaStripOverlay({
19489
19308
  }) {
19490
19309
  const [hover, setHover] = (0, import_react10.useState)(null);
19491
19310
  const [active, setActive] = (0, import_react10.useState)(null);
19492
- const [itemEditOn, setItemEditOn] = (0, import_react10.useState)(false);
19493
- const [linkOpen, setLinkOpen] = (0, import_react10.useState)(false);
19494
19311
  const [pending, setPending] = (0, import_react10.useState)(false);
19495
19312
  const [error, setError] = (0, import_react10.useState)(null);
19496
19313
  const activeRef = (0, import_react10.useRef)(null);
19497
19314
  activeRef.current = active;
19498
- const linkOpenRef = (0, import_react10.useRef)(false);
19499
- linkOpenRef.current = linkOpen;
19500
19315
  (0, import_react10.useEffect)(() => {
19501
19316
  const onMove = (e) => {
19502
- const ref = itemRefFromTarget(e.target);
19503
19317
  const t = e.target;
19504
- const overEditable = Boolean(t?.closest?.("[data-ohw-editable]"));
19505
- setHover((prev) => {
19506
- const next = ref && !overEditable ? ref : null;
19507
- return sameRef(prev, next) ? prev : next;
19508
- });
19318
+ if (!(t instanceof HTMLElement)) return;
19319
+ 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]")) {
19320
+ return;
19321
+ }
19322
+ const itemEl = t.closest("[data-ohw-media-strip-item]");
19323
+ if (!itemEl) {
19324
+ setHover(null);
19325
+ return;
19326
+ }
19327
+ const sectionEl = itemEl.closest("[data-ohw-section]");
19328
+ const unit = sectionEl ? movableUnit(sectionEl) : null;
19329
+ const sectionId = unit ? instanceIdOf(unit) || null : null;
19330
+ const index = Number(itemEl.dataset.ohwMediaStripIndex);
19331
+ if (!sectionId || Number.isNaN(index)) {
19332
+ setHover(null);
19333
+ return;
19334
+ }
19335
+ setHover({ sectionId, index });
19509
19336
  };
19510
19337
  const onLeave = () => setHover(null);
19511
19338
  document.addEventListener("mousemove", onMove, { passive: true });
@@ -19517,25 +19344,23 @@ function AiMediaStripOverlay({
19517
19344
  }, []);
19518
19345
  (0, import_react10.useEffect)(() => {
19519
19346
  const onClick = (e) => {
19347
+ const current = activeRef.current;
19348
+ if (!current) return;
19520
19349
  const t = e.target;
19521
- if (!(t instanceof Element)) return;
19522
- if (t.closest(CHROME_SELECTOR)) return;
19523
- const ref = itemRefFromTarget(t);
19524
- if (sameRef(activeRef.current, ref)) return;
19525
- if (ref) clearInteractionChromeRef.current();
19526
- setItemEditOn(false);
19350
+ if (!(t instanceof HTMLElement)) return;
19351
+ if (t.closest("[data-ohw-item-interaction]")) return;
19352
+ const itemEl = findMediaStripItemElement(current.sectionId, current.index);
19353
+ if (itemEl && itemEl.contains(t)) return;
19354
+ setActive(null);
19527
19355
  setError(null);
19528
- setActive(ref);
19529
19356
  };
19530
19357
  document.addEventListener("click", onClick, true);
19531
19358
  return () => document.removeEventListener("click", onClick, true);
19532
- }, [clearInteractionChromeRef]);
19359
+ }, []);
19533
19360
  (0, import_react10.useEffect)(() => {
19534
19361
  const onBlur = () => {
19535
19362
  if (!activeRef.current) return;
19536
19363
  setActive(null);
19537
- setItemEditOn(false);
19538
- setLinkOpen(false);
19539
19364
  setError(null);
19540
19365
  };
19541
19366
  window.addEventListener("blur", onBlur);
@@ -19545,10 +19370,7 @@ function AiMediaStripOverlay({
19545
19370
  const onKeyDown = (e) => {
19546
19371
  if (e.key !== "Escape") return;
19547
19372
  if (!activeRef.current) return;
19548
- if (linkOpenRef.current) return;
19549
- if (document.querySelector('[data-ohw-more-menu][data-state="open"]')) return;
19550
19373
  setActive(null);
19551
- setItemEditOn(false);
19552
19374
  setError(null);
19553
19375
  };
19554
19376
  document.addEventListener("keydown", onKeyDown);
@@ -19582,133 +19404,137 @@ function AiMediaStripOverlay({
19582
19404
  tree: nextTree,
19583
19405
  label: entry.label,
19584
19406
  media: extraMedia ? { ...entry.media, ...extraMedia } : entry.media,
19585
- ...entry.brand ? { brand: entry.brand } : {},
19586
- // The user is already looking at the strip they just edited — recentering the viewport
19587
- // on every edit reads as the section jumping around.
19588
- skipScrollIntoView: true
19407
+ ...entry.brand ? { brand: entry.brand } : {}
19589
19408
  });
19590
19409
  return true;
19591
19410
  },
19592
19411
  [findEntry, postToParent2, applyTreeLocalRef]
19593
19412
  );
19594
19413
  const activeEntry = active ? findEntry(active.sectionId) : null;
19414
+ const hoverHandleRect = useLiveItemRect(!active && hover ? hover : null);
19595
19415
  const activeItemRect = useLiveItemRect(active);
19596
- const activeStripRect = useLiveItemRect(active, true);
19597
- const hoverRect = useLiveItemRect(hover && !sameRef(hover, active) ? hover : null);
19598
- const activeItems = activeEntry ? mediaStripItems(activeEntry.tree) : [];
19599
- const activeItem = active ? activeItems[active.index] : void 0;
19600
- const activeHref = activeItem?.href ?? "";
19601
- const activeTarget = activeItem?.linkTarget === "same-tab" ? "same-tab" : "new-tab";
19602
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
19603
- hoverRect && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ItemInteractionLayer, { rect: hoverRect, state: "hover" }),
19604
- activeItemRect && activeStripRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
19605
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ItemInteractionLayer, { rect: activeItemRect, state: "active-top" }),
19606
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(StripToolbarSlot, { stripRect: activeStripRect, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19607
- MediaStripToolbar,
19608
- {
19609
- entry: activeEntry,
19610
- index: active.index,
19611
- pending,
19612
- error,
19613
- itemEditOn,
19614
- onToggleItemEdit: () => setItemEditOn((on) => !on),
19615
- onSetVariant: (variant) => runMutation(active.sectionId, (tree) => setMediaStripVariant(tree, variant)),
19616
- onSetAlign: (align) => runMutation(active.sectionId, (tree) => setMediaStripAlign(tree, align)),
19617
- onSetHover: (hoverPreset) => runMutation(active.sectionId, (tree) => setMediaStripHover(tree, hoverPreset)),
19618
- onSetTreatment: (treatment) => runMutation(active.sectionId, (tree) => setMediaStripTreatment(tree, treatment)),
19619
- onResize: (delta) => {
19620
- const slots = findMediaStripSlots(activeEntry.tree);
19621
- const variant = slots?.variant === "reel" ? "reel" : "logos";
19622
- const current = variant === "logos" ? typeof slots?.logoMaxHeight === "number" ? slots.logoMaxHeight : 48 : typeof slots?.cardWidth === "number" ? slots.cardWidth : 240;
19623
- void runMutation(active.sectionId, (tree) => setMediaStripSizing(tree, current + delta));
19624
- },
19625
- onOpenLink: () => setLinkOpen(true),
19626
- onAddItem: () => {
19627
- void requestAssetPick(postToParent2).then((picked) => {
19628
- if (!picked) return;
19629
- const item = { assetId: picked.assetId, alt: "Logo" };
19416
+ const TOOLBAR_APPROX_H = 170;
19417
+ const toolbarState = activeItemRect && activeItemRect.top < TOOLBAR_APPROX_H ? "active-bottom" : "active-top";
19418
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
19419
+ hoverHandleRect && hover && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
19420
+ "button",
19421
+ {
19422
+ type: "button",
19423
+ "data-ohw-toolbar": "",
19424
+ onClick: () => {
19425
+ clearInteractionChromeRef.current();
19426
+ setActive(hover);
19427
+ },
19428
+ "aria-label": "Configure media item",
19429
+ style: {
19430
+ position: "fixed",
19431
+ top: hoverHandleRect.top + 8,
19432
+ left: hoverHandleRect.left + hoverHandleRect.width - 36,
19433
+ width: 28,
19434
+ height: 28,
19435
+ borderRadius: 8,
19436
+ border: `1.5px dashed ${PRIMARY3}`,
19437
+ background: "#ffffff",
19438
+ color: PRIMARY3,
19439
+ display: "flex",
19440
+ alignItems: "center",
19441
+ justifyContent: "center",
19442
+ cursor: "pointer",
19443
+ zIndex: 2147483643,
19444
+ fontSize: 14,
19445
+ fontWeight: 700
19446
+ },
19447
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react8.Star, { size: 14, style: { pointerEvents: "none" } })
19448
+ }
19449
+ ),
19450
+ activeItemRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
19451
+ ItemInteractionLayer,
19452
+ {
19453
+ rect: activeItemRect,
19454
+ state: toolbarState,
19455
+ toolbar: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
19456
+ MediaStripToolbar,
19457
+ {
19458
+ entry: activeEntry,
19459
+ index: active.index,
19460
+ pending,
19461
+ error,
19462
+ onSetVariant: (variant) => runMutation(active.sectionId, (tree) => setMediaStripVariant(tree, variant)),
19463
+ onSetAlign: (align) => runMutation(active.sectionId, (tree) => setMediaStripAlign(tree, align)),
19464
+ onSetHover: (hoverPreset) => runMutation(active.sectionId, (tree) => setMediaStripHover(tree, hoverPreset)),
19465
+ onSetTreatment: (treatment) => runMutation(active.sectionId, (tree) => setMediaStripTreatment(tree, treatment)),
19466
+ onResize: (delta) => {
19467
+ const slots = findMediaStripSlots(activeEntry.tree);
19468
+ const variant = slots?.variant === "reel" ? "reel" : "logos";
19469
+ const current = variant === "logos" ? typeof slots?.logoMaxHeight === "number" ? slots.logoMaxHeight : 48 : typeof slots?.cardWidth === "number" ? slots.cardWidth : 240;
19470
+ void runMutation(active.sectionId, (tree) => setMediaStripSizing(tree, current + delta));
19471
+ },
19472
+ onSetAlt: (alt) => {
19473
+ void runMutation(active.sectionId, (tree) => setMediaStripItemAlt(tree, active.index, alt));
19474
+ },
19475
+ onSetLink: (href, linkTarget) => {
19630
19476
  void runMutation(
19631
19477
  active.sectionId,
19632
- (tree) => addMediaStripItem(tree, item, active.index),
19633
- { [picked.assetId]: picked.url }
19634
- ).then((applied) => {
19635
- if (applied) setActive({ sectionId: active.sectionId, index: active.index + 1 });
19478
+ (tree) => setMediaStripItemLink(tree, active.index, href ? { href, linkTarget } : {})
19479
+ );
19480
+ },
19481
+ onAddItem: () => {
19482
+ void requestAssetPick(postToParent2).then((picked) => {
19483
+ if (!picked) return;
19484
+ const item = { assetId: picked.assetId, alt: "Logo" };
19485
+ void runMutation(
19486
+ active.sectionId,
19487
+ (tree) => addMediaStripItem(tree, item, active.index),
19488
+ { [picked.assetId]: picked.url }
19489
+ ).then((applied) => {
19490
+ if (applied) setActive({ sectionId: active.sectionId, index: active.index + 1 });
19491
+ });
19636
19492
  });
19637
- });
19638
- },
19639
- onChangeImage: () => {
19640
- void requestAssetPick(postToParent2).then((picked) => {
19641
- if (!picked) return;
19642
- void runMutation(
19643
- active.sectionId,
19644
- (tree) => setMediaStripItemAsset(tree, active.index, picked.assetId),
19645
- { [picked.assetId]: picked.url }
19493
+ },
19494
+ onChangeImage: () => {
19495
+ void requestAssetPick(postToParent2).then((picked) => {
19496
+ if (!picked) return;
19497
+ void runMutation(
19498
+ active.sectionId,
19499
+ (tree) => setMediaStripItemAsset(tree, active.index, picked.assetId),
19500
+ { [picked.assetId]: picked.url }
19501
+ );
19502
+ });
19503
+ },
19504
+ onRemoveItem: () => {
19505
+ void runMutation(active.sectionId, (tree) => removeMediaStripItem(tree, active.index)).then(
19506
+ (applied) => {
19507
+ if (applied) setActive(null);
19508
+ }
19646
19509
  );
19647
- });
19648
- },
19649
- onRemoveItem: () => {
19650
- void runMutation(active.sectionId, (tree) => removeMediaStripItem(tree, active.index)).then(
19651
- (applied) => {
19652
- if (applied) setActive(null);
19653
- }
19654
- );
19655
- },
19656
- onMoveLeft: () => {
19657
- void runMutation(active.sectionId, (tree) => reorderMediaStripItem(tree, active.index, -1)).then(
19658
- (applied) => {
19659
- if (applied) setActive({ sectionId: active.sectionId, index: active.index - 1 });
19660
- }
19661
- );
19662
- },
19663
- onMoveRight: () => {
19664
- void runMutation(active.sectionId, (tree) => reorderMediaStripItem(tree, active.index, 1)).then(
19665
- (applied) => {
19666
- if (applied) setActive({ sectionId: active.sectionId, index: active.index + 1 });
19667
- }
19668
- );
19669
- }
19670
- }
19671
- ) }),
19672
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19673
- "div",
19674
- {
19675
- "data-ohw-bridge": "",
19676
- "data-ohw-item-interaction": "",
19677
- "aria-hidden": true,
19678
- className: "pointer-events-none fixed rounded-[4px] px-[7px] py-[3px] font-sans text-[11px] font-medium leading-none text-primary-foreground whitespace-nowrap",
19679
- style: {
19680
- top: activeItemRect.top + activeItemRect.height + 10,
19681
- left: activeItemRect.left - 5,
19682
- background: PRIMARY3,
19683
- zIndex: 2147483644
19684
- },
19685
- children: "Media item"
19686
- }
19687
- ),
19688
- linkOpen && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19689
- LinkModal,
19690
- {
19691
- href: activeHref,
19692
- linkTarget: activeTarget,
19693
- onClose: () => setLinkOpen(false),
19694
- onSave: (href, linkTarget) => {
19695
- void runMutation(
19696
- active.sectionId,
19697
- (tree) => setMediaStripItemLink(tree, active.index, href ? { href, linkTarget } : {})
19698
- );
19510
+ },
19511
+ onMoveLeft: () => {
19512
+ void runMutation(active.sectionId, (tree) => reorderMediaStripItem(tree, active.index, -1)).then(
19513
+ (applied) => {
19514
+ if (applied) setActive({ sectionId: active.sectionId, index: active.index - 1 });
19515
+ }
19516
+ );
19517
+ },
19518
+ onMoveRight: () => {
19519
+ void runMutation(active.sectionId, (tree) => reorderMediaStripItem(tree, active.index, 1)).then(
19520
+ (applied) => {
19521
+ if (applied) setActive({ sectionId: active.sectionId, index: active.index + 1 });
19522
+ }
19523
+ );
19524
+ }
19699
19525
  }
19700
- }
19701
- )
19702
- ] })
19526
+ )
19527
+ }
19528
+ )
19703
19529
  ] });
19704
19530
  }
19705
19531
 
19706
19532
  // src/ui/ai-section/AiPricingCardOverlay.tsx
19707
19533
  var import_react11 = require("react");
19708
- var import_lucide_react10 = require("lucide-react");
19709
- var import_jsx_runtime22 = require("react/jsx-runtime");
19534
+ var import_lucide_react9 = require("lucide-react");
19535
+ var import_jsx_runtime21 = require("react/jsx-runtime");
19710
19536
  var PRIMARY4 = "var(--ohw-primary, #0885FE)";
19711
- 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]";
19537
+ 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]";
19712
19538
  function findPricingCardElement(sectionId, index) {
19713
19539
  const sectionEl = findByInstanceId(sectionId);
19714
19540
  if (!sectionEl) return null;
@@ -19720,7 +19546,7 @@ function findPricingCardElement(sectionId, index) {
19720
19546
  }
19721
19547
  function cardRefFromTarget(target) {
19722
19548
  if (!(target instanceof Element)) return null;
19723
- if (target.closest(CHROME_SELECTOR2)) return null;
19549
+ if (target.closest(CHROME_SELECTOR)) return null;
19724
19550
  const cardEl = target.closest("[data-ohw-pricing-card]");
19725
19551
  if (!cardEl) return null;
19726
19552
  const sectionEl = cardEl.closest("[data-ohw-section]");
@@ -19730,21 +19556,21 @@ function cardRefFromTarget(target) {
19730
19556
  if (!sectionId || Number.isNaN(index)) return null;
19731
19557
  return { sectionId, index };
19732
19558
  }
19733
- function sameRef2(a, b) {
19559
+ function sameRef(a, b) {
19734
19560
  return a?.sectionId === b?.sectionId && a?.index === b?.index;
19735
19561
  }
19736
- function rectOf2(el) {
19562
+ function rectOf(el) {
19737
19563
  if (!el) return null;
19738
19564
  const r2 = el.getBoundingClientRect();
19739
19565
  if (r2.width <= 0 || r2.height <= 0) return null;
19740
19566
  return { top: r2.top, left: r2.left, width: r2.width, height: r2.height };
19741
19567
  }
19742
19568
  function readRect3(ref) {
19743
- return rectOf2(ref && findPricingCardElement(ref.sectionId, ref.index));
19569
+ return rectOf(ref && findPricingCardElement(ref.sectionId, ref.index));
19744
19570
  }
19745
19571
  function readGridRect(ref) {
19746
19572
  const card = ref && findPricingCardElement(ref.sectionId, ref.index);
19747
- return rectOf2(card?.closest("[data-ohw-pricing-collection]") ?? card);
19573
+ return rectOf(card?.closest("[data-ohw-pricing-collection]") ?? card);
19748
19574
  }
19749
19575
  function useLiveCardRect(ref, grid = false) {
19750
19576
  const [rect, setRect] = (0, import_react11.useState)(null);
@@ -19774,7 +19600,7 @@ function useLiveCardRect(ref, grid = false) {
19774
19600
  }
19775
19601
  var TOOLBAR_GRID_GAP = 52;
19776
19602
  var TOOLBAR_GRID_GAP_MOBILE = 42;
19777
- var TOOLBAR_EDGE_MARGIN3 = 4;
19603
+ var TOOLBAR_EDGE_MARGIN2 = 4;
19778
19604
  function GridToolbarSlot({ gridRect, children }) {
19779
19605
  const slotRef = (0, import_react11.useRef)(null);
19780
19606
  const [shift, setShift] = (0, import_react11.useState)(0);
@@ -19786,7 +19612,7 @@ function GridToolbarSlot({ gridRect, children }) {
19786
19612
  const w = slot.offsetWidth;
19787
19613
  if (w <= 0) return;
19788
19614
  const half = w / 2;
19789
- const clamped = Math.max(TOOLBAR_EDGE_MARGIN3 + half, Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN3 - half));
19615
+ const clamped = Math.max(TOOLBAR_EDGE_MARGIN2 + half, Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN2 - half));
19790
19616
  setShift((prev) => Math.abs(prev - (clamped - centerX)) < 0.5 ? prev : clamped - centerX);
19791
19617
  };
19792
19618
  clamp();
@@ -19795,7 +19621,7 @@ function GridToolbarSlot({ gridRect, children }) {
19795
19621
  return () => ro.disconnect();
19796
19622
  }, [centerX, children]);
19797
19623
  const mobile = window.innerWidth <= 480;
19798
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19624
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19799
19625
  "div",
19800
19626
  {
19801
19627
  ref: slotRef,
@@ -19830,7 +19656,7 @@ function requestValidateTree2(postToParent2, tree) {
19830
19656
  });
19831
19657
  }
19832
19658
  function StyleSwatch({ style }) {
19833
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19659
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19834
19660
  "span",
19835
19661
  {
19836
19662
  "aria-hidden": true,
@@ -19844,35 +19670,35 @@ function StyleSwatch({ style }) {
19844
19670
  );
19845
19671
  }
19846
19672
  function ButtonGlyph() {
19847
- 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: [
19848
- /* @__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" }),
19849
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M5.5 6H12.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
19673
+ 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: [
19674
+ /* @__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" }),
19675
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("path", { d: "M5.5 6H12.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
19850
19676
  ] });
19851
19677
  }
19852
19678
  function FeatureGlyph({ mode }) {
19853
- 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: [
19854
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("circle", { cx: "8", cy: "8", r: "7.25", stroke: "currentColor", strokeWidth: "1.5" }),
19855
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M5 8.2L7.2 10.3L11 6", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }),
19856
- /* @__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" }),
19857
- /* @__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" }),
19858
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M11.5 13.5H15.5", stroke: PRIMARY4, strokeWidth: "1.5", strokeLinecap: "round" }),
19859
- mode === "add" && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M13.5 11.5V15.5", stroke: PRIMARY4, strokeWidth: "1.5", strokeLinecap: "round" })
19679
+ 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: [
19680
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("circle", { cx: "8", cy: "8", r: "7.25", stroke: "currentColor", strokeWidth: "1.5" }),
19681
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("path", { d: "M5 8.2L7.2 10.3L11 6", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }),
19682
+ /* @__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" }),
19683
+ /* @__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" }),
19684
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("path", { d: "M11.5 13.5H15.5", stroke: PRIMARY4, strokeWidth: "1.5", strokeLinecap: "round" }),
19685
+ mode === "add" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("path", { d: "M13.5 11.5V15.5", stroke: PRIMARY4, strokeWidth: "1.5", strokeLinecap: "round" })
19860
19686
  ] });
19861
19687
  }
19862
- function ToolbarAction2({
19688
+ function ToolbarAction({
19863
19689
  label,
19864
19690
  onClick,
19865
19691
  disabled = false,
19866
19692
  children
19867
19693
  }) {
19868
- const button = /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(CustomToolbarButton, { type: "button", "aria-label": label, disabled, onClick, className: "size-8", children });
19869
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Tooltip, { children: [
19870
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipTrigger, { asChild: true, children: disabled ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "inline-flex", children: button }) : button }),
19871
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: label })
19694
+ const button = /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(CustomToolbarButton, { type: "button", "aria-label": label, disabled, onClick, className: "size-8", children });
19695
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Tooltip, { children: [
19696
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipTrigger, { asChild: true, children: disabled ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "inline-flex", children: button }) : button }),
19697
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: label })
19872
19698
  ] });
19873
19699
  }
19874
- function ToolbarSeparator2() {
19875
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { "aria-hidden": true, className: "mx-0.5 block h-6 w-px shrink-0 bg-border" });
19700
+ function ToolbarSeparator() {
19701
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { "aria-hidden": true, className: "mx-0.5 block h-6 w-px shrink-0 bg-border" });
19876
19702
  }
19877
19703
  function CardStyleMenu({
19878
19704
  value,
@@ -19880,7 +19706,7 @@ function CardStyleMenu({
19880
19706
  disabled = false
19881
19707
  }) {
19882
19708
  const current = PRICING_CARD_STYLES.find((s) => s.value === value) ?? PRICING_CARD_STYLES[0];
19883
- const trigger = /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
19709
+ const trigger = /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
19884
19710
  "button",
19885
19711
  {
19886
19712
  type: "button",
@@ -19891,28 +19717,28 @@ function CardStyleMenu({
19891
19717
  disabled && "cursor-not-allowed opacity-40 hover:bg-transparent"
19892
19718
  ),
19893
19719
  children: [
19894
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(StyleSwatch, { style: current.value }),
19895
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "flex-1 text-left", children: current.label }),
19896
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react10.ChevronDown, { size: 16, style: { pointerEvents: "none" } })
19720
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(StyleSwatch, { style: current.value }),
19721
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "flex-1 text-left", children: current.label }),
19722
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.ChevronDown, { size: 16, style: { pointerEvents: "none" } })
19897
19723
  ]
19898
19724
  }
19899
19725
  );
19900
19726
  if (disabled) {
19901
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
19902
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "text-xs text-muted-foreground opacity-50", children: "Card style" }),
19903
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Tooltip, { children: [
19904
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "inline-flex", children: trigger }) }),
19905
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: "Mark as featured to edit card style" })
19727
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
19728
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-xs text-muted-foreground opacity-50", children: "Card style" }),
19729
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Tooltip, { children: [
19730
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "inline-flex", children: trigger }) }),
19731
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: "Mark as featured to edit card style" })
19906
19732
  ] })
19907
19733
  ] });
19908
19734
  }
19909
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
19910
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "text-xs text-muted-foreground", children: "Card style" }),
19911
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(DropdownMenu, { modal: false, children: [
19912
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(DropdownMenuTrigger, { asChild: true, children: trigger }),
19913
- /* @__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) => {
19735
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
19736
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-xs text-muted-foreground", children: "Card style" }),
19737
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(DropdownMenu, { modal: false, children: [
19738
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(DropdownMenuTrigger, { asChild: true, children: trigger }),
19739
+ /* @__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) => {
19914
19740
  const selected = style.value === value;
19915
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
19741
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
19916
19742
  DropdownMenuItem,
19917
19743
  {
19918
19744
  role: "menuitemradio",
@@ -19920,9 +19746,9 @@ function CardStyleMenu({
19920
19746
  onSelect: () => onChange(style.value),
19921
19747
  className: cn("grid grid-cols-[18px_1fr_16px] gap-2 py-2 pl-2 pr-2 text-[13px]", selected && "bg-muted"),
19922
19748
  children: [
19923
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(StyleSwatch, { style: style.value }),
19924
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { children: style.label }),
19925
- selected && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react10.Check, { size: 16, className: "text-bridge-primary" })
19749
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(StyleSwatch, { style: style.value }),
19750
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { children: style.label }),
19751
+ selected && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Check, { size: 16, className: "text-bridge-primary" })
19926
19752
  ]
19927
19753
  },
19928
19754
  style.value
@@ -19952,7 +19778,7 @@ function PricingCardToolbar({
19952
19778
  const style = pricingCardStyle(entry.tree, index);
19953
19779
  const featuredTag = pricingCardFeaturedTag(entry.tree, index);
19954
19780
  const mobile = window.innerWidth <= 480;
19955
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
19781
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
19956
19782
  "div",
19957
19783
  {
19958
19784
  "data-ohw-bridge": "",
@@ -19964,7 +19790,7 @@ function PricingCardToolbar({
19964
19790
  ),
19965
19791
  style: { maxWidth: mobile ? "calc(100vw - 24px)" : void 0 },
19966
19792
  children: [
19967
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
19793
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
19968
19794
  "div",
19969
19795
  {
19970
19796
  className: cn(
@@ -19973,8 +19799,8 @@ function PricingCardToolbar({
19973
19799
  mobile ? "flex-wrap justify-center" : "flex-nowrap"
19974
19800
  ),
19975
19801
  children: [
19976
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Tooltip, { children: [
19977
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19802
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Tooltip, { children: [
19803
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19978
19804
  CustomToolbarButton,
19979
19805
  {
19980
19806
  type: "button",
@@ -19982,8 +19808,8 @@ function PricingCardToolbar({
19982
19808
  "aria-pressed": featuredTag,
19983
19809
  onClick: () => onSetFeaturedTag(!featuredTag),
19984
19810
  className: "group size-8",
19985
- children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19986
- import_lucide_react10.Star,
19811
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
19812
+ import_lucide_react9.Star,
19987
19813
  {
19988
19814
  size: 16,
19989
19815
  fill: "currentColor",
@@ -19997,25 +19823,25 @@ function PricingCardToolbar({
19997
19823
  )
19998
19824
  }
19999
19825
  ) }),
20000
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: featuredTag ? "Unmark featured" : "Mark as featured" })
19826
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(TooltipContent, { side: "bottom", sideOffset: 8, children: featuredTag ? "Unmark featured" : "Mark as featured" })
20001
19827
  ] }),
20002
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarSeparator2, {}),
20003
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(CardStyleMenu, { value: style, onChange: onSetStyle, disabled: !featuredTag }),
20004
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarSeparator2, {}),
20005
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarAction2, { label: "Add button", onClick: onAddButton, disabled: buttonCount >= 2, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ButtonGlyph, {}) }),
20006
- /* @__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: [
20007
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ButtonGlyph, {}),
20008
- /* @__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" })
19828
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
19829
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(CardStyleMenu, { value: style, onChange: onSetStyle, disabled: !featuredTag }),
19830
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
19831
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarAction, { label: "Add button", onClick: onAddButton, disabled: buttonCount >= 2, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ButtonGlyph, {}) }),
19832
+ /* @__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: [
19833
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ButtonGlyph, {}),
19834
+ /* @__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" })
20009
19835
  ] }) }),
20010
- /* @__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" }) }),
20011
- /* @__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" }) }),
20012
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ToolbarSeparator2, {}),
20013
- /* @__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" } }) }),
20014
- /* @__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" } }) })
19836
+ /* @__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" }) }),
19837
+ /* @__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" }) }),
19838
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ToolbarSeparator, {}),
19839
+ /* @__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" } }) }),
19840
+ /* @__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" } }) })
20015
19841
  ]
20016
19842
  }
20017
19843
  ),
20018
- 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 })
19844
+ 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 })
20019
19845
  ]
20020
19846
  }
20021
19847
  ) });
@@ -20049,7 +19875,7 @@ function AiPricingCardOverlay({
20049
19875
  const overEditable = Boolean(t?.closest?.("[data-ohw-editable], [data-ohw-role], a, button"));
20050
19876
  setHover((prev) => {
20051
19877
  const next = ref && !overEditable ? ref : null;
20052
- return sameRef2(prev, next) ? prev : next;
19878
+ return sameRef(prev, next) ? prev : next;
20053
19879
  });
20054
19880
  };
20055
19881
  const onLeave = () => setHover(null);
@@ -20064,10 +19890,10 @@ function AiPricingCardOverlay({
20064
19890
  const onClick = (e) => {
20065
19891
  const t = e.target;
20066
19892
  if (!(t instanceof Element)) return;
20067
- if (t.closest(CHROME_SELECTOR2)) return;
19893
+ if (t.closest(CHROME_SELECTOR)) return;
20068
19894
  const ref = cardRefFromTarget(t);
20069
- setActive((prev) => sameRef2(prev, ref) ? prev : ref);
20070
- if (!ref || !sameRef2(activeRef.current, ref)) setError(null);
19895
+ setActive((prev) => sameRef(prev, ref) ? prev : ref);
19896
+ if (!ref || !sameRef(activeRef.current, ref)) setError(null);
20071
19897
  };
20072
19898
  document.addEventListener("click", onClick, true);
20073
19899
  return () => document.removeEventListener("click", onClick, true);
@@ -20132,12 +19958,12 @@ function AiPricingCardOverlay({
20132
19958
  const activeEntry = active ? findEntry(active.sectionId) : null;
20133
19959
  const activeCardRect = useLiveCardRect(active);
20134
19960
  const activeGridRect = useLiveCardRect(active, true);
20135
- const hoverRect = useLiveCardRect(hover && !sameRef2(hover, active) ? hover : null);
20136
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
20137
- hoverRect && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ItemInteractionLayer, { rect: hoverRect, state: "hover" }),
20138
- activeCardRect && activeGridRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
20139
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ItemInteractionLayer, { rect: activeCardRect, state: "active-top" }),
20140
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(GridToolbarSlot, { gridRect: activeGridRect, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19961
+ const hoverRect = useLiveCardRect(hover && !sameRef(hover, active) ? hover : null);
19962
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
19963
+ hoverRect && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ItemInteractionLayer, { rect: hoverRect, state: "hover" }),
19964
+ activeCardRect && activeGridRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
19965
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ItemInteractionLayer, { rect: activeCardRect, state: "active-top" }),
19966
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(GridToolbarSlot, { gridRect: activeGridRect, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
20141
19967
  PricingCardToolbar,
20142
19968
  {
20143
19969
  entry: activeEntry,
@@ -20165,7 +19991,7 @@ function AiPricingCardOverlay({
20165
19991
  onRemoveFeature: () => runMutation(active.sectionId, (tree) => removePricingCardFeature(tree, active.index))
20166
19992
  }
20167
19993
  ) }),
20168
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
19994
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
20169
19995
  "div",
20170
19996
  {
20171
19997
  "data-ohw-bridge": "",
@@ -20187,7 +20013,7 @@ function AiPricingCardOverlay({
20187
20013
 
20188
20014
  // src/ui/ai-section/AiVideoOverlay.tsx
20189
20015
  var import_react12 = require("react");
20190
- var import_lucide_react11 = require("lucide-react");
20016
+ var import_lucide_react10 = require("lucide-react");
20191
20017
 
20192
20018
  // src/lib/ai-video-tree.ts
20193
20019
  function resolveVideoBlock(tree, path) {
@@ -20258,10 +20084,10 @@ function extractYouTubeId(input) {
20258
20084
  }
20259
20085
 
20260
20086
  // src/ui/ai-section/AiVideoOverlay.tsx
20261
- var import_jsx_runtime23 = require("react/jsx-runtime");
20087
+ var import_jsx_runtime22 = require("react/jsx-runtime");
20262
20088
  var PRIMARY5 = "#0885FE";
20263
- var OHW_BLUE = `var(--ohw-primary, ${PRIMARY5})`;
20264
- var OHW_BLUE_DARK = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY5}) 60%, #000000)`;
20089
+ var OHW_BLUE2 = `var(--ohw-primary, ${PRIMARY5})`;
20090
+ var OHW_BLUE_DARK2 = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY5}) 60%, #000000)`;
20265
20091
  function findVideoBlockElement(sectionId, path) {
20266
20092
  const sectionEl = findByInstanceId(sectionId);
20267
20093
  if (!sectionEl) return null;
@@ -20321,12 +20147,12 @@ function requestValidateTree3(postToParent2, tree) {
20321
20147
  postToParent2({ type: "ow:ai-validate-tree-request", id, tree });
20322
20148
  });
20323
20149
  }
20324
- function Pill({
20150
+ function Pill2({
20325
20151
  label,
20326
20152
  onClick,
20327
20153
  disabled
20328
20154
  }) {
20329
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20155
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
20330
20156
  "button",
20331
20157
  {
20332
20158
  type: "button",
@@ -20342,7 +20168,7 @@ function Pill({
20342
20168
  border: "none",
20343
20169
  cursor: disabled ? "default" : "pointer",
20344
20170
  background: "#ffffff",
20345
- color: OHW_BLUE_DARK,
20171
+ color: OHW_BLUE_DARK2,
20346
20172
  opacity: disabled ? 0.4 : 1,
20347
20173
  fontSize: 12,
20348
20174
  lineHeight: "16px",
@@ -20364,7 +20190,7 @@ function VideoUrlInput({ value, onCommit }) {
20364
20190
  setDraft(value ? watchUrl(value) : "");
20365
20191
  setInvalid(false);
20366
20192
  }, [value]);
20367
- const commit2 = () => {
20193
+ const commit = () => {
20368
20194
  const next = draft.trim();
20369
20195
  if (!next) {
20370
20196
  setInvalid(false);
@@ -20385,8 +20211,8 @@ function VideoUrlInput({ value, onCommit }) {
20385
20211
  onCommit(id);
20386
20212
  }
20387
20213
  };
20388
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 4, width: "100%", maxWidth: 280 }, children: [
20389
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
20214
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 4, width: "100%", maxWidth: 280 }, children: [
20215
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
20390
20216
  "div",
20391
20217
  {
20392
20218
  style: {
@@ -20402,12 +20228,12 @@ function VideoUrlInput({ value, onCommit }) {
20402
20228
  boxShadow: invalid ? "inset 0 0 0 1.5px #EF4444" : "none"
20403
20229
  },
20404
20230
  children: [
20405
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20231
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
20406
20232
  "span",
20407
20233
  {
20408
20234
  style: {
20409
20235
  flex: "0 0 auto",
20410
- color: OHW_BLUE_DARK,
20236
+ color: OHW_BLUE_DARK2,
20411
20237
  fontSize: 10,
20412
20238
  lineHeight: "16px",
20413
20239
  fontWeight: 700,
@@ -20417,14 +20243,14 @@ function VideoUrlInput({ value, onCommit }) {
20417
20243
  children: "Video"
20418
20244
  }
20419
20245
  ),
20420
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20246
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
20421
20247
  "input",
20422
20248
  {
20423
20249
  type: "text",
20424
20250
  inputMode: "url",
20425
20251
  value: draft,
20426
20252
  onChange: (e) => handleChange(e.target.value),
20427
- onBlur: commit2,
20253
+ onBlur: commit,
20428
20254
  onKeyDown: (e) => {
20429
20255
  if (e.key === "Enter") e.currentTarget.blur();
20430
20256
  if (e.key === "Escape") {
@@ -20454,7 +20280,7 @@ function VideoUrlInput({ value, onCommit }) {
20454
20280
  ]
20455
20281
  }
20456
20282
  ),
20457
- 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." })
20283
+ 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." })
20458
20284
  ] });
20459
20285
  }
20460
20286
  function VideoToolbar({
@@ -20464,7 +20290,7 @@ function VideoToolbar({
20464
20290
  onSetVideoId,
20465
20291
  onRemove
20466
20292
  }) {
20467
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
20293
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
20468
20294
  "div",
20469
20295
  {
20470
20296
  "data-ohw-bridge": "",
@@ -20476,7 +20302,7 @@ function VideoToolbar({
20476
20302
  gap: 6,
20477
20303
  alignItems: "center",
20478
20304
  width: 280,
20479
- background: OHW_BLUE,
20305
+ background: OHW_BLUE2,
20480
20306
  padding: 8,
20481
20307
  borderRadius: 12,
20482
20308
  boxShadow: "0 8px 24px rgba(0,0,0,0.25)",
@@ -20484,9 +20310,9 @@ function VideoToolbar({
20484
20310
  opacity: pending ? 0.6 : 1
20485
20311
  },
20486
20312
  children: [
20487
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(VideoUrlInput, { value: videoId, onCommit: onSetVideoId }),
20488
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Pill, { label: "Remove video", onClick: onRemove, disabled: !videoId }),
20489
- error && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
20313
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(VideoUrlInput, { value: videoId, onCommit: onSetVideoId }),
20314
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Pill2, { label: "Remove video", onClick: onRemove, disabled: !videoId }),
20315
+ error && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
20490
20316
  ]
20491
20317
  }
20492
20318
  );
@@ -20606,8 +20432,8 @@ function AiVideoOverlay({
20606
20432
  const activeBlockRect = useLiveBlockRect(active);
20607
20433
  const TOOLBAR_APPROX_H = 90;
20608
20434
  const toolbarState = activeBlockRect && activeBlockRect.top < TOOLBAR_APPROX_H ? "active-bottom" : "active-top";
20609
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
20610
- hoverHandleRect && hover && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20435
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
20436
+ hoverHandleRect && hover && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
20611
20437
  "button",
20612
20438
  {
20613
20439
  type: "button",
@@ -20635,15 +20461,15 @@ function AiVideoOverlay({
20635
20461
  fontSize: 14,
20636
20462
  fontWeight: 700
20637
20463
  },
20638
- children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react11.Star, { size: 14, style: { pointerEvents: "none" } })
20464
+ children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react10.Star, { size: 14, style: { pointerEvents: "none" } })
20639
20465
  }
20640
20466
  ),
20641
- activeBlockRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20467
+ activeBlockRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
20642
20468
  ItemInteractionLayer,
20643
20469
  {
20644
20470
  rect: activeBlockRect,
20645
20471
  state: toolbarState,
20646
- toolbar: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20472
+ toolbar: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
20647
20473
  VideoToolbar,
20648
20474
  {
20649
20475
  videoId: videoIdAt(activeEntry.tree, active.path),
@@ -20667,7 +20493,7 @@ function AiVideoOverlay({
20667
20493
 
20668
20494
  // src/ui/ai-section/AiFadeCarouselOverlay.tsx
20669
20495
  var import_react13 = require("react");
20670
- var import_lucide_react12 = require("lucide-react");
20496
+ var import_lucide_react11 = require("lucide-react");
20671
20497
 
20672
20498
  // src/lib/ai-carousel-tree.ts
20673
20499
  function isCarouselBlock(node) {
@@ -20718,10 +20544,10 @@ function setCarouselAutoplay(tree, on) {
20718
20544
  }
20719
20545
 
20720
20546
  // src/ui/ai-section/AiFadeCarouselOverlay.tsx
20721
- var import_jsx_runtime24 = require("react/jsx-runtime");
20547
+ var import_jsx_runtime23 = require("react/jsx-runtime");
20722
20548
  var PRIMARY6 = "#0885FE";
20723
- var OHW_BLUE2 = `var(--ohw-primary, ${PRIMARY6})`;
20724
- var OHW_BLUE_DARK2 = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY6}) 60%, #000000)`;
20549
+ var OHW_BLUE3 = `var(--ohw-primary, ${PRIMARY6})`;
20550
+ var OHW_BLUE_DARK3 = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY6}) 60%, #000000)`;
20725
20551
  function findCarouselItemElement(sectionId, index) {
20726
20552
  const sectionEl = findByInstanceId(sectionId);
20727
20553
  if (!sectionEl) return null;
@@ -20800,8 +20626,8 @@ function requestAssetPick2(postToParent2) {
20800
20626
  postToParent2({ type: "ow:asset-pick", id });
20801
20627
  });
20802
20628
  }
20803
- function Pill2({ label, onClick, disabled }) {
20804
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
20629
+ function Pill3({ label, onClick, disabled }) {
20630
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20805
20631
  "button",
20806
20632
  {
20807
20633
  type: "button",
@@ -20817,7 +20643,7 @@ function Pill2({ label, onClick, disabled }) {
20817
20643
  border: "none",
20818
20644
  cursor: disabled ? "default" : "pointer",
20819
20645
  background: "#ffffff",
20820
- color: OHW_BLUE_DARK2,
20646
+ color: OHW_BLUE_DARK3,
20821
20647
  opacity: disabled ? 0.4 : 1,
20822
20648
  fontSize: 12,
20823
20649
  lineHeight: "16px",
@@ -20835,7 +20661,7 @@ function TogglePill({
20835
20661
  onToggle,
20836
20662
  disabled
20837
20663
  }) {
20838
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
20664
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
20839
20665
  "button",
20840
20666
  {
20841
20667
  type: "button",
@@ -20854,7 +20680,7 @@ function TogglePill({
20854
20680
  border: "none",
20855
20681
  cursor: disabled ? "default" : "pointer",
20856
20682
  background: "#ffffff",
20857
- color: OHW_BLUE_DARK2,
20683
+ color: OHW_BLUE_DARK3,
20858
20684
  opacity: disabled ? 0.4 : 1,
20859
20685
  fontSize: 12,
20860
20686
  lineHeight: "16px",
@@ -20864,7 +20690,7 @@ function TogglePill({
20864
20690
  },
20865
20691
  children: [
20866
20692
  label,
20867
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
20693
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20868
20694
  "span",
20869
20695
  {
20870
20696
  "aria-hidden": true,
@@ -20877,10 +20703,10 @@ function TogglePill({
20877
20703
  // Off is a grey track, not a washed-out blue: the pill's own white background already
20878
20704
  // carries the brand colour in the text, and a pale blue track next to it read as
20879
20705
  // "disabled control" rather than "switch that is off".
20880
- background: on ? OHW_BLUE_DARK2 : "#CBD5E1",
20706
+ background: on ? OHW_BLUE_DARK3 : "#CBD5E1",
20881
20707
  transition: "background 120ms ease"
20882
20708
  },
20883
- children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
20709
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
20884
20710
  "span",
20885
20711
  {
20886
20712
  style: {
@@ -20909,7 +20735,7 @@ function CarouselSlideToolbar({
20909
20735
  onChangeImage,
20910
20736
  onToggleAutoplay
20911
20737
  }) {
20912
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
20738
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
20913
20739
  "div",
20914
20740
  {
20915
20741
  "data-ohw-bridge": "",
@@ -20920,7 +20746,7 @@ function CarouselSlideToolbar({
20920
20746
  flexDirection: "column",
20921
20747
  gap: 6,
20922
20748
  alignItems: "center",
20923
- background: OHW_BLUE2,
20749
+ background: OHW_BLUE3,
20924
20750
  padding: 8,
20925
20751
  borderRadius: 12,
20926
20752
  boxShadow: "0 8px 24px rgba(0,0,0,0.25)",
@@ -20932,11 +20758,11 @@ function CarouselSlideToolbar({
20932
20758
  pointerEvents: pending ? "none" : "auto"
20933
20759
  },
20934
20760
  children: [
20935
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { display: "flex", gap: 6, alignItems: "center" }, children: [
20936
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Pill2, { label: "Change Image", onClick: onChangeImage }),
20937
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(TogglePill, { label: "Autoplay", on: autoplay, onToggle: onToggleAutoplay })
20761
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { style: { display: "flex", gap: 6, alignItems: "center" }, children: [
20762
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Pill3, { label: "Change Image", onClick: onChangeImage }),
20763
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(TogglePill, { label: "Autoplay", on: autoplay, onToggle: onToggleAutoplay })
20938
20764
  ] }),
20939
- error && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
20765
+ error && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
20940
20766
  ]
20941
20767
  }
20942
20768
  );
@@ -21076,8 +20902,8 @@ function AiFadeCarouselOverlay({
21076
20902
  const activeItemRect = useLiveItemRect2(active);
21077
20903
  const TOOLBAR_APPROX_H = 90;
21078
20904
  const toolbarState = activeItemRect && activeItemRect.top < TOOLBAR_APPROX_H ? "active-bottom" : "active-top";
21079
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
21080
- hoverHandleRect && hover && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
20905
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
20906
+ hoverHandleRect && hover && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
21081
20907
  "button",
21082
20908
  {
21083
20909
  type: "button",
@@ -21106,10 +20932,10 @@ function AiFadeCarouselOverlay({
21106
20932
  fontSize: 14,
21107
20933
  fontWeight: 700
21108
20934
  },
21109
- children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react12.Star, { size: 14, style: { pointerEvents: "none" } })
20935
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react11.Star, { size: 14, style: { pointerEvents: "none" } })
21110
20936
  }
21111
20937
  ),
21112
- swapping && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
20938
+ swapping && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
21113
20939
  "div",
21114
20940
  {
21115
20941
  ref: skeletonRef,
@@ -21127,15 +20953,15 @@ function AiFadeCarouselOverlay({
21127
20953
  zIndex: 2147483646,
21128
20954
  pointerEvents: "none"
21129
20955
  },
21130
- children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("style", { children: MEDIA_SKELETON_CSS })
20956
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("style", { children: MEDIA_SKELETON_CSS })
21131
20957
  }
21132
20958
  ),
21133
- activeItemRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
20959
+ activeItemRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
21134
20960
  ItemInteractionLayer,
21135
20961
  {
21136
20962
  rect: activeItemRect,
21137
20963
  state: toolbarState,
21138
- toolbar: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
20964
+ toolbar: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
21139
20965
  CarouselSlideToolbar,
21140
20966
  {
21141
20967
  pending,
@@ -21353,6 +21179,113 @@ function scrollToHashSectionWhenReady(behavior = "smooth") {
21353
21179
  // src/ui/link-modal/LinkPopover.tsx
21354
21180
  var import_react17 = require("react");
21355
21181
 
21182
+ // src/ui/dialog.tsx
21183
+ var React9 = __toESM(require("react"), 1);
21184
+ var import_radix_ui6 = require("radix-ui");
21185
+ var import_lucide_react12 = require("lucide-react");
21186
+ var import_jsx_runtime24 = require("react/jsx-runtime");
21187
+ function Dialog2({
21188
+ ...props
21189
+ }) {
21190
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_radix_ui6.Dialog.Root, { "data-slot": "dialog", ...props });
21191
+ }
21192
+ function DialogPortal({
21193
+ ...props
21194
+ }) {
21195
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_radix_ui6.Dialog.Portal, { ...props });
21196
+ }
21197
+ function DialogOverlay({
21198
+ className,
21199
+ ...props
21200
+ }) {
21201
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
21202
+ import_radix_ui6.Dialog.Overlay,
21203
+ {
21204
+ "data-slot": "dialog-overlay",
21205
+ "data-ohw-link-modal-root": "",
21206
+ className: cn("fixed inset-0 z-[2147483646] bg-black/50", className),
21207
+ ...props
21208
+ }
21209
+ );
21210
+ }
21211
+ var DialogContent = React9.forwardRef(
21212
+ ({ className, children, showCloseButton = true, container, ...props }, ref) => {
21213
+ const positionMode = container ? "absolute" : "fixed";
21214
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(DialogPortal, { container: container ?? void 0, children: [
21215
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(DialogOverlay, { className: cn(positionMode, "inset-0") }),
21216
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
21217
+ import_radix_ui6.Dialog.Content,
21218
+ {
21219
+ ref,
21220
+ "data-slot": "dialog-content",
21221
+ className: cn(
21222
+ positionMode,
21223
+ "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",
21224
+ "rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
21225
+ container ? "max-h-[calc(100%-32px)]" : "max-h-[calc(100vh-32px)]",
21226
+ className
21227
+ ),
21228
+ onMouseDown: (e) => e.stopPropagation(),
21229
+ ...props,
21230
+ children: [
21231
+ children,
21232
+ showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
21233
+ import_radix_ui6.Dialog.Close,
21234
+ {
21235
+ type: "button",
21236
+ className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
21237
+ "aria-label": "Close",
21238
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react12.X, { size: 16, "aria-hidden": true })
21239
+ }
21240
+ ) : null
21241
+ ]
21242
+ }
21243
+ )
21244
+ ] });
21245
+ }
21246
+ );
21247
+ DialogContent.displayName = import_radix_ui6.Dialog.Content.displayName;
21248
+ function DialogHeader({
21249
+ className,
21250
+ ...props
21251
+ }) {
21252
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: cn("flex flex-col gap-1.5", className), ...props });
21253
+ }
21254
+ function DialogFooter({
21255
+ className,
21256
+ ...props
21257
+ }) {
21258
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
21259
+ "div",
21260
+ {
21261
+ className: cn("flex items-center justify-end gap-2", className),
21262
+ ...props
21263
+ }
21264
+ );
21265
+ }
21266
+ var DialogTitle = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
21267
+ import_radix_ui6.Dialog.Title,
21268
+ {
21269
+ ref,
21270
+ className: cn(
21271
+ "text-lg font-semibold leading-none tracking-tight text-card-foreground",
21272
+ className
21273
+ ),
21274
+ ...props
21275
+ }
21276
+ ));
21277
+ DialogTitle.displayName = import_radix_ui6.Dialog.Title.displayName;
21278
+ var DialogDescription = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
21279
+ import_radix_ui6.Dialog.Description,
21280
+ {
21281
+ ref,
21282
+ className: cn("text-sm text-muted-foreground", className),
21283
+ ...props
21284
+ }
21285
+ ));
21286
+ DialogDescription.displayName = import_radix_ui6.Dialog.Description.displayName;
21287
+ var DialogClose = import_radix_ui6.Dialog.Close;
21288
+
21356
21289
  // src/ui/link-modal/LinkEditorPanel.tsx
21357
21290
  var import_lucide_react16 = require("lucide-react");
21358
21291
 
@@ -22456,7 +22389,6 @@ function shouldUseDevFixtures() {
22456
22389
  function getLogoElement(el) {
22457
22390
  const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
22458
22391
  if (marked) return marked;
22459
- if (el.closest('[data-ohw-editable="icon"]')) return null;
22460
22392
  const root = el.closest("nav, [data-ohw-nav-root], footer");
22461
22393
  if (!root) return null;
22462
22394
  const anchor = el.closest("a");
@@ -24151,102 +24083,6 @@ function deleteSelectedNavFooterItem(deps) {
24151
24083
  return false;
24152
24084
  }
24153
24085
 
24154
- // src/lib/nav-page-sync.ts
24155
- function labelKeyFor(hrefKey) {
24156
- return hrefKey.replace(/-href$/, "-label");
24157
- }
24158
- function isPageLinkKey(key) {
24159
- return isNavbarHrefKey(key) || isFooterHrefKey(key);
24160
- }
24161
- function currentHref(key, anchor, content) {
24162
- const stored = content[key];
24163
- if (typeof stored === "string" && stored.length > 0) return stored;
24164
- return anchor.getAttribute("href") ?? "";
24165
- }
24166
- function normalizePath2(value) {
24167
- if (value.length > 1 && value.endsWith("/")) return value.slice(0, -1);
24168
- return value;
24169
- }
24170
- function pointsAt(href, path) {
24171
- const normalized = normalizePath2(href);
24172
- const target = normalizePath2(path);
24173
- if (normalized === target) return true;
24174
- return (normalized.startsWith(`${target}#`) || normalized.startsWith(`${target}?`)) && target !== "/";
24175
- }
24176
- function retargetHref(href, oldPath, newPath) {
24177
- const normalized = normalizePath2(href);
24178
- const target = normalizePath2(oldPath);
24179
- if (normalized === target) return newPath;
24180
- return `${newPath}${normalized.slice(target.length)}`;
24181
- }
24182
- function setLabelText(key, text) {
24183
- document.querySelectorAll(`[data-ohw-key="${CSS.escape(key)}"]`).forEach((el) => {
24184
- el.textContent = text;
24185
- });
24186
- }
24187
- function commit(delta, deps) {
24188
- if (Object.keys(delta).length === 0) return;
24189
- deps.setEditContent({ ...deps.getEditContent(), ...delta });
24190
- deps.postToParent({
24191
- type: "ow:change",
24192
- nodes: Object.entries(delta).map(([key, text]) => ({ key, text }))
24193
- });
24194
- }
24195
- function navLinksTo(path, content) {
24196
- return Array.from(document.querySelectorAll("[data-ohw-href-key]")).filter((anchor) => {
24197
- const key = anchor.getAttribute("data-ohw-href-key");
24198
- return isNavbarHrefKey(key) && pointsAt(currentHref(key, anchor, content), path);
24199
- }).map((anchor) => anchor.getAttribute("data-ohw-href-key"));
24200
- }
24201
- function addNavLinkForPage(page, deps) {
24202
- const content = deps.getEditContent();
24203
- if (navLinksTo(page.path, content).length > 0) return false;
24204
- const roots = listNavbarRootItems();
24205
- const afterAnchor = roots[roots.length - 1] ?? null;
24206
- let inserted;
24207
- try {
24208
- inserted = insertNavbarItem(page.path, page.title, afterAnchor);
24209
- } catch {
24210
- return false;
24211
- }
24212
- const { hrefKey, labelKey, index } = inserted;
24213
- deps.applyLinkByKey(hrefKey, page.path);
24214
- setLabelText(labelKey, page.title);
24215
- commit(
24216
- {
24217
- [hrefKey]: page.path,
24218
- [labelKey]: page.title,
24219
- [NAV_COUNT_KEY]: String(index + 1),
24220
- [NAV_ORDER_KEY]: getNavbarOrderJson()
24221
- },
24222
- deps
24223
- );
24224
- return true;
24225
- }
24226
- function retargetPageLinks(change, deps) {
24227
- const { oldPath, newPath, oldTitle, newTitle } = change;
24228
- if (!oldPath || !newPath || normalizePath2(oldPath) === normalizePath2(newPath)) return 0;
24229
- const content = deps.getEditContent();
24230
- const delta = {};
24231
- for (const anchor of Array.from(document.querySelectorAll("[data-ohw-href-key]"))) {
24232
- const hrefKey = anchor.getAttribute("data-ohw-href-key");
24233
- if (!isPageLinkKey(hrefKey)) continue;
24234
- const href = currentHref(hrefKey, anchor, content);
24235
- if (!pointsAt(href, oldPath)) continue;
24236
- const nextHref = retargetHref(href, oldPath, newPath);
24237
- deps.applyLinkByKey(hrefKey, nextHref);
24238
- delta[hrefKey] = nextHref;
24239
- if (!oldTitle || !newTitle) continue;
24240
- const labelKey = labelKeyFor(hrefKey);
24241
- const label = content[labelKey] ?? anchor.querySelector('[data-ohw-editable="text"]')?.textContent ?? "";
24242
- if (label.trim() !== oldTitle) continue;
24243
- setLabelText(labelKey, newTitle);
24244
- delta[labelKey] = newTitle;
24245
- }
24246
- commit(delta, deps);
24247
- return Object.keys(delta).filter((key) => key.endsWith("-href")).length;
24248
- }
24249
-
24250
24086
  // src/ui/navbar-container-chrome.tsx
24251
24087
  var import_lucide_react21 = require("lucide-react");
24252
24088
  var import_jsx_runtime37 = require("react/jsx-runtime");
@@ -25134,7 +24970,7 @@ function getNavigationSelectionParent(el) {
25134
24970
  if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
25135
24971
  return resolveFooterLinksContainerFrom(el);
25136
24972
  }
25137
- 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)) {
24973
+ if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
25138
24974
  return getNavigationRoot(el);
25139
24975
  }
25140
24976
  return null;
@@ -25356,6 +25192,7 @@ var ICONS = {
25356
25192
  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"/>',
25357
25193
  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"/>'
25358
25194
  };
25195
+ var HEIGHT_SETTLE_DELAYS = [150, 400, 800];
25359
25196
  var SELECTION_CHROME_GAP2 = 4;
25360
25197
  var TOOLBAR_STROKE_GAP2 = 4;
25361
25198
  var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
@@ -25453,7 +25290,7 @@ function applyVisibleViewport(el, parentScroll) {
25453
25290
  el.style.top = `${top}px`;
25454
25291
  el.style.height = `${height}px`;
25455
25292
  }
25456
- var TOOLBAR_EDGE_MARGIN4 = 4;
25293
+ var TOOLBAR_EDGE_MARGIN3 = 4;
25457
25294
  function clampToolbarToClip(top, transform, rect, clip, approxH, gap) {
25458
25295
  const isAbove = transform.includes("translateY(-100%)");
25459
25296
  let visualTop = isAbove ? top - approxH : top;
@@ -25480,8 +25317,8 @@ function clampToolbarToClip(top, transform, rect, clip, approxH, gap) {
25480
25317
  function clampToolbarLeft(centerX, toolbarWidth) {
25481
25318
  const half = toolbarWidth / 2;
25482
25319
  return Math.max(
25483
- TOOLBAR_EDGE_MARGIN4 + half,
25484
- Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN4 - half)
25320
+ TOOLBAR_EDGE_MARGIN3 + half,
25321
+ Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN3 - half)
25485
25322
  );
25486
25323
  }
25487
25324
  function calcToolbarPos(rect, parentScroll, toolbarW = 306) {
@@ -26361,6 +26198,21 @@ function OhhwellsBridge() {
26361
26198
  const [isItemDragging, setIsItemDragging] = (0, import_react21.useState)(false);
26362
26199
  const [isFooterFrameSelection, setIsFooterFrameSelection] = (0, import_react21.useState)(false);
26363
26200
  isFooterFrameSelectionRef.current = isFooterFrameSelection;
26201
+ const [floatingPanel, setFloatingPanel] = (0, import_react21.useState)(null);
26202
+ const floatingPanelOpenRef = (0, import_react21.useRef)(false);
26203
+ floatingPanelOpenRef.current = floatingPanel !== null;
26204
+ const floatingPanelRef = (0, import_react21.useRef)(null);
26205
+ floatingPanelRef.current = floatingPanel;
26206
+ const [floatingPanelPos, setFloatingPanelPos] = (0, import_react21.useState)(null);
26207
+ const buttonCollectionDragRef = (0, import_react21.useRef)(null);
26208
+ const buttonDropInsertIndexRef = (0, import_react21.useRef)(null);
26209
+ const [buttonDropSlots, setButtonDropSlots] = (0, import_react21.useState)([]);
26210
+ const [activeButtonDropIndex, setActiveButtonDropIndex] = (0, import_react21.useState)(null);
26211
+ const buttonPointerDragRef = (0, import_react21.useRef)(null);
26212
+ const [buttonStyleTick, setButtonStyleTick] = (0, import_react21.useState)(0);
26213
+ const [logoSizeDraft, setLogoSizeDraft] = (0, import_react21.useState)(null);
26214
+ const [editorViewport, setEditorViewport] = (0, import_react21.useState)("desktop");
26215
+ const [parentScrollSnap, setParentScrollSnap] = (0, import_react21.useState)(null);
26364
26216
  const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = (0, import_react21.useState)(null);
26365
26217
  const [footerHeadingVisible, setFooterHeadingVisible] = (0, import_react21.useState)(null);
26366
26218
  const footerDragRef = (0, import_react21.useRef)(null);
@@ -26386,21 +26238,6 @@ function OhhwellsBridge() {
26386
26238
  const stylesRef = (0, import_react21.useRef)("");
26387
26239
  const appliedDrawerRef = (0, import_react21.useRef)(null);
26388
26240
  const pendingDeleteUndoRef = (0, import_react21.useRef)(null);
26389
- const [floatingPanel, setFloatingPanel] = (0, import_react21.useState)(null);
26390
- const floatingPanelOpenRef = (0, import_react21.useRef)(false);
26391
- const floatingPanelRef = (0, import_react21.useRef)(null);
26392
- floatingPanelRef.current = floatingPanel;
26393
- const setFloatingPanelRef = (0, import_react21.useRef)(setFloatingPanel);
26394
- const [floatingPanelPos, setFloatingPanelPos] = (0, import_react21.useState)(null);
26395
- const buttonCollectionDragRef = (0, import_react21.useRef)(null);
26396
- const buttonDropInsertIndexRef = (0, import_react21.useRef)(null);
26397
- const [buttonDropSlots, setButtonDropSlots] = (0, import_react21.useState)([]);
26398
- const [activeButtonDropIndex, setActiveButtonDropIndex] = (0, import_react21.useState)(null);
26399
- const buttonPointerDragRef = (0, import_react21.useRef)(null);
26400
- const [buttonStyleTick, setButtonStyleTick] = (0, import_react21.useState)(0);
26401
- const [logoSizeDraft, setLogoSizeDraft] = (0, import_react21.useState)(null);
26402
- const [editorViewport, setEditorViewport] = (0, import_react21.useState)("desktop");
26403
- const [parentScrollSnap, setParentScrollSnap] = (0, import_react21.useState)(null);
26404
26241
  const [sitePages, setSitePages] = (0, import_react21.useState)([]);
26405
26242
  const collectionItemsRef = (0, import_react21.useRef)(/* @__PURE__ */ new Map());
26406
26243
  const [sectionsByPath, setSectionsByPath] = (0, import_react21.useState)({});
@@ -26410,18 +26247,7 @@ function OhhwellsBridge() {
26410
26247
  const linkPopoverOpenRef = (0, import_react21.useRef)(false);
26411
26248
  const linkPopoverGraceUntilRef = (0, import_react21.useRef)(0);
26412
26249
  setLinkPopoverRef.current = setLinkPopover;
26413
- setFloatingPanelRef.current = setFloatingPanel;
26414
26250
  linkPopoverSessionRef.current = linkPopover;
26415
- floatingPanelOpenRef.current = Boolean(floatingPanel);
26416
- (0, import_react21.useEffect)(() => {
26417
- const syncViewport = () => {
26418
- const next = window.innerWidth <= 480 ? "mobile" : "desktop";
26419
- setEditorViewport((prev) => prev === next ? prev : next);
26420
- };
26421
- syncViewport();
26422
- window.addEventListener("resize", syncViewport);
26423
- return () => window.removeEventListener("resize", syncViewport);
26424
- }, []);
26425
26251
  (0, import_react21.useEffect)(() => {
26426
26252
  setSectionOrderPersistHandler((entries) => {
26427
26253
  const currentPath = window.location.pathname;
@@ -26570,43 +26396,6 @@ function OhhwellsBridge() {
26570
26396
  if (!useFixtures) postToParent2({ type: "ow:request-site-pages" });
26571
26397
  return () => window.removeEventListener("message", onSitePages);
26572
26398
  }, [isEditMode, postToParent2]);
26573
- (0, import_react21.useEffect)(() => {
26574
- if (!isEditMode) return;
26575
- const deps = {
26576
- getEditContent: () => editContentRef.current,
26577
- setEditContent: (next) => {
26578
- editContentRef.current = next;
26579
- },
26580
- applyLinkByKey,
26581
- postToParent: postToParent2
26582
- };
26583
- const onPageMessage = (e) => {
26584
- if (e.data?.type === "ow:page-created") {
26585
- const { path, title } = e.data;
26586
- if (typeof path !== "string" || !path) return;
26587
- if (addNavLinkForPage({ path, title: typeof title === "string" && title ? title : path }, deps)) {
26588
- enforceLinkHrefs();
26589
- }
26590
- return;
26591
- }
26592
- if (e.data?.type === "ow:page-renamed") {
26593
- const { oldPath, newPath, oldTitle, newTitle } = e.data;
26594
- if (typeof oldPath !== "string" || typeof newPath !== "string") return;
26595
- const moved = retargetPageLinks(
26596
- {
26597
- oldPath,
26598
- newPath,
26599
- oldTitle: typeof oldTitle === "string" ? oldTitle : void 0,
26600
- newTitle: typeof newTitle === "string" ? newTitle : void 0
26601
- },
26602
- deps
26603
- );
26604
- if (moved > 0) enforceLinkHrefs();
26605
- }
26606
- };
26607
- window.addEventListener("message", onPageMessage);
26608
- return () => window.removeEventListener("message", onPageMessage);
26609
- }, [isEditMode, postToParent2]);
26610
26399
  (0, import_react21.useEffect)(() => {
26611
26400
  if (!isEditMode || shouldUseDevFixtures()) return;
26612
26401
  void loadAllSectionsManifest().then((manifest) => {
@@ -28155,7 +27944,6 @@ function OhhwellsBridge() {
28155
27944
  applyAiFooterFromContent(content);
28156
27945
  if (typeof content[STYLE_STORE_KEY] === "string") {
28157
27946
  stylesRef.current = content[STYLE_STORE_KEY];
28158
- document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
28159
27947
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
28160
27948
  }
28161
27949
  applyBrandChrome(content);
@@ -28166,12 +27954,12 @@ function OhhwellsBridge() {
28166
27954
  if (key === "__ohw_sections") continue;
28167
27955
  if (key === AI_SECTIONS_KEY) continue;
28168
27956
  if (key === AI_FOOTER_KEY) continue;
28169
- if (key === LOGO_PLACEHOLDER_KEY) continue;
28170
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
28171
27957
  if (key === BRAND_KIT_KEY) continue;
28172
27958
  if (key === STYLE_STORE_KEY) continue;
28173
27959
  if (BRAND_CHROME_KEYS.has(key)) continue;
27960
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
28174
27961
  if (key === CUSTOM_CODE_AFTER_HEAD_KEY || key === CUSTOM_CODE_BEFORE_HEAD_KEY) continue;
27962
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
28175
27963
  if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
28176
27964
  continue;
28177
27965
  }
@@ -28402,18 +28190,17 @@ function OhhwellsBridge() {
28402
28190
  }
28403
28191
  applyAiFooterFromContent(content);
28404
28192
  if (typeof content[STYLE_STORE_KEY] === "string") {
28405
- document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
28406
28193
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
28407
28194
  }
28408
28195
  for (const [key, val] of Object.entries(content)) {
28409
28196
  if (key === "__ohw_sections") continue;
28410
28197
  if (key === AI_SECTIONS_KEY) continue;
28411
28198
  if (key === AI_FOOTER_KEY) continue;
28412
- if (key === LOGO_PLACEHOLDER_KEY) continue;
28413
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
28414
28199
  if (key === BRAND_KIT_KEY) continue;
28415
28200
  if (key === STYLE_STORE_KEY) continue;
28416
28201
  if (BRAND_CHROME_KEYS.has(key)) continue;
28202
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
28203
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
28417
28204
  if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
28418
28205
  continue;
28419
28206
  }
@@ -28680,11 +28467,26 @@ function OhhwellsBridge() {
28680
28467
  const t2 = setTimeout(measure, 500);
28681
28468
  const ro = new ResizeObserver(schedule);
28682
28469
  ro.observe(document.body);
28470
+ let lastWidth = window.innerWidth;
28471
+ let resizeTimers = [];
28472
+ const clearResizeTimers = () => {
28473
+ resizeTimers.forEach(clearTimeout);
28474
+ resizeTimers = [];
28475
+ };
28476
+ const handleResize = () => {
28477
+ if (window.innerWidth === lastWidth) return;
28478
+ lastWidth = window.innerWidth;
28479
+ clearResizeTimers();
28480
+ resizeTimers = HEIGHT_SETTLE_DELAYS.map((delay) => setTimeout(measure, delay));
28481
+ };
28482
+ window.addEventListener("resize", handleResize);
28683
28483
  return () => {
28684
28484
  clearTimeout(t1);
28685
28485
  clearTimeout(t2);
28686
28486
  if (raf != null) cancelAnimationFrame(raf);
28687
28487
  ro.disconnect();
28488
+ clearResizeTimers();
28489
+ window.removeEventListener("resize", handleResize);
28688
28490
  };
28689
28491
  }, [pathname, isEditMode, postToParent2]);
28690
28492
  (0, import_react21.useEffect)(() => {
@@ -28981,13 +28783,6 @@ function OhhwellsBridge() {
28981
28783
  if (target.closest("[data-ohw-state-toggle]")) return;
28982
28784
  if (target.closest("[data-ohw-max-badge]")) return;
28983
28785
  if (isInsideLinkEditor(target)) return;
28984
- if (isInsideFloatingPanel(target)) return;
28985
- if (target.closest("[data-ohw-form-toolbar]")) return;
28986
- if (target.closest(
28987
- '[data-ohw-field-toolbar], [data-ohw-field-type-picker], [data-radix-popper-content-wrapper], [role="menu"], [data-slot="dropdown-menu-content"]'
28988
- )) {
28989
- return;
28990
- }
28991
28786
  if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
28992
28787
  clearMediaSelectionRef.current();
28993
28788
  }
@@ -29011,6 +28806,8 @@ function OhhwellsBridge() {
29011
28806
  return;
29012
28807
  }
29013
28808
  }
28809
+ if (isInsideFloatingPanel(target)) return;
28810
+ if (target.closest("[data-ohw-form-toolbar]")) return;
29014
28811
  const collectionItem = matchCollectionItemClick(target, collectionItemsRef.current);
29015
28812
  if (collectionItem) {
29016
28813
  e.preventDefault();
@@ -29022,6 +28819,11 @@ function OhhwellsBridge() {
29022
28819
  });
29023
28820
  return;
29024
28821
  }
28822
+ if (target.closest(
28823
+ '[data-ohw-field-toolbar], [data-ohw-field-type-picker], [data-radix-popper-content-wrapper], [role="menu"], [data-slot="dropdown-menu-content"]'
28824
+ )) {
28825
+ return;
28826
+ }
29025
28827
  {
29026
28828
  const formEl = getFormElement(target);
29027
28829
  const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
@@ -29175,13 +28977,9 @@ function OhhwellsBridge() {
29175
28977
  if (isIconEditable(editable) && !getSocialItem(editable)) {
29176
28978
  e.preventDefault();
29177
28979
  e.stopPropagation();
28980
+ selectIconElementRef.current(editable);
29178
28981
  const aiIcon = editable.hasAttribute("data-ai-icon");
29179
28982
  const featureIcon = aiIcon && /\.features\.\d+\.icon$/.test(editable.dataset.ohwKey ?? "");
29180
- if (aiIcon) {
29181
- aiSectionApiRef.current?.selectFromElement(editable);
29182
- } else {
29183
- selectIconElementRef.current(editable);
29184
- }
29185
28983
  postToParentRef.current({
29186
28984
  type: "ow:icon-pick",
29187
28985
  key: editable.dataset.ohwKey ?? "",
@@ -29409,9 +29207,6 @@ function OhhwellsBridge() {
29409
29207
  setHoveredItemRect(null);
29410
29208
  hoveredNavContainerRef.current = null;
29411
29209
  setHoveredNavContainerRect(null);
29412
- siblingHintElRef.current = null;
29413
- setSiblingHintRect(null);
29414
- setSiblingHintRects([]);
29415
29210
  return;
29416
29211
  }
29417
29212
  {
@@ -29532,6 +29327,7 @@ function OhhwellsBridge() {
29532
29327
  hoveredNavContainerRef.current = null;
29533
29328
  setHoveredNavContainerRect(null);
29534
29329
  hoveredItemElRef.current = editable;
29330
+ setHoveredItemRect(isSelectedForHoverRef.current(editable) ? null : editable.getBoundingClientRect());
29535
29331
  }
29536
29332
  }
29537
29333
  }
@@ -30241,11 +30037,7 @@ function OhhwellsBridge() {
30241
30037
  };
30242
30038
  const handleMouseMove = (e) => {
30243
30039
  const { clientX, clientY } = e;
30244
- if (pointOwnedByFloatingPanel(clientX, clientY)) {
30245
- dismissImageHover();
30246
- clearImageHover();
30247
- return;
30248
- }
30040
+ if (pointOwnedByFloatingPanel(clientX, clientY)) return;
30249
30041
  if (isNavDrawerOpen() && isPointOutsideOpenDrawer(clientX, clientY)) {
30250
30042
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
30251
30043
  formHoverElRef.current = null;
@@ -30274,12 +30066,7 @@ function OhhwellsBridge() {
30274
30066
  setHoveredItemRect(null);
30275
30067
  hoveredNavContainerRef.current = null;
30276
30068
  setHoveredNavContainerRect(null);
30277
- siblingHintElRef.current = null;
30278
- setSiblingHintRect(null);
30279
- setSiblingHintRects([]);
30280
30069
  dismissImageHover();
30281
- clearImageHover();
30282
- setSectionGap(null);
30283
30070
  return;
30284
30071
  }
30285
30072
  if (probeSocialsRowAt(clientX, clientY)) return;
@@ -30291,11 +30078,7 @@ function OhhwellsBridge() {
30291
30078
  if (e.data?.type !== "ow:pointer-sync") return;
30292
30079
  const { clientX, clientY } = e.data;
30293
30080
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
30294
- if (pointOwnedByFloatingPanel(clientX, clientY)) {
30295
- dismissImageHover();
30296
- clearImageHover();
30297
- return;
30298
- }
30081
+ if (pointOwnedByFloatingPanel(clientX, clientY)) return;
30299
30082
  if (isNavDrawerOpen() && isPointOutsideOpenDrawer(clientX, clientY)) return;
30300
30083
  if (probeSocialsRowAt(clientX, clientY)) return;
30301
30084
  probeSectionGapAt(clientX, clientY);
@@ -30675,7 +30458,6 @@ function OhhwellsBridge() {
30675
30458
  applyAiFooterFromContent(content);
30676
30459
  if (typeof content[STYLE_STORE_KEY] === "string") {
30677
30460
  stylesRef.current = content[STYLE_STORE_KEY];
30678
- document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
30679
30461
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
30680
30462
  }
30681
30463
  applyBrandChrome(content);
@@ -30690,12 +30472,12 @@ function OhhwellsBridge() {
30690
30472
  }
30691
30473
  if (key === AI_SECTIONS_KEY) continue;
30692
30474
  if (key === AI_FOOTER_KEY) continue;
30693
- if (key === LOGO_PLACEHOLDER_KEY) continue;
30694
- if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
30695
30475
  if (key === BRAND_KIT_KEY) continue;
30696
30476
  if (key === STYLE_STORE_KEY) continue;
30697
30477
  if (BRAND_CHROME_KEYS.has(key)) continue;
30478
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
30698
30479
  if (key === CUSTOM_CODE_AFTER_HEAD_KEY || key === CUSTOM_CODE_BEFORE_HEAD_KEY) continue;
30480
+ if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
30699
30481
  if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
30700
30482
  continue;
30701
30483
  }
@@ -31331,12 +31113,6 @@ function OhhwellsBridge() {
31331
31113
  closeLinkPopoverRef.current();
31332
31114
  return;
31333
31115
  }
31334
- if (floatingPanelOpenRef.current) {
31335
- setFloatingPanelRef.current(null);
31336
- deselectRef.current();
31337
- deactivateRef.current();
31338
- return;
31339
- }
31340
31116
  deselectRef.current();
31341
31117
  deactivateRef.current();
31342
31118
  clearMediaSelectionRef.current();
@@ -32172,10 +31948,10 @@ function OhhwellsBridge() {
32172
31948
  window.removeEventListener("message", handleDuplicateSection);
32173
31949
  window.removeEventListener("message", handleDeactivate);
32174
31950
  window.removeEventListener("message", handleSelectNav);
31951
+ document.documentElement.removeAttribute("data-ohw-panel-dragging");
32175
31952
  window.removeEventListener("message", handleToastAction);
32176
31953
  window.removeEventListener("message", handleFormCount);
32177
31954
  window.removeEventListener("message", handleUiEscape);
32178
- document.documentElement.removeAttribute("data-ohw-panel-dragging");
32179
31955
  autoSaveTimers.current.forEach(clearTimeout);
32180
31956
  autoSaveTimers.current.clear();
32181
31957
  if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
@@ -32469,7 +32245,7 @@ function OhhwellsBridge() {
32469
32245
  postToParent2({
32470
32246
  type: "ow:ready",
32471
32247
  version: "1",
32472
- bridgeVersion: "0.1.158-next.424",
32248
+ bridgeVersion: "0.1.159",
32473
32249
  path: pathname,
32474
32250
  nodes: collectEditableNodes(editContentRef.current),
32475
32251
  sections