@ohhwells/bridge 0.1.160-next.429 → 0.1.161

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  // src/OhhwellsBridge.tsx
4
- import React12, { useCallback as useCallback12, useEffect as useEffect19, useLayoutEffect as useLayoutEffect7, useRef as useRef14, useState as useState18 } from "react";
4
+ import React12, { useCallback as useCallback12, useEffect as useEffect19, useLayoutEffect as useLayoutEffect6, useRef as useRef14, useState as useState18 } from "react";
5
5
  import { createRoot as createRoot3 } from "react-dom/client";
6
6
  import { flushSync as flushSync3 } from "react-dom";
7
7
 
@@ -1094,21 +1094,21 @@ function chromeFrom(sample) {
1094
1094
  };
1095
1095
  }
1096
1096
  function filledPrimary(seed) {
1097
- const paint2 = parseColor(paintColor(seed)) ?? { r: 15, g: 15, b: 15, a: 1 };
1098
- const luminance = relativeLuminance(paint2);
1097
+ const paint = parseColor(paintColor(seed)) ?? { r: 15, g: 15, b: 15, a: 1 };
1098
+ const luminance = relativeLuminance(paint);
1099
1099
  return {
1100
- background: toCss({ ...paint2, a: 1 }),
1100
+ background: toCss({ ...paint, a: 1 }),
1101
1101
  color: luminance > 0.55 ? "rgb(15, 15, 15)" : "rgb(255, 255, 255)",
1102
1102
  borderColor: TRANSPARENT,
1103
1103
  borderWidth: "0px"
1104
1104
  };
1105
1105
  }
1106
1106
  function generatedSecondary(primary) {
1107
- const paint2 = accentColor(primary);
1107
+ const paint = accentColor(primary);
1108
1108
  return {
1109
1109
  background: TRANSPARENT,
1110
- color: paint2,
1111
- borderColor: paint2,
1110
+ color: paint,
1111
+ borderColor: paint,
1112
1112
  borderWidth: "1px"
1113
1113
  };
1114
1114
  }
@@ -1121,9 +1121,9 @@ function generatedGhost(primary) {
1121
1121
  };
1122
1122
  }
1123
1123
  function normalizeOutlineChrome(outline, primary) {
1124
- const paint2 = accentColor(primary);
1124
+ const paint = accentColor(primary);
1125
1125
  const text = parseColor(outline.color);
1126
- const paintRgb = parseColor(paint2);
1126
+ const paintRgb = parseColor(paint);
1127
1127
  const primaryLabel = parseColor(primary.color);
1128
1128
  const matchesOnPrimary = Boolean(text && primaryLabel) && colorKey(outline.color) === colorKey(primary.color);
1129
1129
  const textTooLight = text ? relativeLuminance(text) > 0.7 : false;
@@ -1132,8 +1132,8 @@ function normalizeOutlineChrome(outline, primary) {
1132
1132
  const hasBorder = parseFloat(outline.borderWidth) > 0;
1133
1133
  return {
1134
1134
  ...outline,
1135
- color: paint2,
1136
- borderColor: hasBorder ? paint2 : outline.borderColor
1135
+ color: paint,
1136
+ borderColor: hasBorder ? paint : outline.borderColor
1137
1137
  };
1138
1138
  }
1139
1139
  function generateButtonVariants(samples) {
@@ -6649,21 +6649,6 @@ function pricingCardFeatureCount(tree, index) {
6649
6649
  const target = findPricingCollection(tree)?.children?.[index];
6650
6650
  return target ? pricingFeatureList(target).length : 0;
6651
6651
  }
6652
- function prevailingFeatureIcon(features) {
6653
- const counts = /* @__PURE__ */ new Map();
6654
- for (const row of features) {
6655
- const key = `${row.icon ?? ""}|${row.iconColor ?? ""}`;
6656
- const entry = counts.get(key) ?? { count: 0, ...row.icon ? { icon: row.icon } : {}, ...row.iconColor ? { iconColor: row.iconColor } : {} };
6657
- entry.count += 1;
6658
- counts.set(key, entry);
6659
- }
6660
- let best = null;
6661
- for (const entry of counts.values()) {
6662
- if (!best || entry.count > best.count) best = entry;
6663
- }
6664
- if (!best) return {};
6665
- return { ...best.icon ? { icon: best.icon } : {}, ...best.iconColor ? { iconColor: best.iconColor } : {} };
6666
- }
6667
6652
  function addPricingCardFeature(tree, index, text) {
6668
6653
  return withPricingCollection(tree, (collection) => {
6669
6654
  const children = collection.children;
@@ -6672,7 +6657,7 @@ function addPricingCardFeature(tree, index, text) {
6672
6657
  const features = pricingFeatureList(target);
6673
6658
  if (features.length >= PRICING_FEATURES_MAX) return false;
6674
6659
  target.slots = target.slots ?? {};
6675
- target.slots.features = [...features, { text, ...prevailingFeatureIcon(features) }];
6660
+ target.slots.features = [...features, { text }];
6676
6661
  return true;
6677
6662
  });
6678
6663
  }
@@ -7316,120 +7301,6 @@ function findSelectableNavContainer(doc = document) {
7316
7301
  );
7317
7302
  }
7318
7303
 
7319
- // src/lib/youtube.ts
7320
- var YOUTUBE_ID_PATTERN = /^[A-Za-z0-9_-]{11}$/;
7321
- function extractYouTubeId(input) {
7322
- const trimmed = input.trim();
7323
- if (!trimmed) return null;
7324
- if (YOUTUBE_ID_PATTERN.test(trimmed)) return trimmed;
7325
- let url;
7326
- try {
7327
- url = new URL(trimmed);
7328
- } catch {
7329
- return null;
7330
- }
7331
- const host = url.hostname.replace(/^www\./, "");
7332
- if (host !== "youtube.com" && host !== "m.youtube.com" && host !== "youtu.be" && host !== "youtube-nocookie.com") {
7333
- return null;
7334
- }
7335
- let candidate = null;
7336
- if (host === "youtu.be") {
7337
- candidate = url.pathname.slice(1).split("/")[0] ?? null;
7338
- } else if (url.pathname === "/watch") {
7339
- candidate = url.searchParams.get("v");
7340
- } else {
7341
- const match = url.pathname.match(/^\/(?:embed|shorts)\/([^/]+)/);
7342
- candidate = match ? match[1] : null;
7343
- }
7344
- return candidate && YOUTUBE_ID_PATTERN.test(candidate) ? candidate : null;
7345
- }
7346
-
7347
- // src/lib/section-embed.ts
7348
- var SECTION_EMBED_SUFFIX = "__ohw_embed";
7349
- function readStoredEmbed(value) {
7350
- if (value.startsWith("start\n")) {
7351
- return { raw: value.slice("start\n".length), at: "start" };
7352
- }
7353
- return { raw: value, at: "end" };
7354
- }
7355
- function embedFrameSrc(value) {
7356
- const videoId = extractYouTubeId(value);
7357
- if (videoId) {
7358
- return { src: `https://www.youtube-nocookie.com/embed/${videoId}?rel=0`, title: "YouTube video" };
7359
- }
7360
- let url;
7361
- try {
7362
- url = new URL(value);
7363
- } catch {
7364
- return null;
7365
- }
7366
- const host = url.hostname.replace(/^www\./, "").toLowerCase();
7367
- if (host === "google.com" && url.pathname.startsWith("/maps") || host === "maps.google.com") {
7368
- const query = url.searchParams.get("q") || url.searchParams.get("query") || "";
7369
- const src = query ? `https://maps.google.com/maps?q=${encodeURIComponent(query)}&output=embed` : `https://maps.google.com/maps?output=embed`;
7370
- return { src, title: "Map" };
7371
- }
7372
- if (host === "maps.app.goo.gl") {
7373
- return { src: value, title: "Map" };
7374
- }
7375
- if (host === "calendly.com" || host.endsWith(".calendly.com") || host === "calendar.google.com") {
7376
- return { src: value, title: "Calendar" };
7377
- }
7378
- if (host === "docs.google.com" && url.pathname.includes("/forms/")) {
7379
- return { src: value.replace(/\/edit(?:\?.*)?$/i, "/viewform"), title: "Form" };
7380
- }
7381
- if (host === "forms.gle") {
7382
- return { src: value, title: "Form" };
7383
- }
7384
- return null;
7385
- }
7386
- function place(section, box, at) {
7387
- if (at === "start") {
7388
- section.insertBefore(box, section.firstChild);
7389
- return;
7390
- }
7391
- section.appendChild(box);
7392
- }
7393
- function paint(section, key, src, title, at) {
7394
- const existing = section.querySelector(`[data-ohw-section-embed="${CSS.escape(key)}"]`);
7395
- const frame = existing?.querySelector("iframe");
7396
- if (existing && frame) {
7397
- if (frame.getAttribute("src") !== src) frame.setAttribute("src", src);
7398
- frame.title = title;
7399
- place(section, existing, at);
7400
- return;
7401
- }
7402
- existing?.remove();
7403
- const box = document.createElement("div");
7404
- box.setAttribute("data-ohw-section-embed", key);
7405
- box.style.cssText = at === "start" ? "width:100%;max-width:960px;margin:0 auto 32px;padding:32px 24px 0;box-sizing:border-box;" : "width:100%;max-width:960px;margin:32px auto 0;padding:0 24px 32px;box-sizing:border-box;";
7406
- const frameWrap = document.createElement("div");
7407
- frameWrap.style.cssText = "position:relative;width:100%;aspect-ratio:16/9;min-height:250px;border-radius:12px;overflow:hidden;background:#0f172a;";
7408
- const iframe = document.createElement("iframe");
7409
- iframe.src = src;
7410
- iframe.title = title;
7411
- iframe.style.cssText = "position:absolute;inset:0;width:100%;height:100%;border:0;";
7412
- iframe.allow = "accelerometer; autoplay; clipboard-write; encrypted-media; fullscreen; gyroscope; picture-in-picture";
7413
- iframe.setAttribute("allowfullscreen", "");
7414
- frameWrap.appendChild(iframe);
7415
- box.appendChild(frameWrap);
7416
- place(section, box, at);
7417
- }
7418
- function applySectionEmbeds(content) {
7419
- for (const [key, val] of Object.entries(content)) {
7420
- if (!key.endsWith(SECTION_EMBED_SUFFIX)) continue;
7421
- const sectionId = key.slice(0, -SECTION_EMBED_SUFFIX.length);
7422
- const section = document.querySelector(`[data-ohw-section="${CSS.escape(sectionId)}"]`);
7423
- const stored = typeof val === "string" ? readStoredEmbed(val) : null;
7424
- const frame = stored ? embedFrameSrc(stored.raw) : null;
7425
- if (!section || !stored || !frame) {
7426
- section?.querySelector(`[data-ohw-section-embed="${CSS.escape(key)}"]`)?.remove();
7427
- continue;
7428
- }
7429
- paint(section, key, frame.src, frame.title, stored.at);
7430
- }
7431
- }
7432
-
7433
7304
  // src/ui/ai-tree/aiSectionsManager.tsx
7434
7305
  import { flushSync } from "react-dom";
7435
7306
  import { createRoot } from "react-dom/client";
@@ -8003,7 +7874,7 @@ var AI_MOBILE_CSS = [
8003
7874
  // page's first section. Top only (bottom needs no nav clearance); ~96px approximates the
8004
7875
  // template nav's own height (measured ~94px) — there's no shared `--ohw-header-h` var to read
8005
7876
  // an exact figure from (no live measurement exists), so this is a safe approximation, not a
8006
- // pixel-exact fit. Horizontal spacing already comes from `data-ai-section-inner`.
7877
+ // pixel-exact fit. Horizontal spacing already comes from `data-ai-container`.
8007
7878
  '[data-ai-overlay-clearance] [data-ai-overlap-layer="overlay"]{padding-top:96px}',
8008
7879
  // A full-bleed carousel's height is pure arithmetic: it spans the whole window, so 16/9 turns
8009
7880
  // window width straight into height. Measured at 16/9 it came out ~90% of the viewport at EVERY
@@ -8019,10 +7890,8 @@ var AI_MOBILE_CSS = [
8019
7890
  "[data-ai-section]{overflow-x:hidden}",
8020
7891
  // `:not([data-ai-full-bleed])`: this `!important` gutter beat the inline `padding: 0` that a
8021
7892
  // full-bleed section's container sets, so a carousel/media hero that is edge-to-edge on desktop
8022
- // still got a 20px band of section background down both sides on a phone. The footer's own
8023
- // container keeps the unguarded rule below — `data-ai-full-bleed` can never appear on it.
8024
- "[data-ai-section-inner]:not([data-ai-full-bleed]){padding:0 20px !important}",
8025
- "[data-ai-container]{padding:0 20px !important}",
7893
+ // still got a 20px band of section background down both sides on a phone.
7894
+ "[data-ai-container]:not([data-ai-full-bleed]){padding:0 20px !important}",
8026
7895
  "[data-ai-row]{display:flex !important;flex-direction:column !important;align-items:stretch !important}",
8027
7896
  "[data-ai-cell]{width:100%;min-width:0}",
8028
7897
  // The rule above turns every row into a COLUMN flex container on phones, which silently breaks
@@ -8227,31 +8096,6 @@ function pricingCardEmphasis(featured, ctx, hasBg) {
8227
8096
  function textAttrs(ctx, path) {
8228
8097
  return ctx.keyFor ? { "data-ohw-key": ctx.keyFor(path), "data-ohw-editable": "text" } : {};
8229
8098
  }
8230
- var AI_RESPONSIVE_CSS = [
8231
- "@media (max-width: 960px) {",
8232
- " [data-ai-responsive] [data-ai-grid] { grid-template-columns: repeat(2, 1fr) !important; }",
8233
- ' [data-ai-responsive] [data-ai-grid="1"] { grid-template-columns: 1fr !important; }',
8234
- "}",
8235
- "@media (max-width: 640px) {",
8236
- " [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
8237
- " [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
8238
- // Group containers flatten to a column on phones; span placements come along for free.
8239
- // `:not([data-ai-group="overlap"])` for the same reason as the twin pair in AI_MOBILE_CSS — see
8240
- // the comment there. This sheet's copy is the narrower selector, so without the exemption it
8241
- // flattens an `overlap` hero below 640px even though the other rule stands down: measured a 390px
8242
- // viewport with the heading/CTA in flow *under* the image, 219px of section background where the
8243
- // layered text used to be.
8244
- ' [data-ai-responsive] [data-ai-group]:not([data-ai-group="overlap"]) { display: flex !important; flex-direction: column !important; }',
8245
- ' [data-ai-responsive] [data-ai-group]:not([data-ai-group="overlap"]) > * { grid-column: auto !important; }',
8246
- // `:not([data-ai-full-bleed])` for the same reason as the twin rule in AI_MOBILE_CSS: this one
8247
- // is narrower (0,2,0 vs 0,1,0) and also `!important`, so without the guard it wins outright and
8248
- // a full-bleed hero gets its 20px side band back below 640px only — the one width where the
8249
- // other rule had already been taught to stand down.
8250
- " [data-ai-responsive] [data-ai-section-inner]:not([data-ai-full-bleed]) { padding-left: 20px !important; padding-right: 20px !important; }",
8251
- " [data-ai-responsive] { overflow-x: hidden; }",
8252
- " [data-ai-responsive] img { max-width: 100%; }",
8253
- "}"
8254
- ].join("\n");
8255
8099
  var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
8256
8100
  function MediaBox({
8257
8101
  refValue,
@@ -8306,7 +8150,7 @@ function MediaBox({
8306
8150
  position: fill ? "absolute" : void 0,
8307
8151
  inset: fill ? 0 : void 0,
8308
8152
  width: fill ? "100%" : fixedSize ? "auto" : "100%",
8309
- height: fill ? "100%" : fixedSize ? height : void 0,
8153
+ height: fill ? "100%" : void 0,
8310
8154
  aspectRatio: fill ? void 0 : aspectRatio,
8311
8155
  minHeight: fill || fixedSize ? void 0 : height ?? (aspectRatio ? void 0 : 180),
8312
8156
  borderRadius: radius,
@@ -9802,7 +9646,7 @@ function CollectionBlock({ node, ctx, path }) {
9802
9646
  return /* @__PURE__ */ jsx(
9803
9647
  "div",
9804
9648
  {
9805
- "data-ai-grid": String(itemsPerRow),
9649
+ "data-ai-grid": "",
9806
9650
  "data-ohw-pricing-collection": isPricing ? "" : void 0,
9807
9651
  style: {
9808
9652
  display: "grid",
@@ -10104,7 +9948,7 @@ function renderNode(node, ctx, path, ambientAlign) {
10104
9948
  },
10105
9949
  children: [
10106
9950
  i > 0 && ctx.heroOverlapFullBleed ? (
10107
- // The section's `data-ai-section-inner` gave up its max-width/gutter so the base
9951
+ // The section's `data-ai-container` gave up its max-width/gutter so the base
10108
9952
  // layer (this group's i===0 sibling) can span edge-to-edge — reintroduced here,
10109
9953
  // scoped to just the overlay text, so a heading/copy/CTA stack doesn't stretch
10110
9954
  // the full section width instead of reading at the usual content width.
@@ -10523,7 +10367,6 @@ function AiTreeRenderer({
10523
10367
  ...fillHeight ? { "data-ai-fill-height": "" } : {},
10524
10368
  ...needsOverlayClearance ? { "data-ai-overlay-clearance": "" } : {},
10525
10369
  ...bgAttrs,
10526
- "data-ai-responsive": "",
10527
10370
  style: {
10528
10371
  position: "relative",
10529
10372
  padding: `${padCss} 0`,
@@ -10537,13 +10380,12 @@ function AiTreeRenderer({
10537
10380
  ...fillHeight ? { display: "grid" } : {}
10538
10381
  },
10539
10382
  children: [
10540
- /* @__PURE__ */ jsx("style", { children: AI_RESPONSIVE_CSS }),
10541
- /* @__PURE__ */ jsx("style", { children: AI_MOBILE_CSS }),
10542
10383
  isOverlay && backgroundUrl && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
10384
+ /* @__PURE__ */ jsx("style", { children: AI_MOBILE_CSS }),
10543
10385
  /* @__PURE__ */ jsx(
10544
10386
  "div",
10545
10387
  {
10546
- "data-ai-section-inner": "",
10388
+ "data-ai-container": "",
10547
10389
  ...fullBleedMedia ? { "data-ai-full-bleed": "" } : {},
10548
10390
  style: {
10549
10391
  position: "relative",
@@ -10560,7 +10402,7 @@ function AiTreeRenderer({
10560
10402
  children: tree.rows.map((row, r2) => /* @__PURE__ */ jsx(
10561
10403
  "div",
10562
10404
  {
10563
- "data-ai-columns": "",
10405
+ "data-ai-row": "",
10564
10406
  style: {
10565
10407
  display: "grid",
10566
10408
  gridTemplateColumns: "repeat(12, 1fr)",
@@ -11222,10 +11064,9 @@ function applyAiSectionsToDom(state, options) {
11222
11064
  if (existing && existing.serialized === serialized && existing.container.isConnected) {
11223
11065
  continue;
11224
11066
  }
11067
+ const isNewMount = !existing || !existing.container.isConnected;
11225
11068
  let container = existing?.container;
11226
- let isNewMount = false;
11227
11069
  if (!container || !container.isConnected) {
11228
- isNewMount = true;
11229
11070
  existing?.root.unmount();
11230
11071
  container?.remove();
11231
11072
  container = document.createElement("div");
@@ -11563,34 +11404,6 @@ function minSpanForFooterBlock(type) {
11563
11404
  return 3;
11564
11405
  }
11565
11406
  }
11566
- function readLiveSocialItems() {
11567
- const root = getFooterRoot();
11568
- const row = root?.querySelector("[data-ohw-socials-row]");
11569
- if (!row) return [];
11570
- const items = [];
11571
- for (const anchor of Array.from(row.querySelectorAll("a[href]"))) {
11572
- const href = anchor.getAttribute("href")?.trim() ?? "";
11573
- const aria = anchor.getAttribute("aria-label")?.trim().toLowerCase() ?? "";
11574
- const platform = FOOTER_SOCIAL_PLATFORMS.find((name) => aria === name) ?? FOOTER_SOCIAL_PLATFORMS.find((name) => href.toLowerCase().includes(name));
11575
- if (!platform || !/^https?:\/\//i.test(href)) continue;
11576
- items.push({ platform, href });
11577
- }
11578
- return items;
11579
- }
11580
- function syncSocialRowsFromDom(tree) {
11581
- const root = getFooterRoot();
11582
- if (!root) return;
11583
- const row = root.querySelector("[data-ohw-socials-row]");
11584
- const live = readLiveSocialItems();
11585
- if (row && live.length === 0) return;
11586
- for (const col of tree.columns) {
11587
- col.blocks = col.blocks.flatMap((block) => {
11588
- if (block.type !== "social-row") return [block];
11589
- if (!row) return [];
11590
- return [{ ...block, items: live.map((item) => ({ platform: item.platform, href: item.href })) }];
11591
- });
11592
- }
11593
- }
11594
11407
  function collectBrandBlocks(tree) {
11595
11408
  const brand = [];
11596
11409
  for (const col of tree.columns) {
@@ -11836,7 +11649,6 @@ function textOf2(el) {
11836
11649
  }
11837
11650
  function syncFooterTreeLinksFromDom(tree) {
11838
11651
  const next = cloneTree(tree);
11839
- syncSocialRowsFromDom(next);
11840
11652
  const domCols = listFooterColumns().map((col) => {
11841
11653
  const heading = getFooterColumnHeadingEl(col);
11842
11654
  const title = heading ? textOf2(heading) : void 0;
@@ -17541,7 +17353,7 @@ function apiBase() {
17541
17353
  function bridgeVersion() {
17542
17354
  try {
17543
17355
  if (true) {
17544
- return "0.1.160-next.429";
17356
+ return "0.1.161";
17545
17357
  }
17546
17358
  } catch {
17547
17359
  }
@@ -17555,7 +17367,7 @@ function stampBridgeVersionOnDocument() {
17555
17367
  return;
17556
17368
  }
17557
17369
  try {
17558
- const v = true ? "0.1.160-next.429" : "";
17370
+ const v = true ? "0.1.161" : "";
17559
17371
  if (v) {
17560
17372
  document.documentElement.setAttribute("data-ohw-bridge-version", v);
17561
17373
  window.__OHW_BRIDGE_VERSION__ = v;
@@ -18233,7 +18045,6 @@ function MediaOverlay({
18233
18045
  (prev) => prev && prev.fullWidth === width && prev.height === height ? prev : { fullWidth: width, height }
18234
18046
  );
18235
18047
  }, [isVideo]);
18236
- const replaceLabel = isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image";
18237
18048
  const replaceMode = buttonSize ? replaceButtonMode({ width: rect.width, height: rect.height }, buttonSize) : "none";
18238
18049
  const box = {
18239
18050
  position: "fixed",
@@ -18332,10 +18143,11 @@ function MediaOverlay({
18332
18143
  className: "flex items-center justify-center cursor-pointer",
18333
18144
  style: {
18334
18145
  ...box,
18335
- // Visual only. `pointer-events: auto` on this full-bleed rect covered the navbar
18336
- // (hero bg includes the absolute TopNav) and swallowed CTA press-drag. Text overlay
18337
- // and cueOnly also need clicks to fall through — Replace / video controls opt back in.
18338
- pointerEvents: "none",
18146
+ // When text overlays this media, let clicks fall THROUGH to the text underneath. In the
18147
+ // parent this needed a whole `ow:click-at` round-trip to re-hit-test inside the iframe;
18148
+ // in-document, pointer-events does it natively. The button below opts back in, so
18149
+ // Replace still works.
18150
+ pointerEvents: hover.hasTextOverlap || cueOnly ? "none" : "auto",
18339
18151
  // Selected: a firm component ring with no wash, so the image reads as chosen rather
18340
18152
  // than hovered. Hover keeps the existing tinted preview.
18341
18153
  boxShadow: selected ? "inset 0 0 0 2px var(--color-bridge-primary)" : "inset 0 0 0 1.5px var(--color-bridge-primary)",
@@ -18362,17 +18174,17 @@ function MediaOverlay({
18362
18174
  },
18363
18175
  children: [
18364
18176
  isVideo ? /* @__PURE__ */ jsx17(Film, { size: 14 }) : /* @__PURE__ */ jsx17(ImageIcon, { size: 14 }),
18365
- replaceLabel
18177
+ isVideo ? "Replace video" : "Replace image"
18366
18178
  ]
18367
18179
  }
18368
18180
  ),
18369
- showChrome && replaceMode !== "none" && /* @__PURE__ */ jsxs8(
18181
+ replaceMode === "none" || cueOnly ? null : /* @__PURE__ */ jsxs8(
18370
18182
  Button,
18371
18183
  {
18372
18184
  "data-ohw-media-overlay": "",
18373
18185
  variant: "outline",
18374
18186
  size: "sm",
18375
- "aria-label": replaceLabel,
18187
+ "aria-label": isVideo ? "Replace video" : "Replace image",
18376
18188
  className: "gap-1.5 cursor-pointer hover:bg-background",
18377
18189
  style: {
18378
18190
  ...OVERLAY_BUTTON_STYLE,
@@ -18395,7 +18207,7 @@ function MediaOverlay({
18395
18207
  },
18396
18208
  children: [
18397
18209
  isVideo ? /* @__PURE__ */ jsx17(Film, { size: 14 }) : /* @__PURE__ */ jsx17(ImageIcon, { size: 14 }),
18398
- replaceMode === "full" ? replaceLabel : null
18210
+ replaceMode === "full" ? isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image" : null
18399
18211
  ]
18400
18212
  }
18401
18213
  )
@@ -18988,24 +18800,8 @@ function AiSectionOverlay({
18988
18800
  }
18989
18801
 
18990
18802
  // src/ui/ai-section/AiMediaStripOverlay.tsx
18991
- import { useCallback as useCallback3, useEffect as useEffect8, useLayoutEffect as useLayoutEffect4, useRef as useRef5, useState as useState8 } from "react";
18992
- import {
18993
- AlignCenter,
18994
- AlignLeft,
18995
- AlignRight,
18996
- ArrowLeft as ArrowLeft2,
18997
- ArrowRight as ArrowRight2,
18998
- Check as Check2,
18999
- ChevronDown as ChevronDown3,
19000
- Image as ImageIcon2,
19001
- ImageMinus,
19002
- ImagePlus,
19003
- Link as LinkIcon,
19004
- Minus,
19005
- Palette,
19006
- Plus as Plus2,
19007
- Star
19008
- } from "lucide-react";
18803
+ import { useCallback as useCallback3, useEffect as useEffect8, useRef as useRef5, useState as useState8 } from "react";
18804
+ import { Star } from "lucide-react";
19009
18805
 
19010
18806
  // src/lib/ai-media-strip-tree.ts
19011
18807
  var MEDIA_STRIP_ITEM_MIN = 1;
@@ -19119,6 +18915,14 @@ function setMediaStripItemAsset(tree, index, assetId) {
19119
18915
  return true;
19120
18916
  });
19121
18917
  }
18918
+ function setMediaStripItemAlt(tree, index, alt) {
18919
+ return withMediaStrip(tree, (block) => {
18920
+ const items = block.items;
18921
+ if (index < 0 || index >= items.length) return false;
18922
+ items[index] = { ...items[index], alt };
18923
+ return true;
18924
+ });
18925
+ }
19122
18926
  function setMediaStripItemLink(tree, index, link) {
19123
18927
  return withMediaStrip(tree, (block) => {
19124
18928
  const items = block.items;
@@ -19136,117 +18940,11 @@ function setMediaStripItemLink(tree, index, link) {
19136
18940
  });
19137
18941
  }
19138
18942
 
19139
- // src/ui/dialog.tsx
19140
- import * as React9 from "react";
19141
- import { Dialog as DialogPrimitive } from "radix-ui";
19142
- import { X as X2 } from "lucide-react";
19143
- import { jsx as jsx20, jsxs as jsxs11 } from "react/jsx-runtime";
19144
- function Dialog2({
19145
- ...props
19146
- }) {
19147
- return /* @__PURE__ */ jsx20(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
19148
- }
19149
- function DialogPortal({
19150
- ...props
19151
- }) {
19152
- return /* @__PURE__ */ jsx20(DialogPrimitive.Portal, { ...props });
19153
- }
19154
- function DialogOverlay({
19155
- className,
19156
- ...props
19157
- }) {
19158
- return /* @__PURE__ */ jsx20(
19159
- DialogPrimitive.Overlay,
19160
- {
19161
- "data-slot": "dialog-overlay",
19162
- "data-ohw-link-modal-root": "",
19163
- className: cn("fixed inset-0 z-[2147483646] bg-black/50", className),
19164
- ...props
19165
- }
19166
- );
19167
- }
19168
- var DialogContent = React9.forwardRef(
19169
- ({ className, children, showCloseButton = true, container, ...props }, ref) => {
19170
- const positionMode = container ? "absolute" : "fixed";
19171
- return /* @__PURE__ */ jsxs11(DialogPortal, { container: container ?? void 0, children: [
19172
- /* @__PURE__ */ jsx20(DialogOverlay, { className: cn(positionMode, "inset-0") }),
19173
- /* @__PURE__ */ jsxs11(
19174
- DialogPrimitive.Content,
19175
- {
19176
- ref,
19177
- "data-slot": "dialog-content",
19178
- className: cn(
19179
- positionMode,
19180
- "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",
19181
- "rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
19182
- container ? "max-h-[calc(100%-32px)]" : "max-h-[calc(100vh-32px)]",
19183
- className
19184
- ),
19185
- onMouseDown: (e) => e.stopPropagation(),
19186
- ...props,
19187
- children: [
19188
- children,
19189
- showCloseButton ? /* @__PURE__ */ jsx20(
19190
- DialogPrimitive.Close,
19191
- {
19192
- type: "button",
19193
- className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
19194
- "aria-label": "Close",
19195
- children: /* @__PURE__ */ jsx20(X2, { size: 16, "aria-hidden": true })
19196
- }
19197
- ) : null
19198
- ]
19199
- }
19200
- )
19201
- ] });
19202
- }
19203
- );
19204
- DialogContent.displayName = DialogPrimitive.Content.displayName;
19205
- function DialogHeader({
19206
- className,
19207
- ...props
19208
- }) {
19209
- return /* @__PURE__ */ jsx20("div", { className: cn("flex flex-col gap-1.5", className), ...props });
19210
- }
19211
- function DialogFooter({
19212
- className,
19213
- ...props
19214
- }) {
19215
- return /* @__PURE__ */ jsx20(
19216
- "div",
19217
- {
19218
- className: cn("flex items-center justify-end gap-2", className),
19219
- ...props
19220
- }
19221
- );
19222
- }
19223
- var DialogTitle = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx20(
19224
- DialogPrimitive.Title,
19225
- {
19226
- ref,
19227
- className: cn(
19228
- "text-lg font-semibold leading-none tracking-tight text-card-foreground",
19229
- className
19230
- ),
19231
- ...props
19232
- }
19233
- ));
19234
- DialogTitle.displayName = DialogPrimitive.Title.displayName;
19235
- var DialogDescription = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx20(
19236
- DialogPrimitive.Description,
19237
- {
19238
- ref,
19239
- className: cn("text-sm text-muted-foreground", className),
19240
- ...props
19241
- }
19242
- ));
19243
- DialogDescription.displayName = DialogPrimitive.Description.displayName;
19244
- var DialogClose = DialogPrimitive.Close;
19245
-
19246
18943
  // src/ui/ai-section/AiMediaStripOverlay.tsx
19247
- import { Fragment as Fragment7, jsx as jsx21, jsxs as jsxs12 } from "react/jsx-runtime";
19248
- var PRIMARY3 = "var(--ohw-primary, #0885FE)";
19249
- 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]";
18944
+ import { Fragment as Fragment7, jsx as jsx20, jsxs as jsxs11 } from "react/jsx-runtime";
18945
+ var PRIMARY3 = "#0885FE";
18946
+ var OHW_BLUE = `var(--ohw-primary, ${PRIMARY3})`;
18947
+ var OHW_BLUE_DARK = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY3}) 60%, #000000)`;
19250
18948
  function findMediaStripItemElement(sectionId, index) {
19251
18949
  const sectionEl = findByInstanceId(sectionId);
19252
18950
  if (!sectionEl) return null;
@@ -19256,35 +18954,14 @@ function findMediaStripItemElement(sectionId, index) {
19256
18954
  }
19257
18955
  return null;
19258
18956
  }
19259
- function itemRefFromTarget(target) {
19260
- if (!(target instanceof Element)) return null;
19261
- if (target.closest(CHROME_SELECTOR)) return null;
19262
- const itemEl = target.closest("[data-ohw-media-strip-item]");
19263
- if (!itemEl) return null;
19264
- const sectionEl = itemEl.closest("[data-ohw-section]");
19265
- const unit = sectionEl ? movableUnit(sectionEl) : null;
19266
- const sectionId = unit ? instanceIdOf(unit) || null : null;
19267
- const index = Number(itemEl.dataset.ohwMediaStripIndex);
19268
- if (!sectionId || Number.isNaN(index)) return null;
19269
- return { sectionId, index };
19270
- }
19271
- function sameRef(a, b) {
19272
- return a?.sectionId === b?.sectionId && a?.index === b?.index;
19273
- }
19274
- function rectOf(el) {
18957
+ function readRect2(ref) {
18958
+ const el = ref && findMediaStripItemElement(ref.sectionId, ref.index);
19275
18959
  if (!el) return null;
19276
18960
  const r2 = el.getBoundingClientRect();
19277
18961
  if (r2.width <= 0 || r2.height <= 0) return null;
19278
18962
  return { top: r2.top, left: r2.left, width: r2.width, height: r2.height };
19279
18963
  }
19280
- function readRect2(ref) {
19281
- return rectOf(ref && findMediaStripItemElement(ref.sectionId, ref.index));
19282
- }
19283
- function readStripRect(ref) {
19284
- const item = ref && findMediaStripItemElement(ref.sectionId, ref.index);
19285
- return rectOf(item?.closest("[data-ohw-media-strip]") ?? item);
19286
- }
19287
- function useLiveItemRect(ref, strip = false) {
18964
+ function useLiveItemRect(ref) {
19288
18965
  const [rect, setRect] = useState8(null);
19289
18966
  useEffect8(() => {
19290
18967
  if (!ref) {
@@ -19292,7 +18969,7 @@ function useLiveItemRect(ref, strip = false) {
19292
18969
  return;
19293
18970
  }
19294
18971
  const update = () => {
19295
- const next = strip ? readStripRect(ref) : readRect2(ref);
18972
+ const next = readRect2(ref);
19296
18973
  setRect(
19297
18974
  (prev) => prev && next && prev.top === next.top && prev.left === next.left && prev.width === next.width && prev.height === next.height ? prev : next
19298
18975
  );
@@ -19307,49 +18984,9 @@ function useLiveItemRect(ref, strip = false) {
19307
18984
  window.removeEventListener("resize", update);
19308
18985
  clearInterval(interval);
19309
18986
  };
19310
- }, [ref?.sectionId, ref?.index, strip]);
18987
+ }, [ref?.sectionId, ref?.index]);
19311
18988
  return rect;
19312
18989
  }
19313
- var TOOLBAR_STRIP_GAP = 52;
19314
- var TOOLBAR_STRIP_GAP_MOBILE = 42;
19315
- var TOOLBAR_EDGE_MARGIN2 = 4;
19316
- function StripToolbarSlot({ stripRect, children }) {
19317
- const slotRef = useRef5(null);
19318
- const [shift, setShift] = useState8(0);
19319
- const centerX = stripRect.left + stripRect.width / 2;
19320
- useLayoutEffect4(() => {
19321
- const slot = slotRef.current;
19322
- if (!slot) return;
19323
- const clamp = () => {
19324
- const w = slot.offsetWidth;
19325
- if (w <= 0) return;
19326
- const half = w / 2;
19327
- const clamped = Math.max(TOOLBAR_EDGE_MARGIN2 + half, Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN2 - half));
19328
- setShift((prev) => Math.abs(prev - (clamped - centerX)) < 0.5 ? prev : clamped - centerX);
19329
- };
19330
- clamp();
19331
- const ro = new ResizeObserver(clamp);
19332
- ro.observe(slot);
19333
- return () => ro.disconnect();
19334
- }, [centerX, children]);
19335
- const mobile = window.innerWidth <= 480;
19336
- return /* @__PURE__ */ jsx21(
19337
- "div",
19338
- {
19339
- ref: slotRef,
19340
- "data-ohw-bridge": "",
19341
- "data-ohw-item-interaction": "",
19342
- className: "pointer-events-auto fixed",
19343
- style: {
19344
- top: stripRect.top - (mobile ? TOOLBAR_STRIP_GAP_MOBILE : TOOLBAR_STRIP_GAP),
19345
- left: centerX + shift,
19346
- transform: "translate(-50%, -100%)",
19347
- zIndex: 2147483645
19348
- },
19349
- children
19350
- }
19351
- );
19352
- }
19353
18990
  function requestValidateTree(postToParent2, tree) {
19354
18991
  return new Promise((resolve) => {
19355
18992
  const id = `ai-validate-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
@@ -19386,72 +19023,116 @@ function requestAssetPick(postToParent2) {
19386
19023
  postToParent2({ type: "ow:asset-pick", id });
19387
19024
  });
19388
19025
  }
19389
- function ToolbarAction({
19026
+ function Pill({
19390
19027
  label,
19391
19028
  onClick,
19392
- disabled = false,
19393
- active = false,
19394
- children
19395
- }) {
19396
- const button = /* @__PURE__ */ jsx21(CustomToolbarButton, { type: "button", "aria-label": label, disabled, active, onClick, className: "size-8", children });
19397
- return /* @__PURE__ */ jsxs12(Tooltip, { children: [
19398
- /* @__PURE__ */ jsx21(TooltipTrigger, { asChild: true, children: disabled ? /* @__PURE__ */ jsx21("span", { className: "inline-flex", children: button }) : button }),
19399
- /* @__PURE__ */ jsx21(TooltipContent, { side: "bottom", sideOffset: 8, children: label })
19400
- ] });
19401
- }
19402
- function ToolbarSeparator() {
19403
- return /* @__PURE__ */ jsx21("span", { "aria-hidden": true, className: "mx-0.5 block h-6 w-px shrink-0 bg-border" });
19404
- }
19405
- function OptionMenu({
19406
- label,
19407
- value,
19408
- options,
19409
- onChange
19029
+ disabled,
19030
+ active
19410
19031
  }) {
19411
- const current = options.find((o) => o.value === value) ?? options[0];
19412
- return /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
19413
- /* @__PURE__ */ jsx21("span", { className: "text-xs text-muted-foreground", children: label }),
19414
- /* @__PURE__ */ jsxs12(DropdownMenu, { modal: false, children: [
19415
- /* @__PURE__ */ jsx21(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs12(
19416
- "button",
19417
- {
19418
- type: "button",
19419
- "aria-label": label,
19420
- 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",
19421
- children: [
19422
- /* @__PURE__ */ jsx21("span", { className: "flex-1 text-left", children: current.label }),
19423
- /* @__PURE__ */ jsx21(ChevronDown3, { size: 16, style: { pointerEvents: "none" } })
19424
- ]
19425
- }
19426
- ) }),
19427
- /* @__PURE__ */ jsx21(DropdownMenuContent, { align: "end", sideOffset: 8, className: "w-[132px] min-w-[132px] p-1", children: options.map((option) => {
19428
- const selected = option.value === value;
19429
- return /* @__PURE__ */ jsxs12(
19430
- DropdownMenuItem,
19431
- {
19432
- role: "menuitemradio",
19433
- "aria-checked": selected,
19434
- onSelect: () => onChange(option.value),
19435
- className: cn("grid grid-cols-[1fr_16px] gap-2 py-2 pl-2 pr-2 text-[13px]", selected && "bg-muted"),
19436
- children: [
19437
- /* @__PURE__ */ jsx21("span", { children: option.label }),
19438
- selected && /* @__PURE__ */ jsx21(Check2, { size: 16, className: "text-bridge-primary" })
19439
- ]
19440
- },
19441
- option.value
19442
- );
19443
- }) })
19444
- ] })
19445
- ] });
19032
+ return /* @__PURE__ */ jsx20(
19033
+ "button",
19034
+ {
19035
+ type: "button",
19036
+ onClick,
19037
+ disabled,
19038
+ style: {
19039
+ height: 28,
19040
+ padding: "0 10px",
19041
+ display: "inline-flex",
19042
+ alignItems: "center",
19043
+ gap: 4,
19044
+ borderRadius: 999,
19045
+ border: "none",
19046
+ cursor: disabled ? "default" : "pointer",
19047
+ background: active ? OHW_BLUE_DARK : "#ffffff",
19048
+ color: active ? "#ffffff" : OHW_BLUE_DARK,
19049
+ opacity: disabled ? 0.4 : 1,
19050
+ fontSize: 12,
19051
+ lineHeight: "16px",
19052
+ fontFamily: "Figtree, ui-sans-serif, system-ui, sans-serif",
19053
+ whiteSpace: "nowrap",
19054
+ flex: "0 0 auto"
19055
+ },
19056
+ children: label
19057
+ }
19058
+ );
19059
+ }
19060
+ function AltInput({ value, onCommit }) {
19061
+ const [draft, setDraft] = useState8(value);
19062
+ useEffect8(() => setDraft(value), [value]);
19063
+ return /* @__PURE__ */ jsxs11(
19064
+ "div",
19065
+ {
19066
+ style: {
19067
+ display: "flex",
19068
+ alignItems: "center",
19069
+ gap: 6,
19070
+ height: 28,
19071
+ padding: "0 12px",
19072
+ borderRadius: 999,
19073
+ background: "#ffffff",
19074
+ width: "100%",
19075
+ maxWidth: 260,
19076
+ boxSizing: "border-box"
19077
+ },
19078
+ children: [
19079
+ /* @__PURE__ */ jsx20(
19080
+ "span",
19081
+ {
19082
+ style: {
19083
+ flex: "0 0 auto",
19084
+ color: OHW_BLUE_DARK,
19085
+ fontSize: 10,
19086
+ fontWeight: 700,
19087
+ textTransform: "uppercase",
19088
+ letterSpacing: 0.4
19089
+ },
19090
+ children: "Alt"
19091
+ }
19092
+ ),
19093
+ /* @__PURE__ */ jsx20(
19094
+ "input",
19095
+ {
19096
+ type: "text",
19097
+ value: draft,
19098
+ onChange: (e) => setDraft(e.target.value),
19099
+ onBlur: () => {
19100
+ if (draft !== value) onCommit(draft);
19101
+ },
19102
+ onKeyDown: (e) => {
19103
+ if (e.key === "Enter") e.currentTarget.blur();
19104
+ if (e.key === "Escape") {
19105
+ setDraft(value);
19106
+ e.currentTarget.blur();
19107
+ }
19108
+ },
19109
+ placeholder: "Describe this image",
19110
+ "aria-label": "Image alt text",
19111
+ style: {
19112
+ flex: 1,
19113
+ minWidth: 0,
19114
+ border: "none",
19115
+ outline: "none",
19116
+ background: "transparent",
19117
+ fontSize: 12,
19118
+ lineHeight: "16px",
19119
+ fontFamily: "Figtree, ui-sans-serif, system-ui, sans-serif",
19120
+ color: "#1f2937"
19121
+ }
19122
+ }
19123
+ )
19124
+ ]
19125
+ }
19126
+ );
19446
19127
  }
19447
19128
  var VARIANTS = [
19448
19129
  { value: "logos", label: "Logos" },
19449
19130
  { value: "reel", label: "Reel" }
19450
19131
  ];
19451
19132
  var ALIGNS = [
19452
- { value: "left", label: "Align left", Icon: AlignLeft },
19453
- { value: "center", label: "Align center", Icon: AlignCenter },
19454
- { value: "right", label: "Align right", Icon: AlignRight }
19133
+ { value: "left", label: "Left" },
19134
+ { value: "center", label: "Center" },
19135
+ { value: "right", label: "Right" }
19455
19136
  ];
19456
19137
  var HOVERS = [
19457
19138
  { value: "none", label: "None" },
@@ -19459,104 +19140,104 @@ var HOVERS = [
19459
19140
  { value: "zoom", label: "Zoom" },
19460
19141
  { value: "fade", label: "Fade" }
19461
19142
  ];
19143
+ var TREATMENTS = [
19144
+ { value: "color", label: "Color" },
19145
+ { value: "grayscale", label: "Grayscale" }
19146
+ ];
19462
19147
  var HTTP_URL_RE = /^https?:\/\/\S+$/i;
19463
- function LinkModal({
19464
- href,
19465
- linkTarget,
19466
- onClose,
19467
- onSave
19468
- }) {
19469
- const [draft, setDraft] = useState8(href);
19470
- const [newTab, setNewTab] = useState8(linkTarget !== "same-tab");
19148
+ function LinkInput({ value, onCommit }) {
19149
+ const [draft, setDraft] = useState8(value);
19471
19150
  const [invalid, setInvalid] = useState8(false);
19472
- const save = () => {
19151
+ useEffect8(() => {
19152
+ setDraft(value);
19153
+ setInvalid(false);
19154
+ }, [value]);
19155
+ const commit = () => {
19473
19156
  const next = draft.trim();
19474
- if (next && !HTTP_URL_RE.test(next)) {
19475
- setInvalid(true);
19476
- return;
19477
- }
19478
- onSave(next, newTab ? "new-tab" : "same-tab");
19479
- onClose();
19157
+ if (next === value) return setInvalid(false);
19158
+ if (next && !HTTP_URL_RE.test(next)) return setInvalid(true);
19159
+ setInvalid(false);
19160
+ onCommit(next);
19480
19161
  };
19481
- return /* @__PURE__ */ jsx21(Dialog2, { open: true, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxs12(DialogContent, { "data-ohw-link-modal-root": "", className: "max-w-[400px] gap-4 p-5", children: [
19482
- /* @__PURE__ */ jsx21(DialogHeader, { children: /* @__PURE__ */ jsx21(DialogTitle, { className: "text-base", children: "Image link" }) }),
19483
- /* @__PURE__ */ jsxs12("div", { className: "flex flex-col gap-3", children: [
19484
- /* @__PURE__ */ jsx21(
19485
- "input",
19486
- {
19487
- autoFocus: true,
19488
- type: "text",
19489
- inputMode: "url",
19490
- value: draft,
19491
- onChange: (e) => {
19492
- setDraft(e.target.value);
19493
- setInvalid(false);
19494
- },
19495
- onKeyDown: (e) => {
19496
- if (e.key === "Enter") save();
19497
- },
19498
- placeholder: "https://example.com",
19499
- "aria-label": "Image link URL",
19500
- "aria-invalid": invalid,
19501
- className: cn(
19502
- "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",
19503
- invalid && "border-destructive focus:border-destructive"
19504
- )
19505
- }
19506
- ),
19507
- invalid && /* @__PURE__ */ jsx21("p", { className: "text-xs text-destructive", children: "Enter a full URL starting with http:// or https://" }),
19508
- /* @__PURE__ */ jsxs12("label", { className: "flex items-center gap-2 text-[13px] text-foreground", children: [
19509
- /* @__PURE__ */ jsx21("input", { type: "checkbox", checked: newTab, onChange: (e) => setNewTab(e.target.checked) }),
19510
- "Open in new tab"
19511
- ] })
19512
- ] }),
19513
- /* @__PURE__ */ jsxs12(DialogFooter, { children: [
19514
- href && /* @__PURE__ */ jsx21(
19515
- "button",
19516
- {
19517
- type: "button",
19518
- onClick: () => {
19519
- onSave("", "new-tab");
19520
- onClose();
19521
- },
19522
- className: "mr-auto rounded-[6px] px-3 py-2 text-[13px] font-medium text-destructive transition-colors hover:bg-muted",
19523
- children: "Remove link"
19524
- }
19525
- ),
19526
- /* @__PURE__ */ jsx21(
19527
- "button",
19528
- {
19529
- type: "button",
19530
- onClick: onClose,
19531
- className: "rounded-[6px] px-3 py-2 text-[13px] font-medium text-foreground transition-colors hover:bg-muted",
19532
- children: "Cancel"
19533
- }
19534
- ),
19535
- /* @__PURE__ */ jsx21(
19536
- "button",
19537
- {
19538
- type: "button",
19539
- onClick: save,
19540
- className: "rounded-[6px] bg-bridge-primary px-3 py-2 text-[13px] font-medium text-primary-foreground transition-opacity hover:opacity-90",
19541
- children: "Save"
19542
- }
19543
- )
19544
- ] })
19545
- ] }) });
19162
+ return /* @__PURE__ */ jsxs11(
19163
+ "div",
19164
+ {
19165
+ style: {
19166
+ display: "flex",
19167
+ alignItems: "center",
19168
+ gap: 6,
19169
+ height: 28,
19170
+ padding: "0 12px",
19171
+ borderRadius: 999,
19172
+ background: "#ffffff",
19173
+ width: "100%",
19174
+ maxWidth: 260,
19175
+ boxSizing: "border-box",
19176
+ boxShadow: invalid ? "inset 0 0 0 1.5px #EF4444" : "none"
19177
+ },
19178
+ children: [
19179
+ /* @__PURE__ */ jsx20(
19180
+ "span",
19181
+ {
19182
+ style: {
19183
+ flex: "0 0 auto",
19184
+ color: OHW_BLUE_DARK,
19185
+ fontSize: 10,
19186
+ fontWeight: 700,
19187
+ textTransform: "uppercase",
19188
+ letterSpacing: 0.4
19189
+ },
19190
+ children: "Link"
19191
+ }
19192
+ ),
19193
+ /* @__PURE__ */ jsx20(
19194
+ "input",
19195
+ {
19196
+ type: "text",
19197
+ inputMode: "url",
19198
+ value: draft,
19199
+ onChange: (e) => setDraft(e.target.value),
19200
+ onBlur: commit,
19201
+ onKeyDown: (e) => {
19202
+ if (e.key === "Enter") e.currentTarget.blur();
19203
+ if (e.key === "Escape") {
19204
+ setDraft(value);
19205
+ setInvalid(false);
19206
+ e.currentTarget.blur();
19207
+ }
19208
+ },
19209
+ placeholder: "https://example.com",
19210
+ "aria-label": "Image link URL",
19211
+ "aria-invalid": invalid,
19212
+ style: {
19213
+ flex: 1,
19214
+ minWidth: 0,
19215
+ border: "none",
19216
+ outline: "none",
19217
+ background: "transparent",
19218
+ fontSize: 12,
19219
+ lineHeight: "16px",
19220
+ fontFamily: "Figtree, ui-sans-serif, system-ui, sans-serif",
19221
+ color: "#1f2937"
19222
+ }
19223
+ }
19224
+ )
19225
+ ]
19226
+ }
19227
+ );
19546
19228
  }
19547
19229
  function MediaStripToolbar({
19548
19230
  entry,
19549
19231
  index,
19550
19232
  pending,
19551
19233
  error,
19552
- itemEditOn,
19553
- onToggleItemEdit,
19554
19234
  onSetVariant,
19555
19235
  onSetAlign,
19556
19236
  onSetHover,
19557
19237
  onSetTreatment,
19558
19238
  onResize,
19559
- onOpenLink,
19239
+ onSetAlt,
19240
+ onSetLink,
19560
19241
  onAddItem,
19561
19242
  onRemoveItem,
19562
19243
  onChangeImage,
@@ -19565,102 +19246,81 @@ function MediaStripToolbar({
19565
19246
  }) {
19566
19247
  const items = mediaStripItems(entry.tree);
19567
19248
  const itemCount = items.length;
19249
+ const currentAlt = items[index]?.alt ?? "";
19250
+ const currentHref = items[index]?.href ?? "";
19251
+ const currentTarget = items[index]?.linkTarget === "same-tab" ? "same-tab" : "new-tab";
19568
19252
  const slots = findMediaStripSlots(entry.tree);
19569
19253
  const variant = slots?.variant === "reel" ? "reel" : "logos";
19570
19254
  const align = slots?.align === "left" || slots?.align === "right" ? slots.align : "center";
19571
19255
  const hover = slots?.hover === "none" || slots?.hover === "lift" || slots?.hover === "zoom" || slots?.hover === "fade" ? slots.hover : "lift";
19572
19256
  const treatment = slots?.treatment === "color" || slots?.treatment === "grayscale" ? slots.treatment : variant === "logos" ? "grayscale" : "color";
19573
19257
  const sizeValue = variant === "logos" ? typeof slots?.logoMaxHeight === "number" ? slots.logoMaxHeight : 48 : typeof slots?.cardWidth === "number" ? slots.cardWidth : 240;
19574
- const mobile = window.innerWidth <= 480;
19575
- const gatedLabel = (label) => itemEditOn ? label : "Turn on the star to edit this image";
19576
- return /* @__PURE__ */ jsx21(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsxs12(
19258
+ const isMobile = window.innerWidth <= 480;
19259
+ const rowStyle = isMobile ? {
19260
+ display: "flex",
19261
+ gap: 6,
19262
+ flexWrap: "nowrap",
19263
+ justifyContent: "center",
19264
+ overflowX: "auto",
19265
+ WebkitOverflowScrolling: "touch",
19266
+ width: "100%",
19267
+ scrollbarWidth: "none"
19268
+ } : { display: "flex", gap: 6, flexWrap: "nowrap", justifyContent: "center" };
19269
+ return /* @__PURE__ */ jsxs11(
19577
19270
  "div",
19578
19271
  {
19579
19272
  "data-ohw-bridge": "",
19580
19273
  "data-ohw-toolbar": "",
19581
19274
  onMouseDown: (e) => e.stopPropagation(),
19582
- className: cn("flex flex-col items-center gap-1.5 font-sans", pending && "pointer-events-none opacity-60"),
19583
- style: { maxWidth: mobile ? "calc(100vw - 24px)" : void 0 },
19275
+ style: {
19276
+ display: "flex",
19277
+ flexDirection: "column",
19278
+ gap: 6,
19279
+ alignItems: "center",
19280
+ background: OHW_BLUE,
19281
+ padding: 8,
19282
+ borderRadius: 12,
19283
+ boxShadow: "0 8px 24px rgba(0,0,0,0.25)",
19284
+ pointerEvents: pending ? "none" : "auto",
19285
+ opacity: pending ? 0.6 : 1,
19286
+ maxWidth: isMobile ? "calc(100vw - 24px)" : "none"
19287
+ },
19584
19288
  children: [
19585
- /* @__PURE__ */ jsxs12(
19586
- "div",
19587
- {
19588
- className: cn(
19589
- "flex items-center gap-1 rounded-[10px] border border-border bg-background p-1 whitespace-nowrap",
19590
- "shadow-[0px_10px_15px_-3px_rgba(0,0,0,0.1),0px_4px_6px_-4px_rgba(0,0,0,0.1)]",
19591
- mobile ? "flex-wrap justify-center" : "flex-nowrap"
19592
- ),
19593
- children: [
19594
- /* @__PURE__ */ jsxs12(Tooltip, { children: [
19595
- /* @__PURE__ */ jsx21(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx21(
19596
- CustomToolbarButton,
19597
- {
19598
- type: "button",
19599
- "aria-label": itemEditOn ? "Done editing this image" : "Edit this image",
19600
- "aria-pressed": itemEditOn,
19601
- onClick: onToggleItemEdit,
19602
- className: "group size-8",
19603
- children: /* @__PURE__ */ jsx21(
19604
- Star,
19605
- {
19606
- size: 16,
19607
- fill: "currentColor",
19608
- strokeWidth: 0,
19609
- className: cn(
19610
- "transition-colors",
19611
- itemEditOn ? "text-[#f59e0b] group-hover:text-[#d97706]" : "text-[#d6d3d1] group-hover:text-[#fbbf24]"
19612
- ),
19613
- style: { pointerEvents: "none" }
19614
- }
19615
- )
19616
- }
19617
- ) }),
19618
- /* @__PURE__ */ jsx21(TooltipContent, { side: "bottom", sideOffset: 8, children: itemEditOn ? "Done editing this image" : "Edit this image" })
19619
- ] }),
19620
- /* @__PURE__ */ jsx21(ToolbarSeparator, {}),
19621
- /* @__PURE__ */ jsx21(ToolbarAction, { label: gatedLabel("Change image"), onClick: onChangeImage, disabled: !itemEditOn, children: /* @__PURE__ */ jsx21(ImageIcon2, { size: 16, style: { pointerEvents: "none" } }) }),
19622
- /* @__PURE__ */ jsx21(ToolbarAction, { label: gatedLabel("Link"), onClick: onOpenLink, disabled: !itemEditOn, children: /* @__PURE__ */ jsx21(LinkIcon, { size: 16, style: { pointerEvents: "none" } }) }),
19623
- /* @__PURE__ */ jsx21(ToolbarAction, { label: gatedLabel("Move left"), onClick: onMoveLeft, disabled: !itemEditOn || index <= 0, children: /* @__PURE__ */ jsx21(ArrowLeft2, { size: 16, style: { pointerEvents: "none" } }) }),
19624
- /* @__PURE__ */ jsx21(
19625
- ToolbarAction,
19626
- {
19627
- label: gatedLabel("Move right"),
19628
- onClick: onMoveRight,
19629
- disabled: !itemEditOn || index >= itemCount - 1,
19630
- children: /* @__PURE__ */ jsx21(ArrowRight2, { size: 16, style: { pointerEvents: "none" } })
19631
- }
19632
- ),
19633
- /* @__PURE__ */ jsx21(ToolbarAction, { label: gatedLabel("Remove image"), onClick: onRemoveItem, disabled: !itemEditOn || itemCount <= 1, children: /* @__PURE__ */ jsx21(ImageMinus, { size: 16, style: { pointerEvents: "none" } }) }),
19634
- /* @__PURE__ */ jsx21(ToolbarSeparator, {}),
19635
- /* @__PURE__ */ jsx21(OptionMenu, { label: "Layout", value: variant, options: VARIANTS, onChange: onSetVariant }),
19636
- /* @__PURE__ */ jsx21(OptionMenu, { label: "Hover", value: hover, options: HOVERS, onChange: onSetHover }),
19637
- /* @__PURE__ */ jsx21(
19638
- ToolbarAction,
19639
- {
19640
- label: treatment === "grayscale" ? "Show in color" : "Show grayscale",
19641
- active: treatment === "grayscale",
19642
- onClick: () => onSetTreatment(treatment === "grayscale" ? "color" : "grayscale"),
19643
- children: /* @__PURE__ */ jsx21(Palette, { size: 16, style: { pointerEvents: "none" } })
19644
- }
19645
- ),
19646
- /* @__PURE__ */ jsx21(ToolbarSeparator, {}),
19647
- ALIGNS.map(({ value, label, Icon }) => /* @__PURE__ */ jsx21(ToolbarAction, { label, active: align === value, onClick: () => onSetAlign(value), children: /* @__PURE__ */ jsx21(Icon, { size: 16, style: { pointerEvents: "none" } }) }, value)),
19648
- /* @__PURE__ */ jsx21(ToolbarSeparator, {}),
19649
- /* @__PURE__ */ jsx21(ToolbarAction, { label: "Smaller", onClick: () => onResize(-8), children: /* @__PURE__ */ jsx21(Minus, { size: 16, style: { pointerEvents: "none" } }) }),
19650
- /* @__PURE__ */ jsxs12("span", { className: "min-w-[40px] text-center text-xs tabular-nums text-muted-foreground", children: [
19651
- sizeValue,
19652
- "px"
19653
- ] }),
19654
- /* @__PURE__ */ jsx21(ToolbarAction, { label: "Larger", onClick: () => onResize(8), children: /* @__PURE__ */ jsx21(Plus2, { size: 16, style: { pointerEvents: "none" } }) }),
19655
- /* @__PURE__ */ jsx21(ToolbarSeparator, {}),
19656
- /* @__PURE__ */ jsx21(ToolbarAction, { label: "Add image", onClick: onAddItem, children: /* @__PURE__ */ jsx21(ImagePlus, { size: 16, style: { pointerEvents: "none" } }) })
19657
- ]
19658
- }
19659
- ),
19660
- error && /* @__PURE__ */ jsx21("div", { className: "max-w-[260px] rounded-md bg-background px-2 py-1 text-center text-[11px] text-destructive shadow-sm", children: error })
19289
+ /* @__PURE__ */ jsxs11("div", { style: rowStyle, children: [
19290
+ VARIANTS.map((v) => /* @__PURE__ */ jsx20(Pill, { label: v.label, active: variant === v.value, onClick: () => onSetVariant(v.value) }, v.value)),
19291
+ ALIGNS.map((a) => /* @__PURE__ */ jsx20(Pill, { label: a.label, active: align === a.value, onClick: () => onSetAlign(a.value) }, a.value))
19292
+ ] }),
19293
+ /* @__PURE__ */ jsxs11("div", { style: rowStyle, children: [
19294
+ HOVERS.map((h) => /* @__PURE__ */ jsx20(Pill, { label: h.label, active: hover === h.value, onClick: () => onSetHover(h.value) }, h.value)),
19295
+ TREATMENTS.map((t) => /* @__PURE__ */ jsx20(Pill, { label: t.label, active: treatment === t.value, onClick: () => onSetTreatment(t.value) }, t.value))
19296
+ ] }),
19297
+ /* @__PURE__ */ jsxs11("div", { style: { display: "flex", gap: 6, alignItems: "center" }, children: [
19298
+ /* @__PURE__ */ jsx20(Pill, { label: "\u2212", onClick: () => onResize(-8) }),
19299
+ /* @__PURE__ */ jsxs11("span", { style: { color: "#ffffff", fontSize: 12, minWidth: 34, textAlign: "center" }, children: [
19300
+ sizeValue,
19301
+ "px"
19302
+ ] }),
19303
+ /* @__PURE__ */ jsx20(Pill, { label: "+", onClick: () => onResize(8) })
19304
+ ] }),
19305
+ /* @__PURE__ */ jsx20("div", { style: { display: "flex", justifyContent: "center", width: "100%" }, children: /* @__PURE__ */ jsx20(AltInput, { value: currentAlt, onCommit: onSetAlt }) }),
19306
+ /* @__PURE__ */ jsx20("div", { style: { display: "flex", justifyContent: "center", width: "100%" }, children: /* @__PURE__ */ jsx20(LinkInput, { value: currentHref, onCommit: (href) => onSetLink(href, currentTarget) }) }),
19307
+ currentHref && /* @__PURE__ */ jsxs11("div", { style: rowStyle, children: [
19308
+ /* @__PURE__ */ jsx20(Pill, { label: "New tab", active: currentTarget === "new-tab", onClick: () => onSetLink(currentHref, "new-tab") }),
19309
+ /* @__PURE__ */ jsx20(Pill, { label: "Same tab", active: currentTarget === "same-tab", onClick: () => onSetLink(currentHref, "same-tab") })
19310
+ ] }),
19311
+ /* @__PURE__ */ jsxs11("div", { style: rowStyle, children: [
19312
+ /* @__PURE__ */ jsx20(Pill, { label: "Change Image", onClick: onChangeImage }),
19313
+ /* @__PURE__ */ jsx20(Pill, { label: "+ Item", onClick: onAddItem }),
19314
+ /* @__PURE__ */ jsx20(Pill, { label: "\u2212 Item", onClick: onRemoveItem, disabled: itemCount <= 1 })
19315
+ ] }),
19316
+ /* @__PURE__ */ jsxs11("div", { style: rowStyle, children: [
19317
+ /* @__PURE__ */ jsx20(Pill, { label: "\u2190 Move", onClick: onMoveLeft, disabled: index <= 0 }),
19318
+ /* @__PURE__ */ jsx20(Pill, { label: "\u2192 Move", onClick: onMoveRight, disabled: index >= itemCount - 1 })
19319
+ ] }),
19320
+ error && /* @__PURE__ */ jsx20("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
19661
19321
  ]
19662
19322
  }
19663
- ) });
19323
+ );
19664
19324
  }
19665
19325
  function findMediaStripSlots(tree) {
19666
19326
  const stack = tree.rows.flatMap((row) => row.blocks);
@@ -19679,23 +19339,31 @@ function AiMediaStripOverlay({
19679
19339
  }) {
19680
19340
  const [hover, setHover] = useState8(null);
19681
19341
  const [active, setActive] = useState8(null);
19682
- const [itemEditOn, setItemEditOn] = useState8(false);
19683
- const [linkOpen, setLinkOpen] = useState8(false);
19684
19342
  const [pending, setPending] = useState8(false);
19685
19343
  const [error, setError] = useState8(null);
19686
19344
  const activeRef = useRef5(null);
19687
19345
  activeRef.current = active;
19688
- const linkOpenRef = useRef5(false);
19689
- linkOpenRef.current = linkOpen;
19690
19346
  useEffect8(() => {
19691
19347
  const onMove = (e) => {
19692
- const ref = itemRefFromTarget(e.target);
19693
19348
  const t = e.target;
19694
- const overEditable = Boolean(t?.closest?.("[data-ohw-editable]"));
19695
- setHover((prev) => {
19696
- const next = ref && !overEditable ? ref : null;
19697
- return sameRef(prev, next) ? prev : next;
19698
- });
19349
+ if (!(t instanceof HTMLElement)) return;
19350
+ 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]")) {
19351
+ return;
19352
+ }
19353
+ const itemEl = t.closest("[data-ohw-media-strip-item]");
19354
+ if (!itemEl) {
19355
+ setHover(null);
19356
+ return;
19357
+ }
19358
+ const sectionEl = itemEl.closest("[data-ohw-section]");
19359
+ const unit = sectionEl ? movableUnit(sectionEl) : null;
19360
+ const sectionId = unit ? instanceIdOf(unit) || null : null;
19361
+ const index = Number(itemEl.dataset.ohwMediaStripIndex);
19362
+ if (!sectionId || Number.isNaN(index)) {
19363
+ setHover(null);
19364
+ return;
19365
+ }
19366
+ setHover({ sectionId, index });
19699
19367
  };
19700
19368
  const onLeave = () => setHover(null);
19701
19369
  document.addEventListener("mousemove", onMove, { passive: true });
@@ -19707,25 +19375,23 @@ function AiMediaStripOverlay({
19707
19375
  }, []);
19708
19376
  useEffect8(() => {
19709
19377
  const onClick = (e) => {
19378
+ const current = activeRef.current;
19379
+ if (!current) return;
19710
19380
  const t = e.target;
19711
- if (!(t instanceof Element)) return;
19712
- if (t.closest(CHROME_SELECTOR)) return;
19713
- const ref = itemRefFromTarget(t);
19714
- if (sameRef(activeRef.current, ref)) return;
19715
- if (ref) clearInteractionChromeRef.current();
19716
- setItemEditOn(false);
19381
+ if (!(t instanceof HTMLElement)) return;
19382
+ if (t.closest("[data-ohw-item-interaction]")) return;
19383
+ const itemEl = findMediaStripItemElement(current.sectionId, current.index);
19384
+ if (itemEl && itemEl.contains(t)) return;
19385
+ setActive(null);
19717
19386
  setError(null);
19718
- setActive(ref);
19719
19387
  };
19720
19388
  document.addEventListener("click", onClick, true);
19721
19389
  return () => document.removeEventListener("click", onClick, true);
19722
- }, [clearInteractionChromeRef]);
19390
+ }, []);
19723
19391
  useEffect8(() => {
19724
19392
  const onBlur = () => {
19725
19393
  if (!activeRef.current) return;
19726
19394
  setActive(null);
19727
- setItemEditOn(false);
19728
- setLinkOpen(false);
19729
19395
  setError(null);
19730
19396
  };
19731
19397
  window.addEventListener("blur", onBlur);
@@ -19735,10 +19401,7 @@ function AiMediaStripOverlay({
19735
19401
  const onKeyDown = (e) => {
19736
19402
  if (e.key !== "Escape") return;
19737
19403
  if (!activeRef.current) return;
19738
- if (linkOpenRef.current) return;
19739
- if (document.querySelector('[data-ohw-more-menu][data-state="open"]')) return;
19740
19404
  setActive(null);
19741
- setItemEditOn(false);
19742
19405
  setError(null);
19743
19406
  };
19744
19407
  document.addEventListener("keydown", onKeyDown);
@@ -19772,133 +19435,137 @@ function AiMediaStripOverlay({
19772
19435
  tree: nextTree,
19773
19436
  label: entry.label,
19774
19437
  media: extraMedia ? { ...entry.media, ...extraMedia } : entry.media,
19775
- ...entry.brand ? { brand: entry.brand } : {},
19776
- // The user is already looking at the strip they just edited — recentering the viewport
19777
- // on every edit reads as the section jumping around.
19778
- skipScrollIntoView: true
19438
+ ...entry.brand ? { brand: entry.brand } : {}
19779
19439
  });
19780
19440
  return true;
19781
19441
  },
19782
19442
  [findEntry, postToParent2, applyTreeLocalRef]
19783
19443
  );
19784
19444
  const activeEntry = active ? findEntry(active.sectionId) : null;
19445
+ const hoverHandleRect = useLiveItemRect(!active && hover ? hover : null);
19785
19446
  const activeItemRect = useLiveItemRect(active);
19786
- const activeStripRect = useLiveItemRect(active, true);
19787
- const hoverRect = useLiveItemRect(hover && !sameRef(hover, active) ? hover : null);
19788
- const activeItems = activeEntry ? mediaStripItems(activeEntry.tree) : [];
19789
- const activeItem = active ? activeItems[active.index] : void 0;
19790
- const activeHref = activeItem?.href ?? "";
19791
- const activeTarget = activeItem?.linkTarget === "same-tab" ? "same-tab" : "new-tab";
19792
- return /* @__PURE__ */ jsxs12(Fragment7, { children: [
19793
- hoverRect && /* @__PURE__ */ jsx21(ItemInteractionLayer, { rect: hoverRect, state: "hover" }),
19794
- activeItemRect && activeStripRect && active && activeEntry && /* @__PURE__ */ jsxs12(Fragment7, { children: [
19795
- /* @__PURE__ */ jsx21(ItemInteractionLayer, { rect: activeItemRect, state: "active-top" }),
19796
- /* @__PURE__ */ jsx21(StripToolbarSlot, { stripRect: activeStripRect, children: /* @__PURE__ */ jsx21(
19797
- MediaStripToolbar,
19798
- {
19799
- entry: activeEntry,
19800
- index: active.index,
19801
- pending,
19802
- error,
19803
- itemEditOn,
19804
- onToggleItemEdit: () => setItemEditOn((on) => !on),
19805
- onSetVariant: (variant) => runMutation(active.sectionId, (tree) => setMediaStripVariant(tree, variant)),
19806
- onSetAlign: (align) => runMutation(active.sectionId, (tree) => setMediaStripAlign(tree, align)),
19807
- onSetHover: (hoverPreset) => runMutation(active.sectionId, (tree) => setMediaStripHover(tree, hoverPreset)),
19808
- onSetTreatment: (treatment) => runMutation(active.sectionId, (tree) => setMediaStripTreatment(tree, treatment)),
19809
- onResize: (delta) => {
19810
- const slots = findMediaStripSlots(activeEntry.tree);
19811
- const variant = slots?.variant === "reel" ? "reel" : "logos";
19812
- const current = variant === "logos" ? typeof slots?.logoMaxHeight === "number" ? slots.logoMaxHeight : 48 : typeof slots?.cardWidth === "number" ? slots.cardWidth : 240;
19813
- void runMutation(active.sectionId, (tree) => setMediaStripSizing(tree, current + delta));
19814
- },
19815
- onOpenLink: () => setLinkOpen(true),
19816
- onAddItem: () => {
19817
- void requestAssetPick(postToParent2).then((picked) => {
19818
- if (!picked) return;
19819
- const item = { assetId: picked.assetId, alt: "Logo" };
19447
+ const TOOLBAR_APPROX_H = 170;
19448
+ const toolbarState = activeItemRect && activeItemRect.top < TOOLBAR_APPROX_H ? "active-bottom" : "active-top";
19449
+ return /* @__PURE__ */ jsxs11(Fragment7, { children: [
19450
+ hoverHandleRect && hover && /* @__PURE__ */ jsx20(
19451
+ "button",
19452
+ {
19453
+ type: "button",
19454
+ "data-ohw-toolbar": "",
19455
+ onClick: () => {
19456
+ clearInteractionChromeRef.current();
19457
+ setActive(hover);
19458
+ },
19459
+ "aria-label": "Configure media item",
19460
+ style: {
19461
+ position: "fixed",
19462
+ top: hoverHandleRect.top + 8,
19463
+ left: hoverHandleRect.left + hoverHandleRect.width - 36,
19464
+ width: 28,
19465
+ height: 28,
19466
+ borderRadius: 8,
19467
+ border: `1.5px dashed ${PRIMARY3}`,
19468
+ background: "#ffffff",
19469
+ color: PRIMARY3,
19470
+ display: "flex",
19471
+ alignItems: "center",
19472
+ justifyContent: "center",
19473
+ cursor: "pointer",
19474
+ zIndex: 2147483643,
19475
+ fontSize: 14,
19476
+ fontWeight: 700
19477
+ },
19478
+ children: /* @__PURE__ */ jsx20(Star, { size: 14, style: { pointerEvents: "none" } })
19479
+ }
19480
+ ),
19481
+ activeItemRect && active && activeEntry && /* @__PURE__ */ jsx20(
19482
+ ItemInteractionLayer,
19483
+ {
19484
+ rect: activeItemRect,
19485
+ state: toolbarState,
19486
+ toolbar: /* @__PURE__ */ jsx20(
19487
+ MediaStripToolbar,
19488
+ {
19489
+ entry: activeEntry,
19490
+ index: active.index,
19491
+ pending,
19492
+ error,
19493
+ onSetVariant: (variant) => runMutation(active.sectionId, (tree) => setMediaStripVariant(tree, variant)),
19494
+ onSetAlign: (align) => runMutation(active.sectionId, (tree) => setMediaStripAlign(tree, align)),
19495
+ onSetHover: (hoverPreset) => runMutation(active.sectionId, (tree) => setMediaStripHover(tree, hoverPreset)),
19496
+ onSetTreatment: (treatment) => runMutation(active.sectionId, (tree) => setMediaStripTreatment(tree, treatment)),
19497
+ onResize: (delta) => {
19498
+ const slots = findMediaStripSlots(activeEntry.tree);
19499
+ const variant = slots?.variant === "reel" ? "reel" : "logos";
19500
+ const current = variant === "logos" ? typeof slots?.logoMaxHeight === "number" ? slots.logoMaxHeight : 48 : typeof slots?.cardWidth === "number" ? slots.cardWidth : 240;
19501
+ void runMutation(active.sectionId, (tree) => setMediaStripSizing(tree, current + delta));
19502
+ },
19503
+ onSetAlt: (alt) => {
19504
+ void runMutation(active.sectionId, (tree) => setMediaStripItemAlt(tree, active.index, alt));
19505
+ },
19506
+ onSetLink: (href, linkTarget) => {
19820
19507
  void runMutation(
19821
19508
  active.sectionId,
19822
- (tree) => addMediaStripItem(tree, item, active.index),
19823
- { [picked.assetId]: picked.url }
19824
- ).then((applied) => {
19825
- if (applied) setActive({ sectionId: active.sectionId, index: active.index + 1 });
19509
+ (tree) => setMediaStripItemLink(tree, active.index, href ? { href, linkTarget } : {})
19510
+ );
19511
+ },
19512
+ onAddItem: () => {
19513
+ void requestAssetPick(postToParent2).then((picked) => {
19514
+ if (!picked) return;
19515
+ const item = { assetId: picked.assetId, alt: "Logo" };
19516
+ void runMutation(
19517
+ active.sectionId,
19518
+ (tree) => addMediaStripItem(tree, item, active.index),
19519
+ { [picked.assetId]: picked.url }
19520
+ ).then((applied) => {
19521
+ if (applied) setActive({ sectionId: active.sectionId, index: active.index + 1 });
19522
+ });
19826
19523
  });
19827
- });
19828
- },
19829
- onChangeImage: () => {
19830
- void requestAssetPick(postToParent2).then((picked) => {
19831
- if (!picked) return;
19832
- void runMutation(
19833
- active.sectionId,
19834
- (tree) => setMediaStripItemAsset(tree, active.index, picked.assetId),
19835
- { [picked.assetId]: picked.url }
19524
+ },
19525
+ onChangeImage: () => {
19526
+ void requestAssetPick(postToParent2).then((picked) => {
19527
+ if (!picked) return;
19528
+ void runMutation(
19529
+ active.sectionId,
19530
+ (tree) => setMediaStripItemAsset(tree, active.index, picked.assetId),
19531
+ { [picked.assetId]: picked.url }
19532
+ );
19533
+ });
19534
+ },
19535
+ onRemoveItem: () => {
19536
+ void runMutation(active.sectionId, (tree) => removeMediaStripItem(tree, active.index)).then(
19537
+ (applied) => {
19538
+ if (applied) setActive(null);
19539
+ }
19836
19540
  );
19837
- });
19838
- },
19839
- onRemoveItem: () => {
19840
- void runMutation(active.sectionId, (tree) => removeMediaStripItem(tree, active.index)).then(
19841
- (applied) => {
19842
- if (applied) setActive(null);
19843
- }
19844
- );
19845
- },
19846
- onMoveLeft: () => {
19847
- void runMutation(active.sectionId, (tree) => reorderMediaStripItem(tree, active.index, -1)).then(
19848
- (applied) => {
19849
- if (applied) setActive({ sectionId: active.sectionId, index: active.index - 1 });
19850
- }
19851
- );
19852
- },
19853
- onMoveRight: () => {
19854
- void runMutation(active.sectionId, (tree) => reorderMediaStripItem(tree, active.index, 1)).then(
19855
- (applied) => {
19856
- if (applied) setActive({ sectionId: active.sectionId, index: active.index + 1 });
19857
- }
19858
- );
19859
- }
19860
- }
19861
- ) }),
19862
- /* @__PURE__ */ jsx21(
19863
- "div",
19864
- {
19865
- "data-ohw-bridge": "",
19866
- "data-ohw-item-interaction": "",
19867
- "aria-hidden": true,
19868
- className: "pointer-events-none fixed rounded-[4px] px-[7px] py-[3px] font-sans text-[11px] font-medium leading-none text-primary-foreground whitespace-nowrap",
19869
- style: {
19870
- top: activeItemRect.top + activeItemRect.height + 10,
19871
- left: activeItemRect.left - 5,
19872
- background: PRIMARY3,
19873
- zIndex: 2147483644
19874
- },
19875
- children: "Media item"
19876
- }
19877
- ),
19878
- linkOpen && /* @__PURE__ */ jsx21(
19879
- LinkModal,
19880
- {
19881
- href: activeHref,
19882
- linkTarget: activeTarget,
19883
- onClose: () => setLinkOpen(false),
19884
- onSave: (href, linkTarget) => {
19885
- void runMutation(
19886
- active.sectionId,
19887
- (tree) => setMediaStripItemLink(tree, active.index, href ? { href, linkTarget } : {})
19888
- );
19541
+ },
19542
+ onMoveLeft: () => {
19543
+ void runMutation(active.sectionId, (tree) => reorderMediaStripItem(tree, active.index, -1)).then(
19544
+ (applied) => {
19545
+ if (applied) setActive({ sectionId: active.sectionId, index: active.index - 1 });
19546
+ }
19547
+ );
19548
+ },
19549
+ onMoveRight: () => {
19550
+ void runMutation(active.sectionId, (tree) => reorderMediaStripItem(tree, active.index, 1)).then(
19551
+ (applied) => {
19552
+ if (applied) setActive({ sectionId: active.sectionId, index: active.index + 1 });
19553
+ }
19554
+ );
19555
+ }
19889
19556
  }
19890
- }
19891
- )
19892
- ] })
19557
+ )
19558
+ }
19559
+ )
19893
19560
  ] });
19894
19561
  }
19895
19562
 
19896
19563
  // src/ui/ai-section/AiPricingCardOverlay.tsx
19897
- import { useCallback as useCallback4, useEffect as useEffect9, useLayoutEffect as useLayoutEffect5, useRef as useRef6, useState as useState9 } from "react";
19898
- import { Check as Check3, ChevronDown as ChevronDown4, Minus as Minus2, Plus as Plus3, Star as Star2 } from "lucide-react";
19899
- import { Fragment as Fragment8, jsx as jsx22, jsxs as jsxs13 } from "react/jsx-runtime";
19564
+ import { useCallback as useCallback4, useEffect as useEffect9, useLayoutEffect as useLayoutEffect4, useRef as useRef6, useState as useState9 } from "react";
19565
+ import { Check as Check2, ChevronDown as ChevronDown3, Minus, Plus as Plus2, Star as Star2 } from "lucide-react";
19566
+ import { Fragment as Fragment8, jsx as jsx21, jsxs as jsxs12 } from "react/jsx-runtime";
19900
19567
  var PRIMARY4 = "var(--ohw-primary, #0885FE)";
19901
- 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]";
19568
+ 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]";
19902
19569
  function findPricingCardElement(sectionId, index) {
19903
19570
  const sectionEl = findByInstanceId(sectionId);
19904
19571
  if (!sectionEl) return null;
@@ -19910,7 +19577,7 @@ function findPricingCardElement(sectionId, index) {
19910
19577
  }
19911
19578
  function cardRefFromTarget(target) {
19912
19579
  if (!(target instanceof Element)) return null;
19913
- if (target.closest(CHROME_SELECTOR2)) return null;
19580
+ if (target.closest(CHROME_SELECTOR)) return null;
19914
19581
  const cardEl = target.closest("[data-ohw-pricing-card]");
19915
19582
  if (!cardEl) return null;
19916
19583
  const sectionEl = cardEl.closest("[data-ohw-section]");
@@ -19920,21 +19587,21 @@ function cardRefFromTarget(target) {
19920
19587
  if (!sectionId || Number.isNaN(index)) return null;
19921
19588
  return { sectionId, index };
19922
19589
  }
19923
- function sameRef2(a, b) {
19590
+ function sameRef(a, b) {
19924
19591
  return a?.sectionId === b?.sectionId && a?.index === b?.index;
19925
19592
  }
19926
- function rectOf2(el) {
19593
+ function rectOf(el) {
19927
19594
  if (!el) return null;
19928
19595
  const r2 = el.getBoundingClientRect();
19929
19596
  if (r2.width <= 0 || r2.height <= 0) return null;
19930
19597
  return { top: r2.top, left: r2.left, width: r2.width, height: r2.height };
19931
19598
  }
19932
19599
  function readRect3(ref) {
19933
- return rectOf2(ref && findPricingCardElement(ref.sectionId, ref.index));
19600
+ return rectOf(ref && findPricingCardElement(ref.sectionId, ref.index));
19934
19601
  }
19935
19602
  function readGridRect(ref) {
19936
19603
  const card = ref && findPricingCardElement(ref.sectionId, ref.index);
19937
- return rectOf2(card?.closest("[data-ohw-pricing-collection]") ?? card);
19604
+ return rectOf(card?.closest("[data-ohw-pricing-collection]") ?? card);
19938
19605
  }
19939
19606
  function useLiveCardRect(ref, grid = false) {
19940
19607
  const [rect, setRect] = useState9(null);
@@ -19964,19 +19631,19 @@ function useLiveCardRect(ref, grid = false) {
19964
19631
  }
19965
19632
  var TOOLBAR_GRID_GAP = 52;
19966
19633
  var TOOLBAR_GRID_GAP_MOBILE = 42;
19967
- var TOOLBAR_EDGE_MARGIN3 = 4;
19634
+ var TOOLBAR_EDGE_MARGIN2 = 4;
19968
19635
  function GridToolbarSlot({ gridRect, children }) {
19969
19636
  const slotRef = useRef6(null);
19970
19637
  const [shift, setShift] = useState9(0);
19971
19638
  const centerX = gridRect.left + gridRect.width / 2;
19972
- useLayoutEffect5(() => {
19639
+ useLayoutEffect4(() => {
19973
19640
  const slot = slotRef.current;
19974
19641
  if (!slot) return;
19975
19642
  const clamp = () => {
19976
19643
  const w = slot.offsetWidth;
19977
19644
  if (w <= 0) return;
19978
19645
  const half = w / 2;
19979
- const clamped = Math.max(TOOLBAR_EDGE_MARGIN3 + half, Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN3 - half));
19646
+ const clamped = Math.max(TOOLBAR_EDGE_MARGIN2 + half, Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN2 - half));
19980
19647
  setShift((prev) => Math.abs(prev - (clamped - centerX)) < 0.5 ? prev : clamped - centerX);
19981
19648
  };
19982
19649
  clamp();
@@ -19985,7 +19652,7 @@ function GridToolbarSlot({ gridRect, children }) {
19985
19652
  return () => ro.disconnect();
19986
19653
  }, [centerX, children]);
19987
19654
  const mobile = window.innerWidth <= 480;
19988
- return /* @__PURE__ */ jsx22(
19655
+ return /* @__PURE__ */ jsx21(
19989
19656
  "div",
19990
19657
  {
19991
19658
  ref: slotRef,
@@ -20020,7 +19687,7 @@ function requestValidateTree2(postToParent2, tree) {
20020
19687
  });
20021
19688
  }
20022
19689
  function StyleSwatch({ style }) {
20023
- return /* @__PURE__ */ jsx22(
19690
+ return /* @__PURE__ */ jsx21(
20024
19691
  "span",
20025
19692
  {
20026
19693
  "aria-hidden": true,
@@ -20034,35 +19701,35 @@ function StyleSwatch({ style }) {
20034
19701
  );
20035
19702
  }
20036
19703
  function ButtonGlyph() {
20037
- return /* @__PURE__ */ jsxs13("svg", { width: "18", height: "12", viewBox: "0 0 18 12", fill: "none", "aria-hidden": true, style: { pointerEvents: "none" }, children: [
20038
- /* @__PURE__ */ jsx22("rect", { x: "0.75", y: "0.75", width: "16.5", height: "10.5", rx: "3", stroke: "currentColor", strokeWidth: "1.5" }),
20039
- /* @__PURE__ */ jsx22("path", { d: "M5.5 6H12.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
19704
+ return /* @__PURE__ */ jsxs12("svg", { width: "18", height: "12", viewBox: "0 0 18 12", fill: "none", "aria-hidden": true, style: { pointerEvents: "none" }, children: [
19705
+ /* @__PURE__ */ jsx21("rect", { x: "0.75", y: "0.75", width: "16.5", height: "10.5", rx: "3", stroke: "currentColor", strokeWidth: "1.5" }),
19706
+ /* @__PURE__ */ jsx21("path", { d: "M5.5 6H12.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
20040
19707
  ] });
20041
19708
  }
20042
19709
  function FeatureGlyph({ mode }) {
20043
- return /* @__PURE__ */ jsxs13("svg", { width: "18", height: "18", viewBox: "0 0 18 18", fill: "none", "aria-hidden": true, style: { pointerEvents: "none" }, children: [
20044
- /* @__PURE__ */ jsx22("circle", { cx: "8", cy: "8", r: "7.25", stroke: "currentColor", strokeWidth: "1.5" }),
20045
- /* @__PURE__ */ jsx22("path", { d: "M5 8.2L7.2 10.3L11 6", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }),
20046
- /* @__PURE__ */ jsx22("circle", { cx: "13.5", cy: "13.5", r: "4.25", fill: "var(--ohw-background, #ffffff)", stroke: "var(--ohw-background, #ffffff)", strokeWidth: "1" }),
20047
- /* @__PURE__ */ jsx22("circle", { cx: "13.5", cy: "13.5", r: "3.75", fill: "var(--ohw-background, #ffffff)", stroke: PRIMARY4, strokeWidth: "1" }),
20048
- /* @__PURE__ */ jsx22("path", { d: "M11.5 13.5H15.5", stroke: PRIMARY4, strokeWidth: "1.5", strokeLinecap: "round" }),
20049
- mode === "add" && /* @__PURE__ */ jsx22("path", { d: "M13.5 11.5V15.5", stroke: PRIMARY4, strokeWidth: "1.5", strokeLinecap: "round" })
19710
+ return /* @__PURE__ */ jsxs12("svg", { width: "18", height: "18", viewBox: "0 0 18 18", fill: "none", "aria-hidden": true, style: { pointerEvents: "none" }, children: [
19711
+ /* @__PURE__ */ jsx21("circle", { cx: "8", cy: "8", r: "7.25", stroke: "currentColor", strokeWidth: "1.5" }),
19712
+ /* @__PURE__ */ jsx21("path", { d: "M5 8.2L7.2 10.3L11 6", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }),
19713
+ /* @__PURE__ */ jsx21("circle", { cx: "13.5", cy: "13.5", r: "4.25", fill: "var(--ohw-background, #ffffff)", stroke: "var(--ohw-background, #ffffff)", strokeWidth: "1" }),
19714
+ /* @__PURE__ */ jsx21("circle", { cx: "13.5", cy: "13.5", r: "3.75", fill: "var(--ohw-background, #ffffff)", stroke: PRIMARY4, strokeWidth: "1" }),
19715
+ /* @__PURE__ */ jsx21("path", { d: "M11.5 13.5H15.5", stroke: PRIMARY4, strokeWidth: "1.5", strokeLinecap: "round" }),
19716
+ mode === "add" && /* @__PURE__ */ jsx21("path", { d: "M13.5 11.5V15.5", stroke: PRIMARY4, strokeWidth: "1.5", strokeLinecap: "round" })
20050
19717
  ] });
20051
19718
  }
20052
- function ToolbarAction2({
19719
+ function ToolbarAction({
20053
19720
  label,
20054
19721
  onClick,
20055
19722
  disabled = false,
20056
19723
  children
20057
19724
  }) {
20058
- const button = /* @__PURE__ */ jsx22(CustomToolbarButton, { type: "button", "aria-label": label, disabled, onClick, className: "size-8", children });
20059
- return /* @__PURE__ */ jsxs13(Tooltip, { children: [
20060
- /* @__PURE__ */ jsx22(TooltipTrigger, { asChild: true, children: disabled ? /* @__PURE__ */ jsx22("span", { className: "inline-flex", children: button }) : button }),
20061
- /* @__PURE__ */ jsx22(TooltipContent, { side: "bottom", sideOffset: 8, children: label })
19725
+ const button = /* @__PURE__ */ jsx21(CustomToolbarButton, { type: "button", "aria-label": label, disabled, onClick, className: "size-8", children });
19726
+ return /* @__PURE__ */ jsxs12(Tooltip, { children: [
19727
+ /* @__PURE__ */ jsx21(TooltipTrigger, { asChild: true, children: disabled ? /* @__PURE__ */ jsx21("span", { className: "inline-flex", children: button }) : button }),
19728
+ /* @__PURE__ */ jsx21(TooltipContent, { side: "bottom", sideOffset: 8, children: label })
20062
19729
  ] });
20063
19730
  }
20064
- function ToolbarSeparator2() {
20065
- return /* @__PURE__ */ jsx22("span", { "aria-hidden": true, className: "mx-0.5 block h-6 w-px shrink-0 bg-border" });
19731
+ function ToolbarSeparator() {
19732
+ return /* @__PURE__ */ jsx21("span", { "aria-hidden": true, className: "mx-0.5 block h-6 w-px shrink-0 bg-border" });
20066
19733
  }
20067
19734
  function CardStyleMenu({
20068
19735
  value,
@@ -20070,7 +19737,7 @@ function CardStyleMenu({
20070
19737
  disabled = false
20071
19738
  }) {
20072
19739
  const current = PRICING_CARD_STYLES.find((s) => s.value === value) ?? PRICING_CARD_STYLES[0];
20073
- const trigger = /* @__PURE__ */ jsxs13(
19740
+ const trigger = /* @__PURE__ */ jsxs12(
20074
19741
  "button",
20075
19742
  {
20076
19743
  type: "button",
@@ -20081,28 +19748,28 @@ function CardStyleMenu({
20081
19748
  disabled && "cursor-not-allowed opacity-40 hover:bg-transparent"
20082
19749
  ),
20083
19750
  children: [
20084
- /* @__PURE__ */ jsx22(StyleSwatch, { style: current.value }),
20085
- /* @__PURE__ */ jsx22("span", { className: "flex-1 text-left", children: current.label }),
20086
- /* @__PURE__ */ jsx22(ChevronDown4, { size: 16, style: { pointerEvents: "none" } })
19751
+ /* @__PURE__ */ jsx21(StyleSwatch, { style: current.value }),
19752
+ /* @__PURE__ */ jsx21("span", { className: "flex-1 text-left", children: current.label }),
19753
+ /* @__PURE__ */ jsx21(ChevronDown3, { size: 16, style: { pointerEvents: "none" } })
20087
19754
  ]
20088
19755
  }
20089
19756
  );
20090
19757
  if (disabled) {
20091
- return /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
20092
- /* @__PURE__ */ jsx22("span", { className: "text-xs text-muted-foreground opacity-50", children: "Card style" }),
20093
- /* @__PURE__ */ jsxs13(Tooltip, { children: [
20094
- /* @__PURE__ */ jsx22(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx22("span", { className: "inline-flex", children: trigger }) }),
20095
- /* @__PURE__ */ jsx22(TooltipContent, { side: "bottom", sideOffset: 8, children: "Mark as featured to edit card style" })
19758
+ return /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
19759
+ /* @__PURE__ */ jsx21("span", { className: "text-xs text-muted-foreground opacity-50", children: "Card style" }),
19760
+ /* @__PURE__ */ jsxs12(Tooltip, { children: [
19761
+ /* @__PURE__ */ jsx21(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx21("span", { className: "inline-flex", children: trigger }) }),
19762
+ /* @__PURE__ */ jsx21(TooltipContent, { side: "bottom", sideOffset: 8, children: "Mark as featured to edit card style" })
20096
19763
  ] })
20097
19764
  ] });
20098
19765
  }
20099
- return /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
20100
- /* @__PURE__ */ jsx22("span", { className: "text-xs text-muted-foreground", children: "Card style" }),
20101
- /* @__PURE__ */ jsxs13(DropdownMenu, { modal: false, children: [
20102
- /* @__PURE__ */ jsx22(DropdownMenuTrigger, { asChild: true, children: trigger }),
20103
- /* @__PURE__ */ jsx22(DropdownMenuContent, { align: "end", sideOffset: 8, className: "w-[152px] min-w-[152px] p-1", children: PRICING_CARD_STYLES.map((style) => {
19766
+ return /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-1.5 pl-1.5", children: [
19767
+ /* @__PURE__ */ jsx21("span", { className: "text-xs text-muted-foreground", children: "Card style" }),
19768
+ /* @__PURE__ */ jsxs12(DropdownMenu, { modal: false, children: [
19769
+ /* @__PURE__ */ jsx21(DropdownMenuTrigger, { asChild: true, children: trigger }),
19770
+ /* @__PURE__ */ jsx21(DropdownMenuContent, { align: "end", sideOffset: 8, className: "w-[152px] min-w-[152px] p-1", children: PRICING_CARD_STYLES.map((style) => {
20104
19771
  const selected = style.value === value;
20105
- return /* @__PURE__ */ jsxs13(
19772
+ return /* @__PURE__ */ jsxs12(
20106
19773
  DropdownMenuItem,
20107
19774
  {
20108
19775
  role: "menuitemradio",
@@ -20110,9 +19777,9 @@ function CardStyleMenu({
20110
19777
  onSelect: () => onChange(style.value),
20111
19778
  className: cn("grid grid-cols-[18px_1fr_16px] gap-2 py-2 pl-2 pr-2 text-[13px]", selected && "bg-muted"),
20112
19779
  children: [
20113
- /* @__PURE__ */ jsx22(StyleSwatch, { style: style.value }),
20114
- /* @__PURE__ */ jsx22("span", { children: style.label }),
20115
- selected && /* @__PURE__ */ jsx22(Check3, { size: 16, className: "text-bridge-primary" })
19780
+ /* @__PURE__ */ jsx21(StyleSwatch, { style: style.value }),
19781
+ /* @__PURE__ */ jsx21("span", { children: style.label }),
19782
+ selected && /* @__PURE__ */ jsx21(Check2, { size: 16, className: "text-bridge-primary" })
20116
19783
  ]
20117
19784
  },
20118
19785
  style.value
@@ -20142,7 +19809,7 @@ function PricingCardToolbar({
20142
19809
  const style = pricingCardStyle(entry.tree, index);
20143
19810
  const featuredTag = pricingCardFeaturedTag(entry.tree, index);
20144
19811
  const mobile = window.innerWidth <= 480;
20145
- return /* @__PURE__ */ jsx22(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsxs13(
19812
+ return /* @__PURE__ */ jsx21(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsxs12(
20146
19813
  "div",
20147
19814
  {
20148
19815
  "data-ohw-bridge": "",
@@ -20154,7 +19821,7 @@ function PricingCardToolbar({
20154
19821
  ),
20155
19822
  style: { maxWidth: mobile ? "calc(100vw - 24px)" : void 0 },
20156
19823
  children: [
20157
- /* @__PURE__ */ jsxs13(
19824
+ /* @__PURE__ */ jsxs12(
20158
19825
  "div",
20159
19826
  {
20160
19827
  className: cn(
@@ -20163,8 +19830,8 @@ function PricingCardToolbar({
20163
19830
  mobile ? "flex-wrap justify-center" : "flex-nowrap"
20164
19831
  ),
20165
19832
  children: [
20166
- /* @__PURE__ */ jsxs13(Tooltip, { children: [
20167
- /* @__PURE__ */ jsx22(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx22(
19833
+ /* @__PURE__ */ jsxs12(Tooltip, { children: [
19834
+ /* @__PURE__ */ jsx21(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx21(
20168
19835
  CustomToolbarButton,
20169
19836
  {
20170
19837
  type: "button",
@@ -20172,7 +19839,7 @@ function PricingCardToolbar({
20172
19839
  "aria-pressed": featuredTag,
20173
19840
  onClick: () => onSetFeaturedTag(!featuredTag),
20174
19841
  className: "group size-8",
20175
- children: /* @__PURE__ */ jsx22(
19842
+ children: /* @__PURE__ */ jsx21(
20176
19843
  Star2,
20177
19844
  {
20178
19845
  size: 16,
@@ -20187,25 +19854,25 @@ function PricingCardToolbar({
20187
19854
  )
20188
19855
  }
20189
19856
  ) }),
20190
- /* @__PURE__ */ jsx22(TooltipContent, { side: "bottom", sideOffset: 8, children: featuredTag ? "Unmark featured" : "Mark as featured" })
19857
+ /* @__PURE__ */ jsx21(TooltipContent, { side: "bottom", sideOffset: 8, children: featuredTag ? "Unmark featured" : "Mark as featured" })
20191
19858
  ] }),
20192
- /* @__PURE__ */ jsx22(ToolbarSeparator2, {}),
20193
- /* @__PURE__ */ jsx22(CardStyleMenu, { value: style, onChange: onSetStyle, disabled: !featuredTag }),
20194
- /* @__PURE__ */ jsx22(ToolbarSeparator2, {}),
20195
- /* @__PURE__ */ jsx22(ToolbarAction2, { label: "Add button", onClick: onAddButton, disabled: buttonCount >= 2, children: /* @__PURE__ */ jsx22(ButtonGlyph, {}) }),
20196
- /* @__PURE__ */ jsx22(ToolbarAction2, { label: "Remove button", onClick: onRemoveButton, disabled: buttonCount === 0, children: /* @__PURE__ */ jsxs13("span", { className: "relative inline-flex", children: [
20197
- /* @__PURE__ */ jsx22(ButtonGlyph, {}),
20198
- /* @__PURE__ */ jsx22(Minus2, { size: 10, strokeWidth: 2.5, className: "absolute -right-1.5 -top-1.5 rounded-full bg-background text-bridge-primary" })
19859
+ /* @__PURE__ */ jsx21(ToolbarSeparator, {}),
19860
+ /* @__PURE__ */ jsx21(CardStyleMenu, { value: style, onChange: onSetStyle, disabled: !featuredTag }),
19861
+ /* @__PURE__ */ jsx21(ToolbarSeparator, {}),
19862
+ /* @__PURE__ */ jsx21(ToolbarAction, { label: "Add button", onClick: onAddButton, disabled: buttonCount >= 2, children: /* @__PURE__ */ jsx21(ButtonGlyph, {}) }),
19863
+ /* @__PURE__ */ jsx21(ToolbarAction, { label: "Remove button", onClick: onRemoveButton, disabled: buttonCount === 0, children: /* @__PURE__ */ jsxs12("span", { className: "relative inline-flex", children: [
19864
+ /* @__PURE__ */ jsx21(ButtonGlyph, {}),
19865
+ /* @__PURE__ */ jsx21(Minus, { size: 10, strokeWidth: 2.5, className: "absolute -right-1.5 -top-1.5 rounded-full bg-background text-bridge-primary" })
20199
19866
  ] }) }),
20200
- /* @__PURE__ */ jsx22(ToolbarAction2, { label: "Add feature", onClick: onAddFeature, disabled: featureCount >= 10, children: /* @__PURE__ */ jsx22(FeatureGlyph, { mode: "add" }) }),
20201
- /* @__PURE__ */ jsx22(ToolbarAction2, { label: "Remove feature", onClick: onRemoveFeature, disabled: featureCount <= 1, children: /* @__PURE__ */ jsx22(FeatureGlyph, { mode: "remove" }) }),
20202
- /* @__PURE__ */ jsx22(ToolbarSeparator2, {}),
20203
- /* @__PURE__ */ jsx22(ToolbarAction2, { label: "Add tier", onClick: onAddTier, disabled: collectionCount >= 4, children: /* @__PURE__ */ jsx22(Plus3, { size: 16, style: { pointerEvents: "none" } }) }),
20204
- /* @__PURE__ */ jsx22(ToolbarAction2, { label: "Remove tier", onClick: onRemoveTier, disabled: collectionCount <= 1, children: /* @__PURE__ */ jsx22(Minus2, { size: 16, style: { pointerEvents: "none" } }) })
19867
+ /* @__PURE__ */ jsx21(ToolbarAction, { label: "Add feature", onClick: onAddFeature, disabled: featureCount >= 10, children: /* @__PURE__ */ jsx21(FeatureGlyph, { mode: "add" }) }),
19868
+ /* @__PURE__ */ jsx21(ToolbarAction, { label: "Remove feature", onClick: onRemoveFeature, disabled: featureCount <= 1, children: /* @__PURE__ */ jsx21(FeatureGlyph, { mode: "remove" }) }),
19869
+ /* @__PURE__ */ jsx21(ToolbarSeparator, {}),
19870
+ /* @__PURE__ */ jsx21(ToolbarAction, { label: "Add tier", onClick: onAddTier, disabled: collectionCount >= 4, children: /* @__PURE__ */ jsx21(Plus2, { size: 16, style: { pointerEvents: "none" } }) }),
19871
+ /* @__PURE__ */ jsx21(ToolbarAction, { label: "Remove tier", onClick: onRemoveTier, disabled: collectionCount <= 1, children: /* @__PURE__ */ jsx21(Minus, { size: 16, style: { pointerEvents: "none" } }) })
20205
19872
  ]
20206
19873
  }
20207
19874
  ),
20208
- error && /* @__PURE__ */ jsx22("div", { className: "max-w-[260px] rounded-md bg-background px-2 py-1 text-center text-[11px] text-destructive shadow-sm", children: error })
19875
+ error && /* @__PURE__ */ jsx21("div", { className: "max-w-[260px] rounded-md bg-background px-2 py-1 text-center text-[11px] text-destructive shadow-sm", children: error })
20209
19876
  ]
20210
19877
  }
20211
19878
  ) });
@@ -20239,7 +19906,7 @@ function AiPricingCardOverlay({
20239
19906
  const overEditable = Boolean(t?.closest?.("[data-ohw-editable], [data-ohw-role], a, button"));
20240
19907
  setHover((prev) => {
20241
19908
  const next = ref && !overEditable ? ref : null;
20242
- return sameRef2(prev, next) ? prev : next;
19909
+ return sameRef(prev, next) ? prev : next;
20243
19910
  });
20244
19911
  };
20245
19912
  const onLeave = () => setHover(null);
@@ -20254,10 +19921,10 @@ function AiPricingCardOverlay({
20254
19921
  const onClick = (e) => {
20255
19922
  const t = e.target;
20256
19923
  if (!(t instanceof Element)) return;
20257
- if (t.closest(CHROME_SELECTOR2)) return;
19924
+ if (t.closest(CHROME_SELECTOR)) return;
20258
19925
  const ref = cardRefFromTarget(t);
20259
- setActive((prev) => sameRef2(prev, ref) ? prev : ref);
20260
- if (!ref || !sameRef2(activeRef.current, ref)) setError(null);
19926
+ setActive((prev) => sameRef(prev, ref) ? prev : ref);
19927
+ if (!ref || !sameRef(activeRef.current, ref)) setError(null);
20261
19928
  };
20262
19929
  document.addEventListener("click", onClick, true);
20263
19930
  return () => document.removeEventListener("click", onClick, true);
@@ -20322,12 +19989,12 @@ function AiPricingCardOverlay({
20322
19989
  const activeEntry = active ? findEntry(active.sectionId) : null;
20323
19990
  const activeCardRect = useLiveCardRect(active);
20324
19991
  const activeGridRect = useLiveCardRect(active, true);
20325
- const hoverRect = useLiveCardRect(hover && !sameRef2(hover, active) ? hover : null);
20326
- return /* @__PURE__ */ jsxs13(Fragment8, { children: [
20327
- hoverRect && /* @__PURE__ */ jsx22(ItemInteractionLayer, { rect: hoverRect, state: "hover" }),
20328
- activeCardRect && activeGridRect && active && activeEntry && /* @__PURE__ */ jsxs13(Fragment8, { children: [
20329
- /* @__PURE__ */ jsx22(ItemInteractionLayer, { rect: activeCardRect, state: "active-top" }),
20330
- /* @__PURE__ */ jsx22(GridToolbarSlot, { gridRect: activeGridRect, children: /* @__PURE__ */ jsx22(
19992
+ const hoverRect = useLiveCardRect(hover && !sameRef(hover, active) ? hover : null);
19993
+ return /* @__PURE__ */ jsxs12(Fragment8, { children: [
19994
+ hoverRect && /* @__PURE__ */ jsx21(ItemInteractionLayer, { rect: hoverRect, state: "hover" }),
19995
+ activeCardRect && activeGridRect && active && activeEntry && /* @__PURE__ */ jsxs12(Fragment8, { children: [
19996
+ /* @__PURE__ */ jsx21(ItemInteractionLayer, { rect: activeCardRect, state: "active-top" }),
19997
+ /* @__PURE__ */ jsx21(GridToolbarSlot, { gridRect: activeGridRect, children: /* @__PURE__ */ jsx21(
20331
19998
  PricingCardToolbar,
20332
19999
  {
20333
20000
  entry: activeEntry,
@@ -20355,7 +20022,7 @@ function AiPricingCardOverlay({
20355
20022
  onRemoveFeature: () => runMutation(active.sectionId, (tree) => removePricingCardFeature(tree, active.index))
20356
20023
  }
20357
20024
  ) }),
20358
- /* @__PURE__ */ jsx22(
20025
+ /* @__PURE__ */ jsx21(
20359
20026
  "div",
20360
20027
  {
20361
20028
  "data-ohw-bridge": "",
@@ -20419,11 +20086,39 @@ function clearVideo(tree, path) {
20419
20086
  });
20420
20087
  }
20421
20088
 
20089
+ // src/lib/youtube.ts
20090
+ var YOUTUBE_ID_PATTERN = /^[A-Za-z0-9_-]{11}$/;
20091
+ function extractYouTubeId(input) {
20092
+ const trimmed = input.trim();
20093
+ if (!trimmed) return null;
20094
+ if (YOUTUBE_ID_PATTERN.test(trimmed)) return trimmed;
20095
+ let url;
20096
+ try {
20097
+ url = new URL(trimmed);
20098
+ } catch {
20099
+ return null;
20100
+ }
20101
+ const host = url.hostname.replace(/^www\./, "");
20102
+ if (host !== "youtube.com" && host !== "m.youtube.com" && host !== "youtu.be" && host !== "youtube-nocookie.com") {
20103
+ return null;
20104
+ }
20105
+ let candidate = null;
20106
+ if (host === "youtu.be") {
20107
+ candidate = url.pathname.slice(1).split("/")[0] ?? null;
20108
+ } else if (url.pathname === "/watch") {
20109
+ candidate = url.searchParams.get("v");
20110
+ } else {
20111
+ const match = url.pathname.match(/^\/(?:embed|shorts)\/([^/]+)/);
20112
+ candidate = match ? match[1] : null;
20113
+ }
20114
+ return candidate && YOUTUBE_ID_PATTERN.test(candidate) ? candidate : null;
20115
+ }
20116
+
20422
20117
  // src/ui/ai-section/AiVideoOverlay.tsx
20423
- import { Fragment as Fragment9, jsx as jsx23, jsxs as jsxs14 } from "react/jsx-runtime";
20118
+ import { Fragment as Fragment9, jsx as jsx22, jsxs as jsxs13 } from "react/jsx-runtime";
20424
20119
  var PRIMARY5 = "#0885FE";
20425
- var OHW_BLUE = `var(--ohw-primary, ${PRIMARY5})`;
20426
- var OHW_BLUE_DARK = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY5}) 60%, #000000)`;
20120
+ var OHW_BLUE2 = `var(--ohw-primary, ${PRIMARY5})`;
20121
+ var OHW_BLUE_DARK2 = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY5}) 60%, #000000)`;
20427
20122
  function findVideoBlockElement(sectionId, path) {
20428
20123
  const sectionEl = findByInstanceId(sectionId);
20429
20124
  if (!sectionEl) return null;
@@ -20483,12 +20178,12 @@ function requestValidateTree3(postToParent2, tree) {
20483
20178
  postToParent2({ type: "ow:ai-validate-tree-request", id, tree });
20484
20179
  });
20485
20180
  }
20486
- function Pill({
20181
+ function Pill2({
20487
20182
  label,
20488
20183
  onClick,
20489
20184
  disabled
20490
20185
  }) {
20491
- return /* @__PURE__ */ jsx23(
20186
+ return /* @__PURE__ */ jsx22(
20492
20187
  "button",
20493
20188
  {
20494
20189
  type: "button",
@@ -20504,7 +20199,7 @@ function Pill({
20504
20199
  border: "none",
20505
20200
  cursor: disabled ? "default" : "pointer",
20506
20201
  background: "#ffffff",
20507
- color: OHW_BLUE_DARK,
20202
+ color: OHW_BLUE_DARK2,
20508
20203
  opacity: disabled ? 0.4 : 1,
20509
20204
  fontSize: 12,
20510
20205
  lineHeight: "16px",
@@ -20526,7 +20221,7 @@ function VideoUrlInput({ value, onCommit }) {
20526
20221
  setDraft(value ? watchUrl(value) : "");
20527
20222
  setInvalid(false);
20528
20223
  }, [value]);
20529
- const commit2 = () => {
20224
+ const commit = () => {
20530
20225
  const next = draft.trim();
20531
20226
  if (!next) {
20532
20227
  setInvalid(false);
@@ -20547,8 +20242,8 @@ function VideoUrlInput({ value, onCommit }) {
20547
20242
  onCommit(id);
20548
20243
  }
20549
20244
  };
20550
- return /* @__PURE__ */ jsxs14("div", { style: { display: "flex", flexDirection: "column", gap: 4, width: "100%", maxWidth: 280 }, children: [
20551
- /* @__PURE__ */ jsxs14(
20245
+ return /* @__PURE__ */ jsxs13("div", { style: { display: "flex", flexDirection: "column", gap: 4, width: "100%", maxWidth: 280 }, children: [
20246
+ /* @__PURE__ */ jsxs13(
20552
20247
  "div",
20553
20248
  {
20554
20249
  style: {
@@ -20564,12 +20259,12 @@ function VideoUrlInput({ value, onCommit }) {
20564
20259
  boxShadow: invalid ? "inset 0 0 0 1.5px #EF4444" : "none"
20565
20260
  },
20566
20261
  children: [
20567
- /* @__PURE__ */ jsx23(
20262
+ /* @__PURE__ */ jsx22(
20568
20263
  "span",
20569
20264
  {
20570
20265
  style: {
20571
20266
  flex: "0 0 auto",
20572
- color: OHW_BLUE_DARK,
20267
+ color: OHW_BLUE_DARK2,
20573
20268
  fontSize: 10,
20574
20269
  lineHeight: "16px",
20575
20270
  fontWeight: 700,
@@ -20579,14 +20274,14 @@ function VideoUrlInput({ value, onCommit }) {
20579
20274
  children: "Video"
20580
20275
  }
20581
20276
  ),
20582
- /* @__PURE__ */ jsx23(
20277
+ /* @__PURE__ */ jsx22(
20583
20278
  "input",
20584
20279
  {
20585
20280
  type: "text",
20586
20281
  inputMode: "url",
20587
20282
  value: draft,
20588
20283
  onChange: (e) => handleChange(e.target.value),
20589
- onBlur: commit2,
20284
+ onBlur: commit,
20590
20285
  onKeyDown: (e) => {
20591
20286
  if (e.key === "Enter") e.currentTarget.blur();
20592
20287
  if (e.key === "Escape") {
@@ -20616,7 +20311,7 @@ function VideoUrlInput({ value, onCommit }) {
20616
20311
  ]
20617
20312
  }
20618
20313
  ),
20619
- invalid && /* @__PURE__ */ jsx23("div", { style: { color: "#FCA5A5", fontSize: 11, textAlign: "center" }, children: "That doesn't look like a YouTube link \u2014 try pasting the video URL again." })
20314
+ invalid && /* @__PURE__ */ jsx22("div", { style: { color: "#FCA5A5", fontSize: 11, textAlign: "center" }, children: "That doesn't look like a YouTube link \u2014 try pasting the video URL again." })
20620
20315
  ] });
20621
20316
  }
20622
20317
  function VideoToolbar({
@@ -20626,7 +20321,7 @@ function VideoToolbar({
20626
20321
  onSetVideoId,
20627
20322
  onRemove
20628
20323
  }) {
20629
- return /* @__PURE__ */ jsxs14(
20324
+ return /* @__PURE__ */ jsxs13(
20630
20325
  "div",
20631
20326
  {
20632
20327
  "data-ohw-bridge": "",
@@ -20638,7 +20333,7 @@ function VideoToolbar({
20638
20333
  gap: 6,
20639
20334
  alignItems: "center",
20640
20335
  width: 280,
20641
- background: OHW_BLUE,
20336
+ background: OHW_BLUE2,
20642
20337
  padding: 8,
20643
20338
  borderRadius: 12,
20644
20339
  boxShadow: "0 8px 24px rgba(0,0,0,0.25)",
@@ -20646,9 +20341,9 @@ function VideoToolbar({
20646
20341
  opacity: pending ? 0.6 : 1
20647
20342
  },
20648
20343
  children: [
20649
- /* @__PURE__ */ jsx23(VideoUrlInput, { value: videoId, onCommit: onSetVideoId }),
20650
- /* @__PURE__ */ jsx23(Pill, { label: "Remove video", onClick: onRemove, disabled: !videoId }),
20651
- error && /* @__PURE__ */ jsx23("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
20344
+ /* @__PURE__ */ jsx22(VideoUrlInput, { value: videoId, onCommit: onSetVideoId }),
20345
+ /* @__PURE__ */ jsx22(Pill2, { label: "Remove video", onClick: onRemove, disabled: !videoId }),
20346
+ error && /* @__PURE__ */ jsx22("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
20652
20347
  ]
20653
20348
  }
20654
20349
  );
@@ -20768,8 +20463,8 @@ function AiVideoOverlay({
20768
20463
  const activeBlockRect = useLiveBlockRect(active);
20769
20464
  const TOOLBAR_APPROX_H = 90;
20770
20465
  const toolbarState = activeBlockRect && activeBlockRect.top < TOOLBAR_APPROX_H ? "active-bottom" : "active-top";
20771
- return /* @__PURE__ */ jsxs14(Fragment9, { children: [
20772
- hoverHandleRect && hover && /* @__PURE__ */ jsx23(
20466
+ return /* @__PURE__ */ jsxs13(Fragment9, { children: [
20467
+ hoverHandleRect && hover && /* @__PURE__ */ jsx22(
20773
20468
  "button",
20774
20469
  {
20775
20470
  type: "button",
@@ -20797,15 +20492,15 @@ function AiVideoOverlay({
20797
20492
  fontSize: 14,
20798
20493
  fontWeight: 700
20799
20494
  },
20800
- children: /* @__PURE__ */ jsx23(Star3, { size: 14, style: { pointerEvents: "none" } })
20495
+ children: /* @__PURE__ */ jsx22(Star3, { size: 14, style: { pointerEvents: "none" } })
20801
20496
  }
20802
20497
  ),
20803
- activeBlockRect && active && activeEntry && /* @__PURE__ */ jsx23(
20498
+ activeBlockRect && active && activeEntry && /* @__PURE__ */ jsx22(
20804
20499
  ItemInteractionLayer,
20805
20500
  {
20806
20501
  rect: activeBlockRect,
20807
20502
  state: toolbarState,
20808
- toolbar: /* @__PURE__ */ jsx23(
20503
+ toolbar: /* @__PURE__ */ jsx22(
20809
20504
  VideoToolbar,
20810
20505
  {
20811
20506
  videoId: videoIdAt(activeEntry.tree, active.path),
@@ -20880,10 +20575,10 @@ function setCarouselAutoplay(tree, on) {
20880
20575
  }
20881
20576
 
20882
20577
  // src/ui/ai-section/AiFadeCarouselOverlay.tsx
20883
- import { Fragment as Fragment10, jsx as jsx24, jsxs as jsxs15 } from "react/jsx-runtime";
20578
+ import { Fragment as Fragment10, jsx as jsx23, jsxs as jsxs14 } from "react/jsx-runtime";
20884
20579
  var PRIMARY6 = "#0885FE";
20885
- var OHW_BLUE2 = `var(--ohw-primary, ${PRIMARY6})`;
20886
- var OHW_BLUE_DARK2 = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY6}) 60%, #000000)`;
20580
+ var OHW_BLUE3 = `var(--ohw-primary, ${PRIMARY6})`;
20581
+ var OHW_BLUE_DARK3 = `color-mix(in srgb, var(--ohw-primary, ${PRIMARY6}) 60%, #000000)`;
20887
20582
  function findCarouselItemElement(sectionId, index) {
20888
20583
  const sectionEl = findByInstanceId(sectionId);
20889
20584
  if (!sectionEl) return null;
@@ -20962,8 +20657,8 @@ function requestAssetPick2(postToParent2) {
20962
20657
  postToParent2({ type: "ow:asset-pick", id });
20963
20658
  });
20964
20659
  }
20965
- function Pill2({ label, onClick, disabled }) {
20966
- return /* @__PURE__ */ jsx24(
20660
+ function Pill3({ label, onClick, disabled }) {
20661
+ return /* @__PURE__ */ jsx23(
20967
20662
  "button",
20968
20663
  {
20969
20664
  type: "button",
@@ -20979,7 +20674,7 @@ function Pill2({ label, onClick, disabled }) {
20979
20674
  border: "none",
20980
20675
  cursor: disabled ? "default" : "pointer",
20981
20676
  background: "#ffffff",
20982
- color: OHW_BLUE_DARK2,
20677
+ color: OHW_BLUE_DARK3,
20983
20678
  opacity: disabled ? 0.4 : 1,
20984
20679
  fontSize: 12,
20985
20680
  lineHeight: "16px",
@@ -20997,7 +20692,7 @@ function TogglePill({
20997
20692
  onToggle,
20998
20693
  disabled
20999
20694
  }) {
21000
- return /* @__PURE__ */ jsxs15(
20695
+ return /* @__PURE__ */ jsxs14(
21001
20696
  "button",
21002
20697
  {
21003
20698
  type: "button",
@@ -21016,7 +20711,7 @@ function TogglePill({
21016
20711
  border: "none",
21017
20712
  cursor: disabled ? "default" : "pointer",
21018
20713
  background: "#ffffff",
21019
- color: OHW_BLUE_DARK2,
20714
+ color: OHW_BLUE_DARK3,
21020
20715
  opacity: disabled ? 0.4 : 1,
21021
20716
  fontSize: 12,
21022
20717
  lineHeight: "16px",
@@ -21026,7 +20721,7 @@ function TogglePill({
21026
20721
  },
21027
20722
  children: [
21028
20723
  label,
21029
- /* @__PURE__ */ jsx24(
20724
+ /* @__PURE__ */ jsx23(
21030
20725
  "span",
21031
20726
  {
21032
20727
  "aria-hidden": true,
@@ -21039,10 +20734,10 @@ function TogglePill({
21039
20734
  // Off is a grey track, not a washed-out blue: the pill's own white background already
21040
20735
  // carries the brand colour in the text, and a pale blue track next to it read as
21041
20736
  // "disabled control" rather than "switch that is off".
21042
- background: on ? OHW_BLUE_DARK2 : "#CBD5E1",
20737
+ background: on ? OHW_BLUE_DARK3 : "#CBD5E1",
21043
20738
  transition: "background 120ms ease"
21044
20739
  },
21045
- children: /* @__PURE__ */ jsx24(
20740
+ children: /* @__PURE__ */ jsx23(
21046
20741
  "span",
21047
20742
  {
21048
20743
  style: {
@@ -21071,7 +20766,7 @@ function CarouselSlideToolbar({
21071
20766
  onChangeImage,
21072
20767
  onToggleAutoplay
21073
20768
  }) {
21074
- return /* @__PURE__ */ jsxs15(
20769
+ return /* @__PURE__ */ jsxs14(
21075
20770
  "div",
21076
20771
  {
21077
20772
  "data-ohw-bridge": "",
@@ -21082,7 +20777,7 @@ function CarouselSlideToolbar({
21082
20777
  flexDirection: "column",
21083
20778
  gap: 6,
21084
20779
  alignItems: "center",
21085
- background: OHW_BLUE2,
20780
+ background: OHW_BLUE3,
21086
20781
  padding: 8,
21087
20782
  borderRadius: 12,
21088
20783
  boxShadow: "0 8px 24px rgba(0,0,0,0.25)",
@@ -21094,11 +20789,11 @@ function CarouselSlideToolbar({
21094
20789
  pointerEvents: pending ? "none" : "auto"
21095
20790
  },
21096
20791
  children: [
21097
- /* @__PURE__ */ jsxs15("div", { style: { display: "flex", gap: 6, alignItems: "center" }, children: [
21098
- /* @__PURE__ */ jsx24(Pill2, { label: "Change Image", onClick: onChangeImage }),
21099
- /* @__PURE__ */ jsx24(TogglePill, { label: "Autoplay", on: autoplay, onToggle: onToggleAutoplay })
20792
+ /* @__PURE__ */ jsxs14("div", { style: { display: "flex", gap: 6, alignItems: "center" }, children: [
20793
+ /* @__PURE__ */ jsx23(Pill3, { label: "Change Image", onClick: onChangeImage }),
20794
+ /* @__PURE__ */ jsx23(TogglePill, { label: "Autoplay", on: autoplay, onToggle: onToggleAutoplay })
21100
20795
  ] }),
21101
- error && /* @__PURE__ */ jsx24("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
20796
+ error && /* @__PURE__ */ jsx23("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
21102
20797
  ]
21103
20798
  }
21104
20799
  );
@@ -21238,8 +20933,8 @@ function AiFadeCarouselOverlay({
21238
20933
  const activeItemRect = useLiveItemRect2(active);
21239
20934
  const TOOLBAR_APPROX_H = 90;
21240
20935
  const toolbarState = activeItemRect && activeItemRect.top < TOOLBAR_APPROX_H ? "active-bottom" : "active-top";
21241
- return /* @__PURE__ */ jsxs15(Fragment10, { children: [
21242
- hoverHandleRect && hover && /* @__PURE__ */ jsx24(
20936
+ return /* @__PURE__ */ jsxs14(Fragment10, { children: [
20937
+ hoverHandleRect && hover && /* @__PURE__ */ jsx23(
21243
20938
  "button",
21244
20939
  {
21245
20940
  type: "button",
@@ -21268,10 +20963,10 @@ function AiFadeCarouselOverlay({
21268
20963
  fontSize: 14,
21269
20964
  fontWeight: 700
21270
20965
  },
21271
- children: /* @__PURE__ */ jsx24(Star4, { size: 14, style: { pointerEvents: "none" } })
20966
+ children: /* @__PURE__ */ jsx23(Star4, { size: 14, style: { pointerEvents: "none" } })
21272
20967
  }
21273
20968
  ),
21274
- swapping && /* @__PURE__ */ jsx24(
20969
+ swapping && /* @__PURE__ */ jsx23(
21275
20970
  "div",
21276
20971
  {
21277
20972
  ref: skeletonRef,
@@ -21289,15 +20984,15 @@ function AiFadeCarouselOverlay({
21289
20984
  zIndex: 2147483646,
21290
20985
  pointerEvents: "none"
21291
20986
  },
21292
- children: /* @__PURE__ */ jsx24("style", { children: MEDIA_SKELETON_CSS })
20987
+ children: /* @__PURE__ */ jsx23("style", { children: MEDIA_SKELETON_CSS })
21293
20988
  }
21294
20989
  ),
21295
- activeItemRect && active && activeEntry && /* @__PURE__ */ jsx24(
20990
+ activeItemRect && active && activeEntry && /* @__PURE__ */ jsx23(
21296
20991
  ItemInteractionLayer,
21297
20992
  {
21298
20993
  rect: activeItemRect,
21299
20994
  state: toolbarState,
21300
- toolbar: /* @__PURE__ */ jsx24(
20995
+ toolbar: /* @__PURE__ */ jsx23(
21301
20996
  CarouselSlideToolbar,
21302
20997
  {
21303
20998
  pending,
@@ -21488,11 +21183,118 @@ function mergePrefetchedSections(prev, fetched, livePath, liveSections) {
21488
21183
  // src/ui/link-modal/LinkPopover.tsx
21489
21184
  import { useEffect as useEffect15 } from "react";
21490
21185
 
21186
+ // src/ui/dialog.tsx
21187
+ import * as React9 from "react";
21188
+ import { Dialog as DialogPrimitive } from "radix-ui";
21189
+ import { X as X2 } from "lucide-react";
21190
+ import { jsx as jsx24, jsxs as jsxs15 } from "react/jsx-runtime";
21191
+ function Dialog2({
21192
+ ...props
21193
+ }) {
21194
+ return /* @__PURE__ */ jsx24(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
21195
+ }
21196
+ function DialogPortal({
21197
+ ...props
21198
+ }) {
21199
+ return /* @__PURE__ */ jsx24(DialogPrimitive.Portal, { ...props });
21200
+ }
21201
+ function DialogOverlay({
21202
+ className,
21203
+ ...props
21204
+ }) {
21205
+ return /* @__PURE__ */ jsx24(
21206
+ DialogPrimitive.Overlay,
21207
+ {
21208
+ "data-slot": "dialog-overlay",
21209
+ "data-ohw-link-modal-root": "",
21210
+ className: cn("fixed inset-0 z-[2147483646] bg-black/50", className),
21211
+ ...props
21212
+ }
21213
+ );
21214
+ }
21215
+ var DialogContent = React9.forwardRef(
21216
+ ({ className, children, showCloseButton = true, container, ...props }, ref) => {
21217
+ const positionMode = container ? "absolute" : "fixed";
21218
+ return /* @__PURE__ */ jsxs15(DialogPortal, { container: container ?? void 0, children: [
21219
+ /* @__PURE__ */ jsx24(DialogOverlay, { className: cn(positionMode, "inset-0") }),
21220
+ /* @__PURE__ */ jsxs15(
21221
+ DialogPrimitive.Content,
21222
+ {
21223
+ ref,
21224
+ "data-slot": "dialog-content",
21225
+ className: cn(
21226
+ positionMode,
21227
+ "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",
21228
+ "rounded-xl border border-border bg-background font-sans shadow-lg outline-none",
21229
+ container ? "max-h-[calc(100%-32px)]" : "max-h-[calc(100vh-32px)]",
21230
+ className
21231
+ ),
21232
+ onMouseDown: (e) => e.stopPropagation(),
21233
+ ...props,
21234
+ children: [
21235
+ children,
21236
+ showCloseButton ? /* @__PURE__ */ jsx24(
21237
+ DialogPrimitive.Close,
21238
+ {
21239
+ type: "button",
21240
+ className: "absolute right-[9px] top-[9px] rounded-sm p-1.5 text-foreground hover:bg-muted/50",
21241
+ "aria-label": "Close",
21242
+ children: /* @__PURE__ */ jsx24(X2, { size: 16, "aria-hidden": true })
21243
+ }
21244
+ ) : null
21245
+ ]
21246
+ }
21247
+ )
21248
+ ] });
21249
+ }
21250
+ );
21251
+ DialogContent.displayName = DialogPrimitive.Content.displayName;
21252
+ function DialogHeader({
21253
+ className,
21254
+ ...props
21255
+ }) {
21256
+ return /* @__PURE__ */ jsx24("div", { className: cn("flex flex-col gap-1.5", className), ...props });
21257
+ }
21258
+ function DialogFooter({
21259
+ className,
21260
+ ...props
21261
+ }) {
21262
+ return /* @__PURE__ */ jsx24(
21263
+ "div",
21264
+ {
21265
+ className: cn("flex items-center justify-end gap-2", className),
21266
+ ...props
21267
+ }
21268
+ );
21269
+ }
21270
+ var DialogTitle = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx24(
21271
+ DialogPrimitive.Title,
21272
+ {
21273
+ ref,
21274
+ className: cn(
21275
+ "text-lg font-semibold leading-none tracking-tight text-card-foreground",
21276
+ className
21277
+ ),
21278
+ ...props
21279
+ }
21280
+ ));
21281
+ DialogTitle.displayName = DialogPrimitive.Title.displayName;
21282
+ var DialogDescription = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx24(
21283
+ DialogPrimitive.Description,
21284
+ {
21285
+ ref,
21286
+ className: cn("text-sm text-muted-foreground", className),
21287
+ ...props
21288
+ }
21289
+ ));
21290
+ DialogDescription.displayName = DialogPrimitive.Description.displayName;
21291
+ var DialogClose = DialogPrimitive.Close;
21292
+
21491
21293
  // src/ui/link-modal/LinkEditorPanel.tsx
21492
21294
  import { Info, X as X4 } from "lucide-react";
21493
21295
 
21494
21296
  // src/ui/link-modal/DestinationBreadcrumb.tsx
21495
- import { ArrowRight as ArrowRight3, File, GalleryVertical } from "lucide-react";
21297
+ import { ArrowRight as ArrowRight2, File, GalleryVertical } from "lucide-react";
21496
21298
  import { jsx as jsx25, jsxs as jsxs16 } from "react/jsx-runtime";
21497
21299
  function DestinationBreadcrumb({
21498
21300
  pageTitle,
@@ -21506,7 +21308,7 @@ function DestinationBreadcrumb({
21506
21308
  /* @__PURE__ */ jsx25("span", { className: "text-sm font-medium leading-none text-foreground!", children: pageTitle })
21507
21309
  ] }),
21508
21310
  /* @__PURE__ */ jsx25(
21509
- ArrowRight3,
21311
+ ArrowRight2,
21510
21312
  {
21511
21313
  size: 16,
21512
21314
  className: "shrink-0 text-muted-foreground",
@@ -21618,7 +21420,7 @@ function Label({ className, ...props }) {
21618
21420
  }
21619
21421
 
21620
21422
  // src/ui/link-modal/UrlOrPageInput.tsx
21621
- import { ChevronDown as ChevronDown5, File as File2, X as X3 } from "lucide-react";
21423
+ import { ChevronDown as ChevronDown4, File as File2, X as X3 } from "lucide-react";
21622
21424
  import { jsx as jsx29, jsxs as jsxs18 } from "react/jsx-runtime";
21623
21425
  function FieldChevron({
21624
21426
  onClick
@@ -21631,7 +21433,7 @@ function FieldChevron({
21631
21433
  onClick,
21632
21434
  "aria-label": "Open page list",
21633
21435
  tabIndex: -1,
21634
- children: /* @__PURE__ */ jsx29(ChevronDown5, { size: 16 })
21436
+ children: /* @__PURE__ */ jsx29(ChevronDown4, { size: 16 })
21635
21437
  }
21636
21438
  );
21637
21439
  }
@@ -21861,7 +21663,7 @@ function LinkEditorPanel({ state, onClose }) {
21861
21663
  // src/ui/link-modal/SectionPickerOverlay.tsx
21862
21664
  import { useCallback as useCallback7, useEffect as useEffect13, useMemo as useMemo2, useRef as useRef10, useState as useState13 } from "react";
21863
21665
  import { createPortal as createPortal2 } from "react-dom";
21864
- import { ArrowLeft as ArrowLeft3, Check as Check4 } from "lucide-react";
21666
+ import { ArrowLeft as ArrowLeft2, Check as Check3 } from "lucide-react";
21865
21667
  import { usePathname as usePathname2, useRouter as useRouter2 } from "next/navigation";
21866
21668
 
21867
21669
  // src/lib/parent-scroll.ts
@@ -22138,7 +21940,7 @@ function SectionPickerOverlay({
22138
21940
  className: "h-8 min-w-0 gap-1 border-border bg-background px-2 py-1.5 shadow-sm hover:bg-muted cursor-pointer",
22139
21941
  onClick: onBack,
22140
21942
  children: [
22141
- /* @__PURE__ */ jsx31(ArrowLeft3, { className: "size-4 shrink-0", "aria-hidden": true }),
21943
+ /* @__PURE__ */ jsx31(ArrowLeft2, { className: "size-4 shrink-0", "aria-hidden": true }),
22142
21944
  "Back"
22143
21945
  ]
22144
21946
  }
@@ -22207,7 +22009,7 @@ function SectionPickerOverlay({
22207
22009
  className: "absolute right-3 top-3 flex size-8 items-center justify-center rounded-full text-white",
22208
22010
  style: { backgroundColor: "var(--ohw-primary, #0885fe)" },
22209
22011
  "aria-hidden": true,
22210
- children: /* @__PURE__ */ jsx31(Check4, { className: "size-5" })
22012
+ children: /* @__PURE__ */ jsx31(Check3, { className: "size-5" })
22211
22013
  }
22212
22014
  ) : null
22213
22015
  ]
@@ -22591,7 +22393,6 @@ function shouldUseDevFixtures() {
22591
22393
  function getLogoElement(el) {
22592
22394
  const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
22593
22395
  if (marked) return marked;
22594
- if (el.closest('[data-ohw-editable="icon"]')) return null;
22595
22396
  const root = el.closest("nav, [data-ohw-nav-root], footer");
22596
22397
  if (!root) return null;
22597
22398
  const anchor = el.closest("a");
@@ -22661,7 +22462,7 @@ function addFooterColumnWithPersist({
22661
22462
  import {
22662
22463
  useCallback as useCallback9,
22663
22464
  useEffect as useEffect16,
22664
- useLayoutEffect as useLayoutEffect6,
22465
+ useLayoutEffect as useLayoutEffect5,
22665
22466
  useRef as useRef11,
22666
22467
  useState as useState15
22667
22468
  } from "react";
@@ -22758,7 +22559,7 @@ function FloatingPanel({
22758
22559
  return () => window.removeEventListener("message", onParentScroll);
22759
22560
  }, [open]);
22760
22561
  const positionRef = useRef11(position);
22761
- useLayoutEffect6(() => {
22562
+ useLayoutEffect5(() => {
22762
22563
  if (!open) {
22763
22564
  positionRef.current = position;
22764
22565
  return;
@@ -22768,7 +22569,7 @@ function FloatingPanel({
22768
22569
  }
22769
22570
  positionRef.current = position;
22770
22571
  }, [open, position, liveScroll, parentScroll]);
22771
- useLayoutEffect6(() => {
22572
+ useLayoutEffect5(() => {
22772
22573
  if (!open || !panelRef.current) return;
22773
22574
  const el = panelRef.current;
22774
22575
  const header = el.querySelector("[data-ohw-floating-panel-header]");
@@ -22977,7 +22778,7 @@ function SocialsDisplayPanel({ display, onChange, className }) {
22977
22778
  }
22978
22779
 
22979
22780
  // src/ui/button-style-panel.tsx
22980
- import { Check as Check5 } from "lucide-react";
22781
+ import { Check as Check4 } from "lucide-react";
22981
22782
  import { jsx as jsx35, jsxs as jsxs24 } from "react/jsx-runtime";
22982
22783
  var SIZE_LABEL = { s: "S", m: "M", l: "L" };
22983
22784
  function ButtonStylePanel({
@@ -23048,7 +22849,7 @@ function ButtonStylePanel({
23048
22849
  {
23049
22850
  className: "absolute right-0.5 top-0.5 flex size-5 items-center justify-center rounded-full bg-primary text-primary-foreground",
23050
22851
  "aria-hidden": true,
23051
- children: /* @__PURE__ */ jsx35(Check5, { className: "size-3", strokeWidth: 3 })
22852
+ children: /* @__PURE__ */ jsx35(Check4, { className: "size-3", strokeWidth: 3 })
23052
22853
  }
23053
22854
  ) : null
23054
22855
  ]
@@ -24073,7 +23874,7 @@ function useSectionDrag({
24073
23874
  }
24074
23875
 
24075
23876
  // src/ui/footer-container-chrome.tsx
24076
- import { Plus as Plus4 } from "lucide-react";
23877
+ import { Plus as Plus3 } from "lucide-react";
24077
23878
  import { jsx as jsx36, jsxs as jsxs25 } from "react/jsx-runtime";
24078
23879
  function FooterContainerChrome({
24079
23880
  rect,
@@ -24114,7 +23915,7 @@ function FooterContainerChrome({
24114
23915
  if (addDisabled) return;
24115
23916
  onAdd();
24116
23917
  },
24117
- children: /* @__PURE__ */ jsx36(Plus4, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
23918
+ children: /* @__PURE__ */ jsx36(Plus3, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
24118
23919
  }
24119
23920
  ) }),
24120
23921
  /* @__PURE__ */ jsx36(TooltipContent, { side: "bottom", sideOffset: 9, children: addDisabled ? "Maximum columns reached" : "Add item" })
@@ -24292,104 +24093,8 @@ function deleteSelectedNavFooterItem(deps) {
24292
24093
  return false;
24293
24094
  }
24294
24095
 
24295
- // src/lib/nav-page-sync.ts
24296
- function labelKeyFor(hrefKey) {
24297
- return hrefKey.replace(/-href$/, "-label");
24298
- }
24299
- function isPageLinkKey(key) {
24300
- return isNavbarHrefKey(key) || isFooterHrefKey(key);
24301
- }
24302
- function currentHref(key, anchor, content) {
24303
- const stored = content[key];
24304
- if (typeof stored === "string" && stored.length > 0) return stored;
24305
- return anchor.getAttribute("href") ?? "";
24306
- }
24307
- function normalizePath2(value) {
24308
- if (value.length > 1 && value.endsWith("/")) return value.slice(0, -1);
24309
- return value;
24310
- }
24311
- function pointsAt(href, path) {
24312
- const normalized = normalizePath2(href);
24313
- const target = normalizePath2(path);
24314
- if (normalized === target) return true;
24315
- return (normalized.startsWith(`${target}#`) || normalized.startsWith(`${target}?`)) && target !== "/";
24316
- }
24317
- function retargetHref(href, oldPath, newPath) {
24318
- const normalized = normalizePath2(href);
24319
- const target = normalizePath2(oldPath);
24320
- if (normalized === target) return newPath;
24321
- return `${newPath}${normalized.slice(target.length)}`;
24322
- }
24323
- function setLabelText(key, text) {
24324
- document.querySelectorAll(`[data-ohw-key="${CSS.escape(key)}"]`).forEach((el) => {
24325
- el.textContent = text;
24326
- });
24327
- }
24328
- function commit(delta, deps) {
24329
- if (Object.keys(delta).length === 0) return;
24330
- deps.setEditContent({ ...deps.getEditContent(), ...delta });
24331
- deps.postToParent({
24332
- type: "ow:change",
24333
- nodes: Object.entries(delta).map(([key, text]) => ({ key, text }))
24334
- });
24335
- }
24336
- function navLinksTo(path, content) {
24337
- return Array.from(document.querySelectorAll("[data-ohw-href-key]")).filter((anchor) => {
24338
- const key = anchor.getAttribute("data-ohw-href-key");
24339
- return isNavbarHrefKey(key) && pointsAt(currentHref(key, anchor, content), path);
24340
- }).map((anchor) => anchor.getAttribute("data-ohw-href-key"));
24341
- }
24342
- function addNavLinkForPage(page, deps) {
24343
- const content = deps.getEditContent();
24344
- if (navLinksTo(page.path, content).length > 0) return false;
24345
- const roots = listNavbarRootItems();
24346
- const afterAnchor = roots[roots.length - 1] ?? null;
24347
- let inserted;
24348
- try {
24349
- inserted = insertNavbarItem(page.path, page.title, afterAnchor);
24350
- } catch {
24351
- return false;
24352
- }
24353
- const { hrefKey, labelKey, index } = inserted;
24354
- deps.applyLinkByKey(hrefKey, page.path);
24355
- setLabelText(labelKey, page.title);
24356
- commit(
24357
- {
24358
- [hrefKey]: page.path,
24359
- [labelKey]: page.title,
24360
- [NAV_COUNT_KEY]: String(index + 1),
24361
- [NAV_ORDER_KEY]: getNavbarOrderJson()
24362
- },
24363
- deps
24364
- );
24365
- return true;
24366
- }
24367
- function retargetPageLinks(change, deps) {
24368
- const { oldPath, newPath, oldTitle, newTitle } = change;
24369
- if (!oldPath || !newPath || normalizePath2(oldPath) === normalizePath2(newPath)) return 0;
24370
- const content = deps.getEditContent();
24371
- const delta = {};
24372
- for (const anchor of Array.from(document.querySelectorAll("[data-ohw-href-key]"))) {
24373
- const hrefKey = anchor.getAttribute("data-ohw-href-key");
24374
- if (!isPageLinkKey(hrefKey)) continue;
24375
- const href = currentHref(hrefKey, anchor, content);
24376
- if (!pointsAt(href, oldPath)) continue;
24377
- const nextHref = retargetHref(href, oldPath, newPath);
24378
- deps.applyLinkByKey(hrefKey, nextHref);
24379
- delta[hrefKey] = nextHref;
24380
- if (!oldTitle || !newTitle) continue;
24381
- const labelKey = labelKeyFor(hrefKey);
24382
- const label = content[labelKey] ?? anchor.querySelector('[data-ohw-editable="text"]')?.textContent ?? "";
24383
- if (label.trim() !== oldTitle) continue;
24384
- setLabelText(labelKey, newTitle);
24385
- delta[labelKey] = newTitle;
24386
- }
24387
- commit(delta, deps);
24388
- return Object.keys(delta).filter((key) => key.endsWith("-href")).length;
24389
- }
24390
-
24391
24096
  // src/ui/navbar-container-chrome.tsx
24392
- import { Plus as Plus5 } from "lucide-react";
24097
+ import { Plus as Plus4 } from "lucide-react";
24393
24098
  import { jsx as jsx37 } from "react/jsx-runtime";
24394
24099
  function NavbarContainerChrome({
24395
24100
  rect,
@@ -24425,7 +24130,7 @@ function NavbarContainerChrome({
24425
24130
  e.stopPropagation();
24426
24131
  onAdd();
24427
24132
  },
24428
- children: /* @__PURE__ */ jsx37(Plus5, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
24133
+ children: /* @__PURE__ */ jsx37(Plus4, { className: "size-4 shrink-0 text-foreground", "aria-hidden": true })
24429
24134
  }
24430
24135
  )
24431
24136
  }
@@ -24497,7 +24202,7 @@ function Badge({ className, variant, ...props }) {
24497
24202
  }
24498
24203
 
24499
24204
  // src/OhhwellsBridge.tsx
24500
- import { Brush as Brush2, Link as Link2, Plus as Plus6, Settings } from "lucide-react";
24205
+ import { Brush as Brush2, Link as Link2, Plus as Plus5, Settings } from "lucide-react";
24501
24206
  import { Fragment as Fragment13, jsx as jsx40, jsxs as jsxs26 } from "react/jsx-runtime";
24502
24207
  var PRIMARY7 = "#0885FE";
24503
24208
  var IMAGE_FADE_MS = 300;
@@ -25275,7 +24980,7 @@ function getNavigationSelectionParent(el) {
25275
24980
  if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
25276
24981
  return resolveFooterLinksContainerFrom(el);
25277
24982
  }
25278
- 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)) {
24983
+ if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
25279
24984
  return getNavigationRoot(el);
25280
24985
  }
25281
24986
  return null;
@@ -25497,6 +25202,7 @@ var ICONS = {
25497
25202
  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"/>',
25498
25203
  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"/>'
25499
25204
  };
25205
+ var HEIGHT_SETTLE_DELAYS = [150, 400, 800];
25500
25206
  var SELECTION_CHROME_GAP2 = 4;
25501
25207
  var TOOLBAR_STROKE_GAP2 = 4;
25502
25208
  var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
@@ -25594,7 +25300,7 @@ function applyVisibleViewport(el, parentScroll) {
25594
25300
  el.style.top = `${top}px`;
25595
25301
  el.style.height = `${height}px`;
25596
25302
  }
25597
- var TOOLBAR_EDGE_MARGIN4 = 4;
25303
+ var TOOLBAR_EDGE_MARGIN3 = 4;
25598
25304
  function clampToolbarToClip(top, transform, rect, clip, approxH, gap) {
25599
25305
  const isAbove = transform.includes("translateY(-100%)");
25600
25306
  let visualTop = isAbove ? top - approxH : top;
@@ -25621,8 +25327,8 @@ function clampToolbarToClip(top, transform, rect, clip, approxH, gap) {
25621
25327
  function clampToolbarLeft(centerX, toolbarWidth) {
25622
25328
  const half = toolbarWidth / 2;
25623
25329
  return Math.max(
25624
- TOOLBAR_EDGE_MARGIN4 + half,
25625
- Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN4 - half)
25330
+ TOOLBAR_EDGE_MARGIN3 + half,
25331
+ Math.min(centerX, window.innerWidth - TOOLBAR_EDGE_MARGIN3 - half)
25626
25332
  );
25627
25333
  }
25628
25334
  function calcToolbarPos(rect, parentScroll, toolbarW = 306) {
@@ -26023,7 +25729,7 @@ function OhhwellsBridge() {
26023
25729
  }, []);
26024
25730
  const subdomainFromQuery = searchParams.get("subdomain");
26025
25731
  const [subdomain, setSubdomain] = useState18(() => resolveSubdomain(subdomainFromQuery));
26026
- useLayoutEffect7(() => {
25732
+ useLayoutEffect6(() => {
26027
25733
  const next = resolveSubdomain(subdomainFromQuery);
26028
25734
  if (next !== subdomain) setSubdomain(next);
26029
25735
  if (next) return;
@@ -26504,6 +26210,21 @@ function OhhwellsBridge() {
26504
26210
  const [isItemDragging, setIsItemDragging] = useState18(false);
26505
26211
  const [isFooterFrameSelection, setIsFooterFrameSelection] = useState18(false);
26506
26212
  isFooterFrameSelectionRef.current = isFooterFrameSelection;
26213
+ const [floatingPanel, setFloatingPanel] = useState18(null);
26214
+ const floatingPanelOpenRef = useRef14(false);
26215
+ floatingPanelOpenRef.current = floatingPanel !== null;
26216
+ const floatingPanelRef = useRef14(null);
26217
+ floatingPanelRef.current = floatingPanel;
26218
+ const [floatingPanelPos, setFloatingPanelPos] = useState18(null);
26219
+ const buttonCollectionDragRef = useRef14(null);
26220
+ const buttonDropInsertIndexRef = useRef14(null);
26221
+ const [buttonDropSlots, setButtonDropSlots] = useState18([]);
26222
+ const [activeButtonDropIndex, setActiveButtonDropIndex] = useState18(null);
26223
+ const buttonPointerDragRef = useRef14(null);
26224
+ const [buttonStyleTick, setButtonStyleTick] = useState18(0);
26225
+ const [logoSizeDraft, setLogoSizeDraft] = useState18(null);
26226
+ const [editorViewport, setEditorViewport] = useState18("desktop");
26227
+ const [parentScrollSnap, setParentScrollSnap] = useState18(null);
26507
26228
  const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = useState18(null);
26508
26229
  const [footerHeadingVisible, setFooterHeadingVisible] = useState18(null);
26509
26230
  const footerDragRef = useRef14(null);
@@ -26529,21 +26250,6 @@ function OhhwellsBridge() {
26529
26250
  const stylesRef = useRef14("");
26530
26251
  const appliedDrawerRef = useRef14(null);
26531
26252
  const pendingDeleteUndoRef = useRef14(null);
26532
- const [floatingPanel, setFloatingPanel] = useState18(null);
26533
- const floatingPanelOpenRef = useRef14(false);
26534
- const floatingPanelRef = useRef14(null);
26535
- floatingPanelRef.current = floatingPanel;
26536
- const setFloatingPanelRef = useRef14(setFloatingPanel);
26537
- const [floatingPanelPos, setFloatingPanelPos] = useState18(null);
26538
- const buttonCollectionDragRef = useRef14(null);
26539
- const buttonDropInsertIndexRef = useRef14(null);
26540
- const [buttonDropSlots, setButtonDropSlots] = useState18([]);
26541
- const [activeButtonDropIndex, setActiveButtonDropIndex] = useState18(null);
26542
- const buttonPointerDragRef = useRef14(null);
26543
- const [buttonStyleTick, setButtonStyleTick] = useState18(0);
26544
- const [logoSizeDraft, setLogoSizeDraft] = useState18(null);
26545
- const [editorViewport, setEditorViewport] = useState18("desktop");
26546
- const [parentScrollSnap, setParentScrollSnap] = useState18(null);
26547
26253
  const [sitePages, setSitePages] = useState18([]);
26548
26254
  const collectionItemsRef = useRef14(/* @__PURE__ */ new Map());
26549
26255
  const [sectionsByPath, setSectionsByPath] = useState18({});
@@ -26553,18 +26259,7 @@ function OhhwellsBridge() {
26553
26259
  const linkPopoverOpenRef = useRef14(false);
26554
26260
  const linkPopoverGraceUntilRef = useRef14(0);
26555
26261
  setLinkPopoverRef.current = setLinkPopover;
26556
- setFloatingPanelRef.current = setFloatingPanel;
26557
26262
  linkPopoverSessionRef.current = linkPopover;
26558
- floatingPanelOpenRef.current = Boolean(floatingPanel);
26559
- useEffect19(() => {
26560
- const syncViewport = () => {
26561
- const next = window.innerWidth <= 480 ? "mobile" : "desktop";
26562
- setEditorViewport((prev) => prev === next ? prev : next);
26563
- };
26564
- syncViewport();
26565
- window.addEventListener("resize", syncViewport);
26566
- return () => window.removeEventListener("resize", syncViewport);
26567
- }, []);
26568
26263
  useEffect19(() => {
26569
26264
  setSectionOrderPersistHandler((entries) => {
26570
26265
  const currentPath = window.location.pathname;
@@ -26713,43 +26408,6 @@ function OhhwellsBridge() {
26713
26408
  if (!useFixtures) postToParent2({ type: "ow:request-site-pages" });
26714
26409
  return () => window.removeEventListener("message", onSitePages);
26715
26410
  }, [isEditMode, postToParent2]);
26716
- useEffect19(() => {
26717
- if (!isEditMode) return;
26718
- const deps = {
26719
- getEditContent: () => editContentRef.current,
26720
- setEditContent: (next) => {
26721
- editContentRef.current = next;
26722
- },
26723
- applyLinkByKey,
26724
- postToParent: postToParent2
26725
- };
26726
- const onPageMessage = (e) => {
26727
- if (e.data?.type === "ow:page-created") {
26728
- const { path, title } = e.data;
26729
- if (typeof path !== "string" || !path) return;
26730
- if (addNavLinkForPage({ path, title: typeof title === "string" && title ? title : path }, deps)) {
26731
- enforceLinkHrefs();
26732
- }
26733
- return;
26734
- }
26735
- if (e.data?.type === "ow:page-renamed") {
26736
- const { oldPath, newPath, oldTitle, newTitle } = e.data;
26737
- if (typeof oldPath !== "string" || typeof newPath !== "string") return;
26738
- const moved = retargetPageLinks(
26739
- {
26740
- oldPath,
26741
- newPath,
26742
- oldTitle: typeof oldTitle === "string" ? oldTitle : void 0,
26743
- newTitle: typeof newTitle === "string" ? newTitle : void 0
26744
- },
26745
- deps
26746
- );
26747
- if (moved > 0) enforceLinkHrefs();
26748
- }
26749
- };
26750
- window.addEventListener("message", onPageMessage);
26751
- return () => window.removeEventListener("message", onPageMessage);
26752
- }, [isEditMode, postToParent2]);
26753
26411
  useEffect19(() => {
26754
26412
  if (!isEditMode || shouldUseDevFixtures()) return;
26755
26413
  void loadAllSectionsManifest().then((manifest) => {
@@ -28271,7 +27929,7 @@ function OhhwellsBridge() {
28271
27929
  isFooterFrameSelection,
28272
27930
  postToParent2
28273
27931
  ]);
28274
- useLayoutEffect7(() => {
27932
+ useLayoutEffect6(() => {
28275
27933
  if (!subdomain || isEditMode) {
28276
27934
  setFetchState("done");
28277
27935
  return;
@@ -28298,7 +27956,6 @@ function OhhwellsBridge() {
28298
27956
  applyAiFooterFromContent(content);
28299
27957
  if (typeof content[STYLE_STORE_KEY] === "string") {
28300
27958
  stylesRef.current = content[STYLE_STORE_KEY];
28301
- document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
28302
27959
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
28303
27960
  }
28304
27961
  applyBrandChrome(content);
@@ -28309,16 +27966,15 @@ function OhhwellsBridge() {
28309
27966
  if (key === "__ohw_sections") continue;
28310
27967
  if (key === AI_SECTIONS_KEY) continue;
28311
27968
  if (key === AI_FOOTER_KEY) continue;
28312
- if (key === LOGO_PLACEHOLDER_KEY) continue;
28313
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
28314
27969
  if (key === BRAND_KIT_KEY) continue;
28315
27970
  if (key === STYLE_STORE_KEY) continue;
28316
27971
  if (BRAND_CHROME_KEYS.has(key)) continue;
27972
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
28317
27973
  if (key === CUSTOM_CODE_AFTER_HEAD_KEY || key === CUSTOM_CODE_BEFORE_HEAD_KEY) continue;
27974
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
28318
27975
  if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
28319
27976
  continue;
28320
27977
  }
28321
- if (key.endsWith(SECTION_EMBED_SUFFIX)) continue;
28322
27978
  if (applyVideoSettingNode(key, val)) continue;
28323
27979
  if (applyCarouselNode(key, val)) continue;
28324
27980
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -28358,7 +28014,6 @@ function OhhwellsBridge() {
28358
28014
  });
28359
28015
  applyLinkByKey(key, val);
28360
28016
  }
28361
- applySectionEmbeds(content);
28362
28017
  applyLogoFromContent(content);
28363
28018
  applyLogoSizes(content);
28364
28019
  reconcileNavbarItemsFromContent(content);
@@ -28547,22 +28202,20 @@ function OhhwellsBridge() {
28547
28202
  }
28548
28203
  applyAiFooterFromContent(content);
28549
28204
  if (typeof content[STYLE_STORE_KEY] === "string") {
28550
- document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
28551
28205
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
28552
28206
  }
28553
28207
  for (const [key, val] of Object.entries(content)) {
28554
28208
  if (key === "__ohw_sections") continue;
28555
28209
  if (key === AI_SECTIONS_KEY) continue;
28556
28210
  if (key === AI_FOOTER_KEY) continue;
28557
- if (key === LOGO_PLACEHOLDER_KEY) continue;
28558
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
28559
28211
  if (key === BRAND_KIT_KEY) continue;
28560
28212
  if (key === STYLE_STORE_KEY) continue;
28561
28213
  if (BRAND_CHROME_KEYS.has(key)) continue;
28214
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
28215
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
28562
28216
  if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
28563
28217
  continue;
28564
28218
  }
28565
- if (key.endsWith(SECTION_EMBED_SUFFIX)) continue;
28566
28219
  if (applyVideoSettingNode(key, val)) continue;
28567
28220
  if (applyCarouselNode(key, val)) continue;
28568
28221
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -28587,7 +28240,6 @@ function OhhwellsBridge() {
28587
28240
  });
28588
28241
  applyLinkByKey(key, val);
28589
28242
  }
28590
- applySectionEmbeds(content);
28591
28243
  applyLogoFromContent(content);
28592
28244
  reconcileNavbarItemsFromContent(content);
28593
28245
  reconcileFooterOrderFromContent(content);
@@ -28643,7 +28295,7 @@ function OhhwellsBridge() {
28643
28295
  if (debounceTimer) clearTimeout(debounceTimer);
28644
28296
  };
28645
28297
  }, [subdomain, isEditMode, pathname]);
28646
- useLayoutEffect7(() => {
28298
+ useLayoutEffect6(() => {
28647
28299
  const el = document.getElementById("ohw-loader");
28648
28300
  if (!el) return;
28649
28301
  const visible = Boolean(subdomain) && fetchState !== "done";
@@ -28827,11 +28479,26 @@ function OhhwellsBridge() {
28827
28479
  const t2 = setTimeout(measure, 500);
28828
28480
  const ro = new ResizeObserver(schedule);
28829
28481
  ro.observe(document.body);
28482
+ let lastWidth = window.innerWidth;
28483
+ let resizeTimers = [];
28484
+ const clearResizeTimers = () => {
28485
+ resizeTimers.forEach(clearTimeout);
28486
+ resizeTimers = [];
28487
+ };
28488
+ const handleResize = () => {
28489
+ if (window.innerWidth === lastWidth) return;
28490
+ lastWidth = window.innerWidth;
28491
+ clearResizeTimers();
28492
+ resizeTimers = HEIGHT_SETTLE_DELAYS.map((delay) => setTimeout(measure, delay));
28493
+ };
28494
+ window.addEventListener("resize", handleResize);
28830
28495
  return () => {
28831
28496
  clearTimeout(t1);
28832
28497
  clearTimeout(t2);
28833
28498
  if (raf != null) cancelAnimationFrame(raf);
28834
28499
  ro.disconnect();
28500
+ clearResizeTimers();
28501
+ window.removeEventListener("resize", handleResize);
28835
28502
  };
28836
28503
  }, [pathname, isEditMode, postToParent2]);
28837
28504
  useEffect19(() => {
@@ -29128,13 +28795,6 @@ function OhhwellsBridge() {
29128
28795
  if (target.closest("[data-ohw-state-toggle]")) return;
29129
28796
  if (target.closest("[data-ohw-max-badge]")) return;
29130
28797
  if (isInsideLinkEditor(target)) return;
29131
- if (isInsideFloatingPanel(target)) return;
29132
- if (target.closest("[data-ohw-form-toolbar]")) return;
29133
- if (target.closest(
29134
- '[data-ohw-field-toolbar], [data-ohw-field-type-picker], [data-radix-popper-content-wrapper], [role="menu"], [data-slot="dropdown-menu-content"]'
29135
- )) {
29136
- return;
29137
- }
29138
28798
  if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
29139
28799
  clearMediaSelectionRef.current();
29140
28800
  }
@@ -29158,6 +28818,8 @@ function OhhwellsBridge() {
29158
28818
  return;
29159
28819
  }
29160
28820
  }
28821
+ if (isInsideFloatingPanel(target)) return;
28822
+ if (target.closest("[data-ohw-form-toolbar]")) return;
29161
28823
  const collectionItem = matchCollectionItemClick(target, collectionItemsRef.current);
29162
28824
  if (collectionItem) {
29163
28825
  e.preventDefault();
@@ -29169,6 +28831,11 @@ function OhhwellsBridge() {
29169
28831
  });
29170
28832
  return;
29171
28833
  }
28834
+ if (target.closest(
28835
+ '[data-ohw-field-toolbar], [data-ohw-field-type-picker], [data-radix-popper-content-wrapper], [role="menu"], [data-slot="dropdown-menu-content"]'
28836
+ )) {
28837
+ return;
28838
+ }
29172
28839
  {
29173
28840
  const formEl = getFormElement(target);
29174
28841
  const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
@@ -29322,13 +28989,9 @@ function OhhwellsBridge() {
29322
28989
  if (isIconEditable(editable) && !getSocialItem(editable)) {
29323
28990
  e.preventDefault();
29324
28991
  e.stopPropagation();
28992
+ selectIconElementRef.current(editable);
29325
28993
  const aiIcon = editable.hasAttribute("data-ai-icon");
29326
28994
  const featureIcon = aiIcon && /\.features\.\d+\.icon$/.test(editable.dataset.ohwKey ?? "");
29327
- if (aiIcon) {
29328
- aiSectionApiRef.current?.selectFromElement(editable);
29329
- } else {
29330
- selectIconElementRef.current(editable);
29331
- }
29332
28995
  postToParentRef.current({
29333
28996
  type: "ow:icon-pick",
29334
28997
  key: editable.dataset.ohwKey ?? "",
@@ -29556,9 +29219,6 @@ function OhhwellsBridge() {
29556
29219
  setHoveredItemRect(null);
29557
29220
  hoveredNavContainerRef.current = null;
29558
29221
  setHoveredNavContainerRect(null);
29559
- siblingHintElRef.current = null;
29560
- setSiblingHintRect(null);
29561
- setSiblingHintRects([]);
29562
29222
  return;
29563
29223
  }
29564
29224
  {
@@ -29679,6 +29339,7 @@ function OhhwellsBridge() {
29679
29339
  hoveredNavContainerRef.current = null;
29680
29340
  setHoveredNavContainerRect(null);
29681
29341
  hoveredItemElRef.current = editable;
29342
+ setHoveredItemRect(isSelectedForHoverRef.current(editable) ? null : editable.getBoundingClientRect());
29682
29343
  }
29683
29344
  }
29684
29345
  }
@@ -30388,11 +30049,7 @@ function OhhwellsBridge() {
30388
30049
  };
30389
30050
  const handleMouseMove = (e) => {
30390
30051
  const { clientX, clientY } = e;
30391
- if (pointOwnedByFloatingPanel(clientX, clientY)) {
30392
- dismissImageHover();
30393
- clearImageHover();
30394
- return;
30395
- }
30052
+ if (pointOwnedByFloatingPanel(clientX, clientY)) return;
30396
30053
  if (isNavDrawerOpen() && isPointOutsideOpenDrawer(clientX, clientY)) {
30397
30054
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
30398
30055
  formHoverElRef.current = null;
@@ -30421,12 +30078,7 @@ function OhhwellsBridge() {
30421
30078
  setHoveredItemRect(null);
30422
30079
  hoveredNavContainerRef.current = null;
30423
30080
  setHoveredNavContainerRect(null);
30424
- siblingHintElRef.current = null;
30425
- setSiblingHintRect(null);
30426
- setSiblingHintRects([]);
30427
30081
  dismissImageHover();
30428
- clearImageHover();
30429
- setSectionGap(null);
30430
30082
  return;
30431
30083
  }
30432
30084
  if (probeSocialsRowAt(clientX, clientY)) return;
@@ -30438,11 +30090,7 @@ function OhhwellsBridge() {
30438
30090
  if (e.data?.type !== "ow:pointer-sync") return;
30439
30091
  const { clientX, clientY } = e.data;
30440
30092
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
30441
- if (pointOwnedByFloatingPanel(clientX, clientY)) {
30442
- dismissImageHover();
30443
- clearImageHover();
30444
- return;
30445
- }
30093
+ if (pointOwnedByFloatingPanel(clientX, clientY)) return;
30446
30094
  if (isNavDrawerOpen() && isPointOutsideOpenDrawer(clientX, clientY)) return;
30447
30095
  if (probeSocialsRowAt(clientX, clientY)) return;
30448
30096
  probeSectionGapAt(clientX, clientY);
@@ -30822,7 +30470,6 @@ function OhhwellsBridge() {
30822
30470
  applyAiFooterFromContent(content);
30823
30471
  if (typeof content[STYLE_STORE_KEY] === "string") {
30824
30472
  stylesRef.current = content[STYLE_STORE_KEY];
30825
- document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
30826
30473
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
30827
30474
  }
30828
30475
  applyBrandChrome(content);
@@ -30837,16 +30484,15 @@ function OhhwellsBridge() {
30837
30484
  }
30838
30485
  if (key === AI_SECTIONS_KEY) continue;
30839
30486
  if (key === AI_FOOTER_KEY) continue;
30840
- if (key === LOGO_PLACEHOLDER_KEY) continue;
30841
- if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
30842
30487
  if (key === BRAND_KIT_KEY) continue;
30843
30488
  if (key === STYLE_STORE_KEY) continue;
30844
30489
  if (BRAND_CHROME_KEYS.has(key)) continue;
30490
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
30845
30491
  if (key === CUSTOM_CODE_AFTER_HEAD_KEY || key === CUSTOM_CODE_BEFORE_HEAD_KEY) continue;
30492
+ if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
30846
30493
  if (typeof content[AI_FOOTER_KEY] === "string" && content[AI_FOOTER_KEY] && isAiFooterOwnedContentKey(key)) {
30847
30494
  continue;
30848
30495
  }
30849
- if (key.endsWith(SECTION_EMBED_SUFFIX)) continue;
30850
30496
  if (applyVideoSettingNode(key, val)) continue;
30851
30497
  if (applyCarouselNode(key, val)) continue;
30852
30498
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -30872,7 +30518,6 @@ function OhhwellsBridge() {
30872
30518
  });
30873
30519
  applyLinkByKey(key, val);
30874
30520
  }
30875
- applySectionEmbeds(content);
30876
30521
  applyLogoFromContent(content);
30877
30522
  applyLogoSizes(content);
30878
30523
  document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
@@ -31480,12 +31125,6 @@ function OhhwellsBridge() {
31480
31125
  closeLinkPopoverRef.current();
31481
31126
  return;
31482
31127
  }
31483
- if (floatingPanelOpenRef.current) {
31484
- setFloatingPanelRef.current(null);
31485
- deselectRef.current();
31486
- deactivateRef.current();
31487
- return;
31488
- }
31489
31128
  deselectRef.current();
31490
31129
  deactivateRef.current();
31491
31130
  clearMediaSelectionRef.current();
@@ -32321,10 +31960,10 @@ function OhhwellsBridge() {
32321
31960
  window.removeEventListener("message", handleDuplicateSection);
32322
31961
  window.removeEventListener("message", handleDeactivate);
32323
31962
  window.removeEventListener("message", handleSelectNav);
31963
+ document.documentElement.removeAttribute("data-ohw-panel-dragging");
32324
31964
  window.removeEventListener("message", handleToastAction);
32325
31965
  window.removeEventListener("message", handleFormCount);
32326
31966
  window.removeEventListener("message", handleUiEscape);
32327
- document.documentElement.removeAttribute("data-ohw-panel-dragging");
32328
31967
  autoSaveTimers.current.forEach(clearTimeout);
32329
31968
  autoSaveTimers.current.clear();
32330
31969
  if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
@@ -32618,7 +32257,7 @@ function OhhwellsBridge() {
32618
32257
  postToParent2({
32619
32258
  type: "ow:ready",
32620
32259
  version: "1",
32621
- bridgeVersion: "0.1.160-next.429",
32260
+ bridgeVersion: "0.1.161",
32622
32261
  path: pathname,
32623
32262
  nodes: collectEditableNodes(editContentRef.current),
32624
32263
  sections
@@ -33353,7 +32992,7 @@ function OhhwellsBridge() {
33353
32992
  className: "flex h-7 w-7 items-center justify-center rounded-md text-foreground transition-colors hover:bg-muted/80",
33354
32993
  onClick: () => setFieldTypePickerOpen((open) => !open),
33355
32994
  "data-ohw-add-field": "",
33356
- children: /* @__PURE__ */ jsx40(Plus6, { size: 15, "aria-hidden": true })
32995
+ children: /* @__PURE__ */ jsx40(Plus5, { size: 15, "aria-hidden": true })
33357
32996
  }
33358
32997
  ),
33359
32998
  /* @__PURE__ */ jsx40("div", { className: "mx-0.5 h-5 w-px bg-border" }),