@ohhwells/bridge 0.1.111 → 0.1.112-next.289

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
@@ -279,16 +279,17 @@ var LEGACY_BRAND_VAR_NAMES = [
279
279
  "text-muted",
280
280
  "surface",
281
281
  "border",
282
- "on-primary",
283
- "navbar-background"
282
+ "on-primary"
284
283
  ].map((role) => `--brand-${role}`);
285
284
  var FONT_VARS = {
286
285
  heading: ["--font-heading", "--font-display", "--brand-font-heading"],
287
286
  body: ["--font-body", "--brand-font-body"]
288
287
  };
289
288
  var BRAND_FONT_LINK_ID = "ohw-brand-fonts";
290
- function brandColorVars(kit) {
291
- const { dark, primary, accent, light } = kit.palette;
289
+ var CUSTOM_FONT_STYLE_ID = "ohw-brand-custom-fonts";
290
+ var CUSTOM_FONT_FORMATS = /* @__PURE__ */ new Set(["woff2", "woff", "truetype", "opentype"]);
291
+ function brandColorVars(palette) {
292
+ const { dark, primary, accent, light } = palette;
292
293
  const mix = (a, aPct, b) => `color-mix(in srgb, ${a} ${aPct}%, ${b})`;
293
294
  const surface = mix(light, 95, dark);
294
295
  const border = mix(light, 85, dark);
@@ -311,22 +312,42 @@ function brandColorVars(kit) {
311
312
  "--brand-border": border,
312
313
  // Buttons/bands painted in the primary colour assume it's dark/saturated enough to need
313
314
  // light text on top — the same assumption LOGO_IMAGE's light-on-dark navbar mark makes.
314
- "--brand-on-primary": light,
315
- "--brand-navbar-background": dark
315
+ "--brand-on-primary": light
316
316
  };
317
317
  }
318
+ function parseCustomFontWeight(raw) {
319
+ if (typeof raw !== "object" || raw === null) return null;
320
+ const w = raw;
321
+ if (typeof w.weight !== "number" || !Number.isFinite(w.weight)) return null;
322
+ if (typeof w.url !== "string" || !w.url) return null;
323
+ if (typeof w.format !== "string" || !CUSTOM_FONT_FORMATS.has(w.format)) return null;
324
+ const weightEnd = typeof w.weightEnd === "number" && Number.isFinite(w.weightEnd) && w.weightEnd > w.weight ? w.weightEnd : void 0;
325
+ return { weight: w.weight, ...weightEnd !== void 0 ? { weightEnd } : {}, url: w.url, format: w.format };
326
+ }
327
+ function parseCustomFont(raw) {
328
+ if (typeof raw !== "object" || raw === null) return null;
329
+ const f = raw;
330
+ if (typeof f.family !== "string" || !f.family) return null;
331
+ if (typeof f.label !== "string" || !f.label) return null;
332
+ if (!Array.isArray(f.weights)) return null;
333
+ const weights = f.weights.map(parseCustomFontWeight).filter((w) => w !== null);
334
+ if (weights.length === 0) return null;
335
+ return { family: f.family, label: f.label, weights };
336
+ }
318
337
  function parseBrandKit(raw) {
319
338
  if (!raw) return null;
320
339
  try {
321
340
  const parsed = JSON.parse(raw);
322
341
  const p = parsed?.palette;
323
342
  const f = parsed?.fonts;
324
- if (!p || !f || typeof p.dark !== "string" || typeof p.primary !== "string" || typeof p.accent !== "string" || typeof p.light !== "string" || typeof f.heading !== "string" || typeof f.body !== "string") {
325
- return null;
326
- }
343
+ const palette = p && typeof p.dark === "string" && typeof p.primary === "string" && typeof p.accent === "string" && typeof p.light === "string" ? { dark: p.dark, primary: p.primary, accent: p.accent, light: p.light } : void 0;
344
+ const fonts = f && typeof f.heading === "string" && typeof f.body === "string" ? { heading: f.heading, body: f.body } : void 0;
345
+ const customFonts = Array.isArray(parsed?.customFonts) ? parsed.customFonts.map(parseCustomFont).filter((c) => c !== null) : void 0;
346
+ if (!palette && !fonts && !customFonts) return null;
327
347
  return {
328
- palette: { dark: p.dark, primary: p.primary, accent: p.accent, light: p.light },
329
- fonts: { heading: f.heading, body: f.body }
348
+ ...palette ? { palette } : {},
349
+ ...fonts ? { fonts } : {},
350
+ ...customFonts ? { customFonts } : {}
330
351
  };
331
352
  } catch {
332
353
  return null;
@@ -336,11 +357,16 @@ function familyOf(stack) {
336
357
  const first = stack.split(",")[0]?.trim() ?? "";
337
358
  return first.replace(/^['"]|['"]$/g, "");
338
359
  }
360
+ function quoteFamily(family) {
361
+ return `'${family.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
362
+ }
363
+ var WEIGHT_SCALE = [100, 200, 300, 400, 500, 600, 700, 800, 900];
339
364
  function loadBrandFonts(families) {
340
365
  const unique = [...new Set(families.map((f) => f.trim()).filter(Boolean))];
341
366
  if (unique.length === 0) return;
342
- const spec = unique.map((f) => `${f.replace(/ /g, "+")}:400,500,600,700`).join("|");
343
- const href = `https://fonts.googleapis.com/css?family=${spec}&display=swap`;
367
+ const weights = WEIGHT_SCALE.join(";");
368
+ const spec = unique.map((f) => `family=${f.replace(/ /g, "+")}:wght@${weights}`).join("&");
369
+ const href = `https://fonts.googleapis.com/css2?${spec}&display=swap`;
344
370
  let link = document.getElementById(BRAND_FONT_LINK_ID);
345
371
  if (!link) {
346
372
  link = document.createElement("link");
@@ -350,18 +376,54 @@ function loadBrandFonts(families) {
350
376
  }
351
377
  if (link.href !== href) link.href = href;
352
378
  }
379
+ function escapeCssString(value) {
380
+ return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
381
+ }
382
+ function loadCustomFonts(fonts) {
383
+ const list = fonts ?? [];
384
+ let style = document.getElementById(CUSTOM_FONT_STYLE_ID);
385
+ if (list.length === 0) {
386
+ style?.remove();
387
+ return;
388
+ }
389
+ if (!style) {
390
+ style = document.createElement("style");
391
+ style.id = CUSTOM_FONT_STYLE_ID;
392
+ document.head.appendChild(style);
393
+ }
394
+ style.textContent = list.flatMap(
395
+ (font) => font.weights.map((w) => {
396
+ const weightDescriptor = w.weightEnd ? `${w.weight} ${w.weightEnd}` : `${w.weight}`;
397
+ return `@font-face { font-family: '${escapeCssString(font.family)}'; src: url('${escapeCssString(w.url)}') format('${w.format}'); font-weight: ${weightDescriptor}; font-display: swap; }`;
398
+ })
399
+ ).join("\n");
400
+ }
353
401
  function applyBrandToDom(kit) {
354
402
  const root = document.documentElement;
355
403
  if (!kit) {
356
404
  for (const name of [...BRAND_VAR_NAMES, ...LEGACY_BRAND_VAR_NAMES]) root.style.removeProperty(name);
357
405
  for (const name of [...FONT_VARS.heading, ...FONT_VARS.body]) root.style.removeProperty(name);
358
406
  document.getElementById(BRAND_FONT_LINK_ID)?.remove();
407
+ document.getElementById(CUSTOM_FONT_STYLE_ID)?.remove();
359
408
  return;
360
409
  }
361
- for (const [name, value] of Object.entries(brandColorVars(kit))) root.style.setProperty(name, value);
362
- for (const name of FONT_VARS.heading) root.style.setProperty(name, kit.fonts.heading);
363
- for (const name of FONT_VARS.body) root.style.setProperty(name, kit.fonts.body);
364
- loadBrandFonts([familyOf(kit.fonts.heading), familyOf(kit.fonts.body)]);
410
+ if (kit.palette) {
411
+ for (const [name, value] of Object.entries(brandColorVars(kit.palette))) root.style.setProperty(name, value);
412
+ }
413
+ if (kit.customFonts) {
414
+ loadCustomFonts(kit.customFonts);
415
+ }
416
+ if (kit.fonts) {
417
+ const heading = quoteFamily(kit.fonts.heading);
418
+ const body = quoteFamily(kit.fonts.body);
419
+ for (const name of FONT_VARS.heading) root.style.setProperty(name, heading);
420
+ for (const name of FONT_VARS.body) root.style.setProperty(name, body);
421
+ const customFamilies = new Set((kit.customFonts ?? []).map((f) => f.family));
422
+ const googleFamilies = [familyOf(kit.fonts.heading), familyOf(kit.fonts.body)].filter(
423
+ (f) => !customFamilies.has(f)
424
+ );
425
+ loadBrandFonts(googleFamilies);
426
+ }
365
427
  }
366
428
 
367
429
  // src/lib/section-styles.ts
@@ -386,6 +448,20 @@ var BG_VALUES = {
386
448
  accent: "var(--ohw-brand-primary, var(--color-primary, #0078E5))",
387
449
  "accent-soft": "color-mix(in srgb, var(--ohw-brand-primary, var(--color-primary, #0078E5)) 12%, var(--ohw-brand-light, var(--color-light, #FFFFFF)))"
388
450
  };
451
+ var NAVBAR_COLOR_TOKENS = ["dark", "primary", "accent", "light"];
452
+ function navbarColorVar(token) {
453
+ const fallback = {
454
+ dark: "#0C0A09",
455
+ primary: "#0078E5",
456
+ accent: "#0078E5",
457
+ light: "#FFFFFF"
458
+ };
459
+ return `var(--ohw-brand-${token}, var(--color-${token}, ${fallback[token]}))`;
460
+ }
461
+ function resolveNavbarValue(value) {
462
+ if (value === void 0) return void 0;
463
+ return NAVBAR_COLOR_TOKENS.includes(value) ? navbarColorVar(value) : value;
464
+ }
389
465
  var HEADLINE_SIZES = { md: 24, lg: 32, xl: 40, display: 56 };
390
466
  function styleSheetCss() {
391
467
  const rules = [];
@@ -555,8 +631,16 @@ function applyStylesToDom(store) {
555
631
  for (const [sectionId, override] of Object.entries(store.sections)) {
556
632
  const escaped = CSS.escape(sectionId);
557
633
  const byInstance = document.querySelectorAll(`[data-ohw-instance="${escaped}"]`);
558
- const sections = byInstance.length ? byInstance : document.querySelectorAll(`[data-ohw-section="${escaped}"]`);
634
+ const sections = Array.from(
635
+ byInstance.length ? byInstance : document.querySelectorAll(`[data-ohw-section="${escaped}"]`)
636
+ );
637
+ const isNavbar = sectionId === "navbar";
638
+ if (isNavbar) {
639
+ const drawer = document.querySelector("[data-ohw-nav-drawer]");
640
+ if (drawer && !sections.includes(drawer)) sections.push(drawer);
641
+ }
559
642
  for (const marker of Array.from(sections)) {
643
+ const isDrawerMarker = isNavbar && marker.hasAttribute("data-ohw-nav-drawer");
560
644
  const section = marker.querySelector(":scope > [data-ai-section]") ?? marker;
561
645
  for (const [prop, attr] of Object.entries(SECTION_ATTRS)) {
562
646
  const value = override[prop];
@@ -564,19 +648,23 @@ function applyStylesToDom(store) {
564
648
  if (prop === "sectionBackground" && override.sectionBackgroundColor !== void 0) continue;
565
649
  section.setAttribute(attr, String(value).replace(":", "-"));
566
650
  }
567
- if (override.sectionBackgroundColor !== void 0) {
651
+ const bgOverride = isDrawerMarker ? override.navbarMobileBackground ?? override.sectionBackgroundColor : override.sectionBackgroundColor;
652
+ const textOverride = isDrawerMarker ? override.navbarMobileTextColor ?? override.textColor : override.textColor;
653
+ const bgValue = isNavbar ? resolveNavbarValue(bgOverride) : bgOverride;
654
+ const textValue = isNavbar ? resolveNavbarValue(textOverride) : textOverride;
655
+ if (bgValue !== void 0) {
568
656
  saveInline(section, "background");
569
- section.style.setProperty("background", override.sectionBackgroundColor, "important");
657
+ section.style.setProperty("background", bgValue, "important");
570
658
  section.setAttribute("data-ohw-style-bgcolor", "");
571
659
  }
572
- if (override.textColor !== void 0 || override.fontFamily !== void 0) {
660
+ if (textValue !== void 0 || override.fontFamily !== void 0) {
573
661
  const textEls = marker.querySelectorAll(
574
662
  '[data-ohw-editable="text"], [data-ohw-editable="plain"], [data-ohw-field-label]'
575
663
  );
576
664
  for (const el of Array.from(textEls)) {
577
- if (override.textColor !== void 0) {
665
+ if (textValue !== void 0) {
578
666
  saveInline(el, "color");
579
- el.style.setProperty("color", override.textColor, "important");
667
+ el.style.setProperty("color", textValue, "important");
580
668
  el.setAttribute(NODE_WROTE_ATTR, "");
581
669
  }
582
670
  if (override.fontFamily !== void 0) {
@@ -1282,6 +1370,22 @@ function accentBandContext(brand) {
1282
1370
  function textAttrs(ctx, path) {
1283
1371
  return ctx.keyFor ? { "data-ohw-key": ctx.keyFor(path), "data-ohw-editable": "text" } : {};
1284
1372
  }
1373
+ var AI_RESPONSIVE_CSS = [
1374
+ "@media (max-width: 960px) {",
1375
+ " [data-ai-responsive] [data-ai-grid] { grid-template-columns: repeat(2, 1fr) !important; }",
1376
+ ' [data-ai-responsive] [data-ai-grid="1"] { grid-template-columns: 1fr !important; }',
1377
+ "}",
1378
+ "@media (max-width: 640px) {",
1379
+ " [data-ai-responsive] [data-ai-grid] { grid-template-columns: 1fr !important; }",
1380
+ " [data-ai-responsive] [data-ai-columns] > * { grid-column: 1 / -1 !important; }",
1381
+ // Group containers flatten to a column on phones; span placements come along for free.
1382
+ " [data-ai-responsive] [data-ai-group] { display: flex !important; flex-direction: column !important; }",
1383
+ " [data-ai-responsive] [data-ai-group] > * { grid-column: auto !important; }",
1384
+ " [data-ai-responsive] [data-ai-section-inner] { padding-left: 20px !important; padding-right: 20px !important; }",
1385
+ " [data-ai-responsive] { overflow-x: hidden; }",
1386
+ " [data-ai-responsive] img { max-width: 100%; }",
1387
+ "}"
1388
+ ].join("\n");
1285
1389
  var TRANSPARENT_PX = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
1286
1390
  function MediaBox({
1287
1391
  refValue,
@@ -1294,13 +1398,17 @@ function MediaBox({
1294
1398
  const url = refValue ? ctx.resolveMedia(refValue) : null;
1295
1399
  const isIcon = /^(lucide|simple):/.test(refValue);
1296
1400
  const aspectRatio = aspect && /^\d+:\d+$/.test(aspect) ? aspect.replace(":", " / ") : void 0;
1297
- const editAttrs = ctx.keyFor && editPath && !isIcon ? { "data-ohw-key": ctx.keyFor(editPath), "data-ohw-editable": "image" } : {};
1401
+ const editAttrs = ctx.keyFor && editPath ? {
1402
+ "data-ohw-key": ctx.keyFor(editPath),
1403
+ "data-ohw-editable": isIcon ? "icon" : "image"
1404
+ } : {};
1298
1405
  if (isIcon) {
1299
1406
  const Icon = refValue.startsWith("lucide:") ? lucideByName(refValue.slice(7)) : null;
1300
1407
  return /* @__PURE__ */ jsx(
1301
1408
  "span",
1302
1409
  {
1303
1410
  "data-ai-icon": refValue,
1411
+ ...editAttrs,
1304
1412
  style: {
1305
1413
  display: "inline-flex",
1306
1414
  width: 48,
@@ -2193,7 +2301,7 @@ function CollectionBlock({ node, ctx, path }) {
2193
2301
  return /* @__PURE__ */ jsx(
2194
2302
  "div",
2195
2303
  {
2196
- "data-ai-grid": "",
2304
+ "data-ai-grid": String(itemsPerRow),
2197
2305
  style: {
2198
2306
  display: "grid",
2199
2307
  gridTemplateColumns: `repeat(${itemsPerRow}, 1fr)`,
@@ -2546,6 +2654,7 @@ function AiTreeRenderer({
2546
2654
  {
2547
2655
  "data-ai-section": tree.tag ?? "",
2548
2656
  ...bgAttrs,
2657
+ "data-ai-responsive": "",
2549
2658
  style: {
2550
2659
  position: "relative",
2551
2660
  padding: `${pad}px 0`,
@@ -2556,12 +2665,13 @@ function AiTreeRenderer({
2556
2665
  color: settings.sectionBackground === "accent" ? blockBrand.palette.dark : void 0
2557
2666
  },
2558
2667
  children: [
2559
- isOverlay && backgroundUrl && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
2668
+ /* @__PURE__ */ jsx("style", { children: AI_RESPONSIVE_CSS }),
2560
2669
  /* @__PURE__ */ jsx("style", { children: AI_MOBILE_CSS }),
2670
+ isOverlay && backgroundUrl && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, background: `rgba(255,255,255,${overlayAlpha})` } }),
2561
2671
  /* @__PURE__ */ jsx(
2562
2672
  "div",
2563
2673
  {
2564
- "data-ai-container": "",
2674
+ "data-ai-section-inner": "",
2565
2675
  style: {
2566
2676
  position: "relative",
2567
2677
  maxWidth: AI_TREE_TOKENS.sectionMaxWidth,
@@ -2572,7 +2682,7 @@ function AiTreeRenderer({
2572
2682
  children: tree.rows.map((row, r2) => /* @__PURE__ */ jsx(
2573
2683
  "div",
2574
2684
  {
2575
- "data-ai-row": "",
2685
+ "data-ai-columns": "",
2576
2686
  style: {
2577
2687
  display: "grid",
2578
2688
  gridTemplateColumns: "repeat(12, 1fr)",
@@ -2622,14 +2732,52 @@ function readRootVar(name) {
2622
2732
  if (typeof document === "undefined") return "";
2623
2733
  return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
2624
2734
  }
2735
+ function normalizeColorToHex(value) {
2736
+ if (!value || typeof document === "undefined") return value;
2737
+ const canvas = document.createElement("canvas");
2738
+ canvas.width = 1;
2739
+ canvas.height = 1;
2740
+ const ctx = canvas.getContext("2d");
2741
+ if (!ctx) return value;
2742
+ ctx.fillStyle = value;
2743
+ ctx.fillRect(0, 0, 1, 1);
2744
+ const [r2, g, b] = ctx.getImageData(0, 0, 1, 1).data;
2745
+ const toHex = (n) => n.toString(16).padStart(2, "0");
2746
+ return `#${toHex(r2)}${toHex(g)}${toHex(b)}`;
2747
+ }
2748
+ var GENERIC_FONT_KEYWORDS = /* @__PURE__ */ new Set([
2749
+ "serif",
2750
+ "sans-serif",
2751
+ "monospace",
2752
+ "cursive",
2753
+ "fantasy",
2754
+ "system-ui",
2755
+ "ui-serif",
2756
+ "ui-sans-serif",
2757
+ "ui-monospace",
2758
+ "ui-rounded",
2759
+ "math",
2760
+ "emoji",
2761
+ "fangsong"
2762
+ ]);
2763
+ function primaryFontFamily(stack) {
2764
+ const first = stack.split(",").map((part) => part.trim().replace(/^["']|["']$/g, "")).find((part) => part && !GENERIC_FONT_KEYWORDS.has(part.toLowerCase()));
2765
+ return first ?? "";
2766
+ }
2767
+ function humanizeFontFamily(name) {
2768
+ return name.replace(/^__/, "").replace(/_[0-9a-f]{6}$/i, "").replace(/_/g, " ").replace(/([a-z0-9])([A-Z])/g, "$1 $2").trim().replace(/\s+/g, " ").replace(/(^|\s)([a-z])/g, (_, sep, c) => sep + c.toUpperCase());
2769
+ }
2770
+ function readFontVar(name) {
2771
+ return humanizeFontFamily(primaryFontFamily(readRootVar(name)));
2772
+ }
2625
2773
  function deriveBrandOverride() {
2626
2774
  const dark = readRootVar("--ohw-brand-dark");
2627
2775
  const primary = readRootVar("--ohw-brand-primary");
2628
2776
  const light = readRootVar("--ohw-brand-light");
2629
2777
  if (!dark || !primary || !light) return null;
2630
2778
  const accent = readRootVar("--ohw-brand-accent");
2631
- const heading = readRootVar("--font-heading") || readRootVar("--font-display");
2632
- const body = readRootVar("--font-body");
2779
+ const heading = readFontVar("--font-heading") || readFontVar("--font-display");
2780
+ const body = readFontVar("--font-body");
2633
2781
  return {
2634
2782
  palette: { dark, primary, accent: accent || dark, light },
2635
2783
  fonts: {
@@ -2638,22 +2786,55 @@ function deriveBrandOverride() {
2638
2786
  }
2639
2787
  };
2640
2788
  }
2641
- function deriveTemplateBrand() {
2789
+ function deriveTemplateBrandLive() {
2642
2790
  const primary = readRootVar("--brand-primary") || readRootVar("--color-primary");
2643
2791
  const dark = readRootVar("--brand-text") || readRootVar("--color-dark");
2644
2792
  const light = readRootVar("--brand-background") || readRootVar("--color-light");
2645
2793
  if (!dark || !primary || !light) return null;
2646
2794
  const accent = readRootVar("--brand-accent") || readRootVar("--color-accent");
2647
- const heading = readRootVar("--brand-font-heading") || readRootVar("--font-heading") || readRootVar("--font-display");
2648
- const body = readRootVar("--brand-font-body") || readRootVar("--font-body");
2795
+ const heading = readFontVar("--brand-font-heading") || readFontVar("--font-heading") || readFontVar("--font-display");
2796
+ const body = readFontVar("--brand-font-body") || readFontVar("--font-body");
2649
2797
  return {
2650
- palette: { dark, primary, accent: accent || dark, light },
2798
+ palette: {
2799
+ dark: normalizeColorToHex(dark),
2800
+ primary: normalizeColorToHex(primary),
2801
+ accent: normalizeColorToHex(accent || dark),
2802
+ light: normalizeColorToHex(light)
2803
+ },
2651
2804
  fonts: {
2652
2805
  heading: heading || AI_DEFAULT_BRAND.fonts.heading,
2653
2806
  body: body || AI_DEFAULT_BRAND.fonts.body
2654
2807
  }
2655
2808
  };
2656
2809
  }
2810
+ function deriveTemplateFontsLive() {
2811
+ const heading = readFontVar("--brand-font-heading") || readFontVar("--font-heading") || readFontVar("--font-display");
2812
+ const body = readFontVar("--brand-font-body") || readFontVar("--font-body");
2813
+ if (!heading && !body) return null;
2814
+ return {
2815
+ heading: heading || AI_DEFAULT_BRAND.fonts.heading,
2816
+ body: body || AI_DEFAULT_BRAND.fonts.body
2817
+ };
2818
+ }
2819
+ var OVERRIDE_VAR_NAMES = [...BRAND_VAR_NAMES, ...LEGACY_BRAND_VAR_NAMES, ...FONT_VARS.heading, ...FONT_VARS.body];
2820
+ function withOverrideStripped(read) {
2821
+ const root = document.documentElement;
2822
+ const restore = OVERRIDE_VAR_NAMES.map((name) => [name, root.style.getPropertyValue(name)]);
2823
+ for (const name of OVERRIDE_VAR_NAMES) root.style.removeProperty(name);
2824
+ try {
2825
+ return read();
2826
+ } finally {
2827
+ for (const [name, value] of restore) if (value) root.style.setProperty(name, value);
2828
+ }
2829
+ }
2830
+ var TEMPLATE_BRAND_SNAPSHOT = typeof document === "undefined" ? null : withOverrideStripped(deriveTemplateBrandLive);
2831
+ var TEMPLATE_FONTS_SNAPSHOT = typeof document === "undefined" ? null : withOverrideStripped(deriveTemplateFontsLive);
2832
+ function deriveTemplateBrand() {
2833
+ return TEMPLATE_BRAND_SNAPSHOT;
2834
+ }
2835
+ function deriveTemplateFonts() {
2836
+ return TEMPLATE_FONTS_SNAPSHOT;
2837
+ }
2657
2838
  function deriveTemplateButtonStyle() {
2658
2839
  if (typeof document === "undefined") return null;
2659
2840
  const candidates = Array.from(
@@ -7151,12 +7332,12 @@ var cva = (base, config) => (props) => {
7151
7332
  import { Toggle as TogglePrimitive } from "radix-ui";
7152
7333
  import { jsx as jsx5 } from "react/jsx-runtime";
7153
7334
  var toggleVariants = cva(
7154
- "inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-[color,box-shadow] outline-none hover:bg-muted hover:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
7335
+ "inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-[color,box-shadow] outline-none hover:bg-muted hover:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[state=on]:bg-bridge-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
7155
7336
  {
7156
7337
  variants: {
7157
7338
  variant: {
7158
7339
  default: "bg-transparent border-0",
7159
- outline: "border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground"
7340
+ outline: "border border-input bg-transparent shadow-xs hover:bg-bridge-accent hover:text-accent-foreground"
7160
7341
  },
7161
7342
  size: {
7162
7343
  default: "px-2 py-1",
@@ -7249,10 +7430,10 @@ var DragHandle = React4.forwardRef(
7249
7430
  type,
7250
7431
  "data-slot": "drag-handle",
7251
7432
  className: cn(
7252
- "inline-flex h-7 w-4 shrink-0 items-center justify-center rounded-md transition-all duration-200 max-h-[30px]",
7433
+ "inline-flex h-7 w-4 shrink-0 items-center justify-center rounded-md transition-all duration-200 max-h-7.5",
7253
7434
  "bg-white border border-transparent text-stone-500 shadow-md cursor-grab",
7254
7435
  "enabled:hover:border enabled:hover:border-stone-200 enabled:hover:text-stone-950",
7255
- "enabled:active:border enabled:active:border-primary enabled:active:bg-primary-50 enabled:active:text-stone-950 enabled:active:shadow enabled:active:cursor-grabbing",
7436
+ "enabled:active:border enabled:active:border-bridge-primary enabled:active:bg-primary-50 enabled:active:text-stone-950 enabled:active:shadow enabled:active:cursor-grabbing",
7256
7437
  "disabled:cursor-not-allowed disabled:opacity-40 disabled:text-stone-950 disabled:pointer-events-none",
7257
7438
  className
7258
7439
  ),
@@ -7274,7 +7455,7 @@ var CustomToolbar = React5.forwardRef(({ className, onMouseDown, ...props }, ref
7274
7455
  "data-ohw-toolbar": "",
7275
7456
  className: cn(
7276
7457
  // Figma: bg background, radius 8, gap-1, p-0.5, shadow-md — no border
7277
- "inline-flex h-8 items-center gap-1 rounded-[var(--radius,0.5rem)] bg-background p-0.5 font-sans whitespace-nowrap shadow-md",
7458
+ "inline-flex h-8 items-center gap-1 rounded-(--radius,0.5rem) bg-background p-0.5 font-sans whitespace-nowrap shadow-md",
7278
7459
  className
7279
7460
  ),
7280
7461
  onMouseDown: (e) => {
@@ -7303,7 +7484,7 @@ var CustomToolbarButton = React5.forwardRef(
7303
7484
  type,
7304
7485
  className: cn(
7305
7486
  "inline-flex size-7 shrink-0 items-center justify-center rounded-[calc(var(--radius,0.5rem)-2px)] text-foreground transition-colors",
7306
- active ? "bg-primary text-primary-foreground" : "bg-transparent hover:bg-muted disabled:cursor-not-allowed disabled:text-muted-foreground disabled:opacity-60",
7487
+ active ? "bg-bridge-primary text-primary-foreground" : "bg-transparent hover:bg-muted disabled:cursor-not-allowed disabled:text-muted-foreground disabled:opacity-60",
7307
7488
  className
7308
7489
  ),
7309
7490
  ...props
@@ -8567,13 +8748,13 @@ function FormFieldToolbar({
8567
8748
  ]
8568
8749
  }
8569
8750
  ) }),
8570
- /* @__PURE__ */ jsx13(DropdownMenuContent, { align: "start", sideOffset: 8, className: "min-w-[190px] p-1", children: FIELD_TYPES.map((entry) => {
8751
+ /* @__PURE__ */ jsx13(DropdownMenuContent, { align: "start", sideOffset: 8, className: "min-w-47.5 p-1", children: FIELD_TYPES.map((entry) => {
8571
8752
  const Icon = TYPE_ICONS[entry.type];
8572
8753
  return /* @__PURE__ */ jsxs6(
8573
8754
  DropdownMenuItem,
8574
8755
  {
8575
8756
  onSelect: () => onTypeChange(entry.type),
8576
- className: "rounded-md py-2 text-[13px] " + (entry.type === type ? "bg-primary/10" : ""),
8757
+ className: "rounded-md py-2 text-[13px] " + (entry.type === type ? "bg-bridge-primary/10" : ""),
8577
8758
  children: [
8578
8759
  /* @__PURE__ */ jsx13(Icon, { size: 14, strokeWidth: 1.75, className: "shrink-0", "aria-hidden": true }),
8579
8760
  entry.label
@@ -8590,7 +8771,7 @@ function FormFieldToolbar({
8590
8771
  type: "button",
8591
8772
  "aria-pressed": required,
8592
8773
  onClick: onRequiredToggle,
8593
- className: "flex h-7 items-center gap-1.5 whitespace-nowrap rounded-md px-2 text-[13px] font-medium transition-colors " + (required ? "bg-primary/10 text-primary" : "text-foreground hover:bg-muted/70"),
8774
+ className: "flex h-7 items-center gap-1.5 whitespace-nowrap rounded-md px-2 text-[13px] font-medium transition-colors " + (required ? "bg-bridge-primary/10 text-bridge-primary" : "text-foreground hover:bg-muted/70"),
8594
8775
  "data-ohw-field-required-toggle": "",
8595
8776
  children: [
8596
8777
  /* @__PURE__ */ jsx13(Asterisk, { size: 14, strokeWidth: 2, "aria-hidden": true }),
@@ -8610,7 +8791,7 @@ function FormFieldToolbar({
8610
8791
  children: /* @__PURE__ */ jsx13(MoreHorizontal2, { size: 15, "aria-hidden": true })
8611
8792
  }
8612
8793
  ) }),
8613
- /* @__PURE__ */ jsxs6(DropdownMenuContent, { align: "start", sideOffset: 8, className: "min-w-[170px] p-1", children: [
8794
+ /* @__PURE__ */ jsxs6(DropdownMenuContent, { align: "start", sideOffset: 8, className: "min-w-42.5 p-1", children: [
8614
8795
  /* @__PURE__ */ jsxs6(DropdownMenuItem, { onSelect: onDuplicate, className: "rounded-md py-2 text-[13px]", children: [
8615
8796
  /* @__PURE__ */ jsx13(Copy2, { size: 14, strokeWidth: 1.75, className: "shrink-0", "aria-hidden": true }),
8616
8797
  "Duplicate"
@@ -8638,7 +8819,7 @@ function FieldTypePicker({ onPick }) {
8638
8819
  {
8639
8820
  type: "button",
8640
8821
  onClick: () => onPick(entry.type),
8641
- className: "flex h-[76px] flex-col items-center justify-center gap-2 rounded-xl border border-border text-[15px] font-medium text-foreground transition-colors hover:border-primary hover:bg-primary/5",
8822
+ className: "flex h-[76px] flex-col items-center justify-center gap-2 rounded-xl border border-border text-[15px] font-medium text-foreground transition-colors hover:border-bridge-primary hover:bg-bridge-primary/5",
8642
8823
  children: [
8643
8824
  /* @__PURE__ */ jsx13(Icon, { size: 26, strokeWidth: 1.5, "aria-hidden": true }),
8644
8825
  entry.label
@@ -8664,7 +8845,7 @@ var buttonVariants = cva(
8664
8845
  {
8665
8846
  variants: {
8666
8847
  variant: {
8667
- default: "bg-primary text-primary-foreground hover:opacity-90",
8848
+ default: "bg-bridge-primary text-primary-foreground hover:opacity-90",
8668
8849
  outline: "border border-border bg-background text-foreground shadow-sm hover:bg-muted/80",
8669
8850
  ghost: "min-w-0 px-3 py-2 text-foreground hover:bg-muted/50"
8670
8851
  },
@@ -8759,6 +8940,7 @@ function MediaOverlay({
8759
8940
  (prev) => prev && prev.fullWidth === width && prev.height === height ? prev : { fullWidth: width, height }
8760
8941
  );
8761
8942
  }, [isVideo]);
8943
+ const replaceLabel = isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image";
8762
8944
  const replaceMode = buttonSize ? replaceButtonMode({ width: rect.width, height: rect.height }, buttonSize) : "none";
8763
8945
  const box = {
8764
8946
  position: "fixed",
@@ -8864,8 +9046,8 @@ function MediaOverlay({
8864
9046
  pointerEvents: hover.hasTextOverlap ? "none" : "auto",
8865
9047
  // Selected: a firm component ring with no wash, so the image reads as chosen rather
8866
9048
  // than hovered. Hover keeps the existing tinted preview.
8867
- boxShadow: selected ? "inset 0 0 0 2px var(--color-primary)" : "inset 0 0 0 1.5px var(--color-primary)",
8868
- background: selected ? "transparent" : "color-mix(in srgb, var(--color-primary) 20%, transparent)"
9049
+ boxShadow: selected ? "inset 0 0 0 2px var(--color-bridge-primary)" : "inset 0 0 0 1.5px var(--color-bridge-primary)",
9050
+ background: selected ? "transparent" : "color-mix(in srgb, var(--color-bridge-primary) 20%, transparent)"
8869
9051
  },
8870
9052
  onClick: () => onSelect && !selected ? onSelect(hover.key) : onReplace(hover.key),
8871
9053
  children: [
@@ -8888,17 +9070,17 @@ function MediaOverlay({
8888
9070
  },
8889
9071
  children: [
8890
9072
  isVideo ? /* @__PURE__ */ jsx15(Film, { size: 14 }) : /* @__PURE__ */ jsx15(ImageIcon, { size: 14 }),
8891
- isVideo ? "Replace video" : "Replace image"
9073
+ replaceLabel
8892
9074
  ]
8893
9075
  }
8894
9076
  ),
8895
- replaceMode === "none" ? null : /* @__PURE__ */ jsxs7(
9077
+ showChrome && replaceMode !== "none" && /* @__PURE__ */ jsxs7(
8896
9078
  Button,
8897
9079
  {
8898
9080
  "data-ohw-media-overlay": "",
8899
9081
  variant: "outline",
8900
9082
  size: "sm",
8901
- "aria-label": isVideo ? "Replace video" : "Replace image",
9083
+ "aria-label": replaceLabel,
8902
9084
  className: "gap-1.5 cursor-pointer hover:bg-background",
8903
9085
  style: {
8904
9086
  ...OVERLAY_BUTTON_STYLE,
@@ -8921,7 +9103,7 @@ function MediaOverlay({
8921
9103
  },
8922
9104
  children: [
8923
9105
  isVideo ? /* @__PURE__ */ jsx15(Film, { size: 14 }) : /* @__PURE__ */ jsx15(ImageIcon, { size: 14 }),
8924
- replaceMode === "full" ? isVideo ? "Replace video" : hover.elementType === "bg-image" ? "Replace background" : "Replace image" : null
9106
+ replaceMode === "full" ? replaceLabel : null
8925
9107
  ]
8926
9108
  }
8927
9109
  )
@@ -8959,8 +9141,8 @@ function CarouselOverlay({
8959
9141
  height: rect.height,
8960
9142
  zIndex: 2147483646,
8961
9143
  pointerEvents: "auto",
8962
- boxShadow: "inset 0 0 0 1.5px var(--color-primary)",
8963
- background: "color-mix(in srgb, var(--color-primary) 20%, transparent)"
9144
+ boxShadow: "inset 0 0 0 1.5px var(--color-bridge-primary)",
9145
+ background: "color-mix(in srgb, var(--color-bridge-primary) 20%, transparent)"
8964
9146
  },
8965
9147
  onClick: () => onEdit(hover.key),
8966
9148
  children: /* @__PURE__ */ jsxs8(
@@ -9813,7 +9995,7 @@ function SectionTreeItem({
9813
9995
  /* @__PURE__ */ jsx20(
9814
9996
  "div",
9815
9997
  {
9816
- className: "mr-[-1px] h-9 w-2 shrink-0 rounded-bl-sm border-b border-l border-border mb-4",
9998
+ className: "-mr-px h-9 w-2 shrink-0 rounded-bl-sm border-b border-l border-border mb-4",
9817
9999
  "aria-hidden": true
9818
10000
  }
9819
10001
  ),
@@ -9832,7 +10014,7 @@ function SectionTreeItem({
9832
10014
  className: cn(
9833
10015
  "flex h-9 min-w-0 flex-1 items-center gap-2 rounded-md border border-border bg-background p-3",
9834
10016
  interactive && "cursor-pointer hover:bg-muted/30",
9835
- interactive && selected && "border-primary"
10017
+ interactive && selected && "border-bridge-primary"
9836
10018
  ),
9837
10019
  children: [
9838
10020
  /* @__PURE__ */ jsx20(
@@ -9962,7 +10144,7 @@ function UrlOrPageInput({
9962
10144
  };
9963
10145
  const fieldClassName = cn(
9964
10146
  "data-ohw-link-field flex h-[36px] w-full items-center overflow-hidden rounded-md border bg-background pl-3 pr-3 py-2 outline-none transition-[border-color,box-shadow]",
9965
- urlError ? "border-destructive shadow-[0_0_0_1px_var(--ohw-destructive)]" : isFocused ? "border-primary shadow-[0_0_0_1px_var(--ohw-primary)]" : "border-input"
10147
+ urlError ? "border-destructive shadow-[0_0_0_1px_var(--ohw-destructive)]" : isFocused ? "border-bridge-primary shadow-[0_0_0_1px_var(--ohw-primary)]" : "border-input"
9966
10148
  );
9967
10149
  return /* @__PURE__ */ jsxs13("div", { className: "flex w-full flex-col gap-2 p-0", children: [
9968
10150
  /* @__PURE__ */ jsx23(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
@@ -13869,6 +14051,7 @@ function readLogoSizeState(content, placement) {
13869
14051
  function getLogoElement(el) {
13870
14052
  const marked = el.closest('[data-ohw-role="logo"], [data-ohw-logo]');
13871
14053
  if (marked) return marked;
14054
+ if (el.closest('[data-ohw-editable="icon"]')) return null;
13872
14055
  const root = el.closest("nav, [data-ohw-nav-root], footer");
13873
14056
  if (!root) return null;
13874
14057
  const anchor = el.closest("a");
@@ -14162,7 +14345,7 @@ function DisplaySwitch({
14162
14345
  onClick: () => onChange(!checked),
14163
14346
  className: cn(
14164
14347
  "relative h-5 w-9 shrink-0 rounded-full transition-colors",
14165
- checked ? "bg-primary" : "bg-primary-50",
14348
+ checked ? "bg-bridge-primary" : "bg-primary-50",
14166
14349
  disabled ? "cursor-default opacity-50" : "cursor-pointer"
14167
14350
  ),
14168
14351
  children: /* @__PURE__ */ jsx28(
@@ -14170,7 +14353,7 @@ function DisplaySwitch({
14170
14353
  {
14171
14354
  className: cn(
14172
14355
  "absolute top-0.5 h-4 w-4 rounded-full bg-white transition-all",
14173
- checked ? "left-[1.125rem]" : "left-0.5"
14356
+ checked ? "left-4.5" : "left-0.5"
14174
14357
  )
14175
14358
  }
14176
14359
  )
@@ -15805,7 +15988,7 @@ var badgeVariants = cva(
15805
15988
  {
15806
15989
  variants: {
15807
15990
  variant: {
15808
- default: "border-transparent bg-primary text-primary-foreground",
15991
+ default: "border-transparent bg-bridge-primary text-primary-foreground",
15809
15992
  secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
15810
15993
  destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
15811
15994
  outline: "text-foreground"
@@ -15987,21 +16170,10 @@ function parseSchedulingInsertAfter(insertAfter) {
15987
16170
  insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
15988
16171
  };
15989
16172
  }
15990
- function resolveSchedulingInsert(insertAfter, explicitInsertBefore) {
15991
- const parsed = parseSchedulingInsertAfter(insertAfter);
15992
- const insertBefore = explicitInsertBefore ?? parsed.insertBefore;
15993
- const effectiveInsertAfter = insertBefore && parsed.insertBefore === null ? `${insertAfter}${INSERT_BEFORE_MARKER}${insertBefore}` : insertAfter;
15994
- return { effectiveInsertAfter, insertBefore };
15995
- }
15996
- function getSchedulingMountPoint(insertAfter) {
15997
- const { anchor } = parseSchedulingInsertAfter(insertAfter);
15998
- let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
15999
- if (!anchorEl && anchor === "scheduling") {
16000
- const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
16001
- anchorEl = widgets.at(-1) ?? null;
16002
- }
16003
- if (!anchorEl) return null;
16004
- return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
16173
+ function resolveEntryAnchor(entry) {
16174
+ if (entry.anchorId !== void 0) return { anchorId: entry.anchorId, beforeId: entry.beforeId ?? null };
16175
+ const parsed = parseSchedulingInsertAfter(entry.insertAfter);
16176
+ return { anchorId: parsed.anchor, beforeId: parsed.insertBefore };
16005
16177
  }
16006
16178
  function schedulingMountDepth(insertAfter) {
16007
16179
  if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
@@ -16018,8 +16190,7 @@ function getPageSchedulingEntries(raw) {
16018
16190
  }
16019
16191
  }
16020
16192
  function isSchedulingWidgetMissing(entry) {
16021
- const { effectiveInsertAfter } = resolveSchedulingInsert(entry.insertAfter);
16022
- return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
16193
+ return !document.querySelector(`[data-ohw-section="${schedulingSectionId(entry.insertAfter)}"]`);
16023
16194
  }
16024
16195
  function hasMissingSchedulingWidgets(entries) {
16025
16196
  return entries.some(isSchedulingWidgetMissing);
@@ -16057,17 +16228,18 @@ function initSectionsFromContent(content, removeExisting = false, currentPath =
16057
16228
  } catch {
16058
16229
  }
16059
16230
  }
16060
- function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId, explicitInsertBefore) {
16061
- const { effectiveInsertAfter, insertBefore } = resolveSchedulingInsert(insertAfter, explicitInsertBefore);
16062
- const sectionId = schedulingSectionId(effectiveInsertAfter);
16231
+ function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, beforeId, existingWidgetId) {
16232
+ const widgetId = existingWidgetId ?? (beforeId ? `${anchorId}${INSERT_BEFORE_MARKER}${beforeId}` : anchorId);
16233
+ const sectionId = schedulingSectionId(widgetId);
16063
16234
  if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
16064
- const mountPoint = getSchedulingMountPoint(effectiveInsertAfter);
16065
- if (!mountPoint) return false;
16235
+ const anchorEl = document.querySelector(`[data-ohw-section="${anchorId}"]`);
16236
+ if (!anchorEl) return false;
16237
+ const mountPoint = anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
16066
16238
  const container = document.createElement("div");
16067
16239
  container.dataset.ohwSectionContainer = "scheduling";
16068
16240
  container.dataset.ohwInstance = sectionId;
16069
- if (insertBefore) {
16070
- const beforeAnchor = document.querySelector(`[data-ohw-section="${insertBefore}"]`);
16241
+ if (beforeId) {
16242
+ const beforeAnchor = document.querySelector(`[data-ohw-section="${beforeId}"]`);
16071
16243
  const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
16072
16244
  if (!beforePoint) return false;
16073
16245
  beforePoint.insertAdjacentElement("beforebegin", container);
@@ -16078,20 +16250,26 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
16078
16250
  }
16079
16251
  tail.insertAdjacentElement("afterend", container);
16080
16252
  }
16081
- const root = createRoot2(container);
16082
- schedulingRoots.set(container, root);
16083
- flushSync2(() => {
16084
- root.render(
16085
- /* @__PURE__ */ jsx33(
16086
- SchedulingWidget,
16087
- {
16088
- notifyOnConnect,
16089
- initialScheduleId: scheduleId,
16090
- insertAfter: effectiveInsertAfter
16091
- }
16092
- )
16093
- );
16094
- });
16253
+ try {
16254
+ const root = createRoot2(container);
16255
+ schedulingRoots.set(container, root);
16256
+ flushSync2(() => {
16257
+ root.render(
16258
+ /* @__PURE__ */ jsx33(
16259
+ SchedulingWidget,
16260
+ {
16261
+ notifyOnConnect,
16262
+ initialScheduleId: scheduleId,
16263
+ insertAfter: widgetId
16264
+ }
16265
+ )
16266
+ );
16267
+ });
16268
+ } catch (err) {
16269
+ console.error("[ow:scheduling] render threw", err);
16270
+ container.remove();
16271
+ return false;
16272
+ }
16095
16273
  const tracker = getSectionsTracker();
16096
16274
  let sections = [];
16097
16275
  try {
@@ -16099,10 +16277,12 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
16099
16277
  } catch {
16100
16278
  }
16101
16279
  const inEditor = typeof window !== "undefined" && window.self !== window.top;
16102
- if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === effectiveInsertAfter)) {
16280
+ if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === widgetId)) {
16103
16281
  sections.push({
16104
16282
  type: "scheduling",
16105
- insertAfter: effectiveInsertAfter,
16283
+ insertAfter: widgetId,
16284
+ anchorId,
16285
+ beforeId: beforeId ?? null,
16106
16286
  pagePath: window.location.pathname,
16107
16287
  ...scheduleId ? { scheduleId } : {}
16108
16288
  });
@@ -16116,7 +16296,8 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
16116
16296
  for (let i = pending.length - 1; i >= 0; i--) {
16117
16297
  const entry = pending[i];
16118
16298
  const shouldNotify = typeof notifyOnConnect === "function" ? notifyOnConnect(entry) : notifyOnConnect;
16119
- if (mountSchedulingWidget(entry.insertAfter, shouldNotify, entry.scheduleId ?? null)) {
16299
+ const { anchorId, beforeId } = resolveEntryAnchor(entry);
16300
+ if (mountSchedulingWidget(anchorId, shouldNotify, entry.scheduleId ?? null, beforeId, entry.insertAfter)) {
16120
16301
  pending.splice(i, 1);
16121
16302
  }
16122
16303
  }
@@ -16280,6 +16461,11 @@ function applyLinkByKey(key, val) {
16280
16461
  hrefAnchors.forEach((el) => applyLinkHref(el, val));
16281
16462
  }
16282
16463
  }
16464
+ function isInsideLinkEditor(target) {
16465
+ return Boolean(
16466
+ target.closest("[data-ohw-link-popover-root]") || target.closest("[data-ohw-link-modal-root]") || target.closest("[data-ohw-link-page-dropdown]") || target.closest("[data-ohw-section-picker]") || target.closest("[data-ohw-navbar-container-chrome]") || target.closest("[data-ohw-navbar-add-button]") || target.closest("[data-ohw-footer-container-chrome]") || target.closest("[data-ohw-footer-add-button]") || target.closest("[data-ohw-more-menu]") || target.closest('[data-slot="dropdown-menu-content"]') || target.closest('[data-slot="popover-content"]') || target.closest('[data-slot="dialog-content"]') || target.closest('[data-slot="dialog-overlay"]')
16467
+ );
16468
+ }
16283
16469
  function isInsideFloatingPanel(target) {
16284
16470
  return Boolean(target.closest("[data-ohw-floating-panel]"));
16285
16471
  }
@@ -16287,11 +16473,6 @@ function isPointOverFloatingPanel(clientX, clientY) {
16287
16473
  const el = document.elementFromPoint(clientX, clientY);
16288
16474
  return Boolean(el instanceof Element && el.closest("[data-ohw-floating-panel]"));
16289
16475
  }
16290
- function isInsideLinkEditor(target) {
16291
- return Boolean(
16292
- target.closest("[data-ohw-link-popover-root]") || target.closest("[data-ohw-link-modal-root]") || target.closest("[data-ohw-link-page-dropdown]") || target.closest("[data-ohw-section-picker]") || target.closest("[data-ohw-navbar-container-chrome]") || target.closest("[data-ohw-navbar-add-button]") || target.closest("[data-ohw-footer-container-chrome]") || target.closest("[data-ohw-footer-add-button]") || target.closest("[data-ohw-more-menu]") || target.closest('[data-slot="dropdown-menu-content"]') || target.closest('[data-slot="popover-content"]') || target.closest('[data-slot="dialog-content"]') || target.closest('[data-slot="dialog-overlay"]')
16293
- );
16294
- }
16295
16476
  function getHrefKeyFromElement(el) {
16296
16477
  if (!el) return null;
16297
16478
  const anchor = el.closest("[data-ohw-href-key]");
@@ -16560,7 +16741,7 @@ function getNavigationSelectionParent(el) {
16560
16741
  if (!isFooterLinksContainer(el) && (el.hasAttribute("data-ohw-footer-col") || el.hasAttribute("data-ohw-footer-column") || isInferredFooterGroup2(el))) {
16561
16742
  return getFooterLinksContainer();
16562
16743
  }
16563
- if (el.hasAttribute("data-ohw-nav-container") || el.hasAttribute("data-ohw-nav-drawer") || isFooterLinksContainer(el)) {
16744
+ 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)) {
16564
16745
  return getNavigationRoot(el);
16565
16746
  }
16566
16747
  return null;
@@ -16776,7 +16957,6 @@ var ICONS = {
16776
16957
  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"/>',
16777
16958
  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"/>'
16778
16959
  };
16779
- var HEIGHT_SETTLE_DELAYS = [150, 400, 800];
16780
16960
  var SELECTION_CHROME_GAP2 = 4;
16781
16961
  var TOOLBAR_STROKE_GAP2 = 4;
16782
16962
  var TOOLBAR_OFFSET_FROM_ELEMENT = SELECTION_CHROME_GAP2 + TOOLBAR_STROKE_GAP2;
@@ -17160,6 +17340,7 @@ function StateToggle({
17160
17340
  );
17161
17341
  }
17162
17342
  var contentCache = /* @__PURE__ */ new Map();
17343
+ var fetchedContentPaths = /* @__PURE__ */ new Set();
17163
17344
  var brandingCache = /* @__PURE__ */ new Map();
17164
17345
  var OHW_LOADER_STYLE = {
17165
17346
  position: "fixed",
@@ -17708,13 +17889,6 @@ function OhhwellsBridge() {
17708
17889
  const [isItemDragging, setIsItemDragging] = useState14(false);
17709
17890
  const [isFooterFrameSelection, setIsFooterFrameSelection] = useState14(false);
17710
17891
  isFooterFrameSelectionRef.current = isFooterFrameSelection;
17711
- const [floatingPanel, setFloatingPanel] = useState14(null);
17712
- const floatingPanelOpenRef = useRef10(false);
17713
- floatingPanelOpenRef.current = floatingPanel !== null;
17714
- const [floatingPanelPos, setFloatingPanelPos] = useState14(null);
17715
- const [logoSizeDraft, setLogoSizeDraft] = useState14(null);
17716
- const [editorViewport, setEditorViewport] = useState14("desktop");
17717
- const [parentScrollSnap, setParentScrollSnap] = useState14(null);
17718
17892
  const [navDropdownPreviewOpen, setNavDropdownPreviewOpen] = useState14(null);
17719
17893
  const [footerHeadingVisible, setFooterHeadingVisible] = useState14(null);
17720
17894
  const footerDragRef = useRef10(null);
@@ -17731,7 +17905,15 @@ function OhhwellsBridge() {
17731
17905
  const aiSectionsRef = useRef10("");
17732
17906
  const brandKitRef = useRef10("");
17733
17907
  const stylesRef = useRef10("");
17908
+ const appliedDrawerRef = useRef10(null);
17734
17909
  const pendingDeleteUndoRef = useRef10(null);
17910
+ const [floatingPanel, setFloatingPanel] = useState14(null);
17911
+ const floatingPanelOpenRef = useRef10(false);
17912
+ const setFloatingPanelRef = useRef10(setFloatingPanel);
17913
+ const [floatingPanelPos, setFloatingPanelPos] = useState14(null);
17914
+ const [logoSizeDraft, setLogoSizeDraft] = useState14(null);
17915
+ const [editorViewport, setEditorViewport] = useState14("desktop");
17916
+ const [parentScrollSnap, setParentScrollSnap] = useState14(null);
17735
17917
  const [sitePages, setSitePages] = useState14([]);
17736
17918
  const [sectionsByPath, setSectionsByPath] = useState14({});
17737
17919
  const sectionsPrefetchGenRef = useRef10(0);
@@ -17740,7 +17922,18 @@ function OhhwellsBridge() {
17740
17922
  const linkPopoverOpenRef = useRef10(false);
17741
17923
  const linkPopoverGraceUntilRef = useRef10(0);
17742
17924
  setLinkPopoverRef.current = setLinkPopover;
17925
+ setFloatingPanelRef.current = setFloatingPanel;
17743
17926
  linkPopoverSessionRef.current = linkPopover;
17927
+ floatingPanelOpenRef.current = Boolean(floatingPanel);
17928
+ useEffect13(() => {
17929
+ const syncViewport = () => {
17930
+ const next = window.innerWidth <= 480 ? "mobile" : "desktop";
17931
+ setEditorViewport((prev) => prev === next ? prev : next);
17932
+ };
17933
+ syncViewport();
17934
+ window.addEventListener("resize", syncViewport);
17935
+ return () => window.removeEventListener("resize", syncViewport);
17936
+ }, []);
17744
17937
  const {
17745
17938
  navDragRef,
17746
17939
  navPointerDragRef,
@@ -19076,6 +19269,7 @@ function OhhwellsBridge() {
19076
19269
  }
19077
19270
  if (typeof content[STYLE_STORE_KEY] === "string") {
19078
19271
  stylesRef.current = content[STYLE_STORE_KEY];
19272
+ document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
19079
19273
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
19080
19274
  }
19081
19275
  applyBrandChrome(content);
@@ -19083,11 +19277,11 @@ function OhhwellsBridge() {
19083
19277
  for (const [key, val] of Object.entries(content)) {
19084
19278
  if (key === "__ohw_sections") continue;
19085
19279
  if (key === AI_SECTIONS_KEY) continue;
19280
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
19281
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
19086
19282
  if (key === BRAND_KIT_KEY) continue;
19087
19283
  if (key === STYLE_STORE_KEY) continue;
19088
19284
  if (BRAND_CHROME_KEYS.has(key)) continue;
19089
- if (key === LOGO_PLACEHOLDER_KEY) continue;
19090
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
19091
19285
  if (applyVideoSettingNode(key, val)) continue;
19092
19286
  if (applyCarouselNode(key, val)) continue;
19093
19287
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -19158,7 +19352,9 @@ function OhhwellsBridge() {
19158
19352
  let cancelled = false;
19159
19353
  setFetchState("loading");
19160
19354
  const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
19161
- fetch(`${apiUrl}/api/public/sites/${subdomain}/content`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
19355
+ const initialPath = pathname;
19356
+ fetchedContentPaths.add(`${subdomain}::${initialPath}`);
19357
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
19162
19358
  if (cancelled) return;
19163
19359
  const content = data?.content ?? {};
19164
19360
  const branding = Boolean(data?.showBranding);
@@ -19267,7 +19463,9 @@ function OhhwellsBridge() {
19267
19463
  }, [isEditMode]);
19268
19464
  useEffect13(() => {
19269
19465
  if (isEditMode || fetchState !== "done") return;
19466
+ console.log("env", process.env.NEXT_PUBLIC_FLOWOPS_API_URL);
19270
19467
  const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
19468
+ console.log({ apiUrl, subdomain });
19271
19469
  bindPublishedForms(apiUrl, subdomain, contentCache.get(subdomain) ?? {});
19272
19470
  }, [isEditMode, fetchState, subdomain]);
19273
19471
  useEffect13(() => {
@@ -19292,16 +19490,17 @@ function OhhwellsBridge() {
19292
19490
  applyAiSectionsToDom(parseAiSectionsState(content[AI_SECTIONS_KEY]));
19293
19491
  }
19294
19492
  if (typeof content[STYLE_STORE_KEY] === "string") {
19493
+ document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
19295
19494
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
19296
19495
  }
19297
19496
  for (const [key, val] of Object.entries(content)) {
19298
19497
  if (key === "__ohw_sections") continue;
19299
19498
  if (key === AI_SECTIONS_KEY) continue;
19499
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
19500
+ if (LOGO_IMAGE_KEYS.includes(key)) continue;
19300
19501
  if (key === BRAND_KIT_KEY) continue;
19301
19502
  if (key === STYLE_STORE_KEY) continue;
19302
19503
  if (BRAND_CHROME_KEYS.has(key)) continue;
19303
- if (key === LOGO_PLACEHOLDER_KEY) continue;
19304
- if (LOGO_IMAGE_KEYS.includes(key)) continue;
19305
19504
  if (applyVideoSettingNode(key, val)) continue;
19306
19505
  if (applyCarouselNode(key, val)) continue;
19307
19506
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -19352,6 +19551,17 @@ function OhhwellsBridge() {
19352
19551
  debounceTimer = setTimeout(applyFromCache, 150);
19353
19552
  };
19354
19553
  applyFromCache();
19554
+ const pathCacheKey = `${subdomain}::${pathname}`;
19555
+ if (!fetchedContentPaths.has(pathCacheKey)) {
19556
+ fetchedContentPaths.add(pathCacheKey);
19557
+ const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
19558
+ fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(pathname)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
19559
+ if (!data?.content) return;
19560
+ contentCache.set(subdomain, data.content);
19561
+ applyFromCache();
19562
+ }).catch(() => {
19563
+ });
19564
+ }
19355
19565
  observer = new MutationObserver(scheduleApply);
19356
19566
  observer.observe(document.body, { childList: true, subtree: true });
19357
19567
  return () => {
@@ -19396,6 +19606,17 @@ function OhhwellsBridge() {
19396
19606
  if (!isEditMode) return;
19397
19607
  initSectionInstancesFromContent(editContentRef.current, pathname);
19398
19608
  }, [pathname, isEditMode]);
19609
+ useEffect13(() => {
19610
+ if (!isEditMode) return;
19611
+ const observer = new MutationObserver(() => {
19612
+ const drawer = document.querySelector("[data-ohw-nav-drawer]");
19613
+ if (!drawer || drawer === appliedDrawerRef.current) return;
19614
+ appliedDrawerRef.current = drawer;
19615
+ applyStylesToDom(parseStyleStore(stylesRef.current));
19616
+ });
19617
+ observer.observe(document.body, { childList: true, subtree: true });
19618
+ return () => observer.disconnect();
19619
+ }, [isEditMode, pathname]);
19399
19620
  useEffect13(() => {
19400
19621
  const contentForNav = () => {
19401
19622
  if (isEditMode) return editContentRef.current;
@@ -19487,26 +19708,11 @@ function OhhwellsBridge() {
19487
19708
  const t2 = setTimeout(measure, 500);
19488
19709
  const ro = new ResizeObserver(schedule);
19489
19710
  ro.observe(document.body);
19490
- let lastWidth = window.innerWidth;
19491
- let resizeTimers = [];
19492
- const clearResizeTimers = () => {
19493
- resizeTimers.forEach(clearTimeout);
19494
- resizeTimers = [];
19495
- };
19496
- const handleResize = () => {
19497
- if (window.innerWidth === lastWidth) return;
19498
- lastWidth = window.innerWidth;
19499
- clearResizeTimers();
19500
- resizeTimers = HEIGHT_SETTLE_DELAYS.map((delay) => setTimeout(measure, delay));
19501
- };
19502
- window.addEventListener("resize", handleResize);
19503
19711
  return () => {
19504
19712
  clearTimeout(t1);
19505
19713
  clearTimeout(t2);
19506
19714
  if (raf != null) cancelAnimationFrame(raf);
19507
19715
  ro.disconnect();
19508
- clearResizeTimers();
19509
- window.removeEventListener("resize", handleResize);
19510
19716
  };
19511
19717
  }, [pathname, isEditMode, postToParent2]);
19512
19718
  useEffect13(() => {
@@ -19689,7 +19895,10 @@ function OhhwellsBridge() {
19689
19895
  flex-wrap: wrap !important;
19690
19896
  align-items: flex-end !important;
19691
19897
  }
19692
- [data-ohw-editable="form"] [data-ohw-form-field] {
19898
+ /* Generated forms are excluded: they are always a single column, so the 12rem
19899
+ flex BASIS \u2014 a minimum width in a horizontal row \u2014 reads as a 12rem HEIGHT
19900
+ there and inflates every field to the tallest one's size. */
19901
+ [data-ohw-editable="form"]:not([data-ai-form]) [data-ohw-form-field] {
19693
19902
  flex: 1 1 12rem !important;
19694
19903
  min-width: min(12rem, 100%) !important;
19695
19904
  max-width: 100% !important;
@@ -19798,9 +20007,6 @@ function OhhwellsBridge() {
19798
20007
  if (target.closest("[data-ohw-state-toggle]")) return;
19799
20008
  if (target.closest("[data-ohw-max-badge]")) return;
19800
20009
  if (isInsideLinkEditor(target)) return;
19801
- if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
19802
- clearMediaSelectionRef.current();
19803
- }
19804
20010
  if (isInsideFloatingPanel(target)) return;
19805
20011
  if (target.closest("[data-ohw-form-toolbar]")) return;
19806
20012
  if (target.closest(
@@ -19808,6 +20014,9 @@ function OhhwellsBridge() {
19808
20014
  )) {
19809
20015
  return;
19810
20016
  }
20017
+ if (selectedMediaElRef.current && !selectedMediaElRef.current.contains(target) && !target.closest("[data-ohw-media-overlay], [data-ohw-edit-chrome], [data-ohw-item-interaction]")) {
20018
+ clearMediaSelectionRef.current();
20019
+ }
19811
20020
  {
19812
20021
  const formEl = getFormElement(target);
19813
20022
  const onSuccessText = target.closest(`[${SUCCESS_TEXT_ATTR}]`);
@@ -19971,14 +20180,6 @@ function OhhwellsBridge() {
19971
20180
  }
19972
20181
  const clickedButton = findClosestButtonLike(target);
19973
20182
  const buttonOnMedia = Boolean(clickedButton && isMediaEditable(editable) && editable.contains(clickedButton));
19974
- console.log("[click-debug]", {
19975
- editableType: editable.dataset.ohwEditable,
19976
- editableTag: editable.tagName,
19977
- targetTag: target.tagName,
19978
- clickedButtonTag: clickedButton?.tagName ?? null,
19979
- buttonOnMedia,
19980
- isMediaEditableEditable: isMediaEditable(editable)
19981
- });
19982
20183
  if (isMediaEditable(editable) && !buttonOnMedia) {
19983
20184
  e.preventDefault();
19984
20185
  e.stopPropagation();
@@ -20005,11 +20206,6 @@ function OhhwellsBridge() {
20005
20206
  const hrefLookupTarget = buttonOnMedia ? clickedButton : editable;
20006
20207
  const hrefCtx = getHrefKeyFromElement(hrefLookupTarget);
20007
20208
  const navAnchor = hrefCtx ? getNavigationItemAnchor(hrefCtx.anchor) : null;
20008
- console.log("[click-debug 2]", {
20009
- hrefLookupTargetTag: hrefLookupTarget.tagName,
20010
- hrefCtx: hrefCtx ? { key: hrefCtx.key } : null,
20011
- navAnchorTag: navAnchor?.tagName ?? null
20012
- });
20013
20209
  if (navAnchor) {
20014
20210
  e.preventDefault();
20015
20211
  e.stopPropagation();
@@ -20179,6 +20375,9 @@ function OhhwellsBridge() {
20179
20375
  setHoveredItemRect(null);
20180
20376
  hoveredNavContainerRef.current = null;
20181
20377
  setHoveredNavContainerRect(null);
20378
+ siblingHintElRef.current = null;
20379
+ setSiblingHintRect(null);
20380
+ setSiblingHintRects([]);
20182
20381
  return;
20183
20382
  }
20184
20383
  {
@@ -20299,7 +20498,6 @@ function OhhwellsBridge() {
20299
20498
  hoveredNavContainerRef.current = null;
20300
20499
  setHoveredNavContainerRect(null);
20301
20500
  hoveredItemElRef.current = editable;
20302
- setHoveredItemRect(isSelectedForHoverRef.current(editable) ? null : editable.getBoundingClientRect());
20303
20501
  }
20304
20502
  }
20305
20503
  }
@@ -20596,7 +20794,7 @@ function OhhwellsBridge() {
20596
20794
  }
20597
20795
  };
20598
20796
  const probeImageAt = (clientX, clientY, isDragOver = false, fromParentViewport = false) => {
20599
- if (linkPopoverOpenRef.current) {
20797
+ if (linkPopoverOpenRef.current || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
20600
20798
  if (hoveredImageRef.current) {
20601
20799
  hoveredImageRef.current = null;
20602
20800
  hoveredImageHasTextOverlapRef.current = false;
@@ -20969,8 +21167,7 @@ function OhhwellsBridge() {
20969
21167
  };
20970
21168
  const handleMouseMove = (e) => {
20971
21169
  const { clientX, clientY } = e;
20972
- if (pointOwnedByFloatingPanel(clientX, clientY)) return;
20973
- if (isOverEditorChrome(clientX, clientY)) {
21170
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY) || isOverEditorChrome(clientX, clientY)) {
20974
21171
  document.querySelectorAll("[data-ohw-hovered]").forEach((el) => el.removeAttribute("data-ohw-hovered"));
20975
21172
  formHoverElRef.current = null;
20976
21173
  setFormHoverRect(null);
@@ -20978,7 +21175,12 @@ function OhhwellsBridge() {
20978
21175
  setHoveredItemRect(null);
20979
21176
  hoveredNavContainerRef.current = null;
20980
21177
  setHoveredNavContainerRect(null);
21178
+ siblingHintElRef.current = null;
21179
+ setSiblingHintRect(null);
21180
+ setSiblingHintRects([]);
20981
21181
  dismissImageHover();
21182
+ clearImageHover();
21183
+ setSectionGap(null);
20982
21184
  return;
20983
21185
  }
20984
21186
  if (probeSocialsRowAt(clientX, clientY)) return;
@@ -20990,7 +21192,11 @@ function OhhwellsBridge() {
20990
21192
  if (e.data?.type !== "ow:pointer-sync") return;
20991
21193
  const { clientX, clientY } = e.data;
20992
21194
  if (typeof clientX !== "number" || typeof clientY !== "number") return;
20993
- if (pointOwnedByFloatingPanel(clientX, clientY)) return;
21195
+ if (document.documentElement.hasAttribute("data-ohw-panel-dragging") || floatingPanelOpenRef.current && isPointOverFloatingPanel(clientX, clientY)) {
21196
+ dismissImageHover();
21197
+ clearImageHover();
21198
+ return;
21199
+ }
20994
21200
  if (probeSocialsRowAt(clientX, clientY)) return;
20995
21201
  probeSectionGapAt(clientX, clientY);
20996
21202
  probeImageAt(clientX, clientY);
@@ -21329,6 +21535,7 @@ function OhhwellsBridge() {
21329
21535
  }
21330
21536
  if (typeof content[STYLE_STORE_KEY] === "string") {
21331
21537
  stylesRef.current = content[STYLE_STORE_KEY];
21538
+ document.querySelectorAll('[data-ohw-editable="form"]').forEach((f) => markFormFields(f));
21332
21539
  applyStylesToDom(parseStyleStore(content[STYLE_STORE_KEY]));
21333
21540
  }
21334
21541
  applyBrandChrome(content);
@@ -21340,11 +21547,11 @@ function OhhwellsBridge() {
21340
21547
  continue;
21341
21548
  }
21342
21549
  if (key === AI_SECTIONS_KEY) continue;
21550
+ if (key === LOGO_PLACEHOLDER_KEY) continue;
21551
+ if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
21343
21552
  if (key === BRAND_KIT_KEY) continue;
21344
21553
  if (key === STYLE_STORE_KEY) continue;
21345
21554
  if (BRAND_CHROME_KEYS.has(key)) continue;
21346
- if (key === LOGO_PLACEHOLDER_KEY) continue;
21347
- if (LOGO_IMAGE_KEYS.includes(key) && !String(val ?? "").trim()) continue;
21348
21555
  if (applyVideoSettingNode(key, val)) continue;
21349
21556
  if (applyCarouselNode(key, val)) continue;
21350
21557
  document.querySelectorAll(`[data-ohw-key="${key}"]`).forEach((el) => {
@@ -21569,10 +21776,26 @@ function OhhwellsBridge() {
21569
21776
  const handleGetBrand = (e) => {
21570
21777
  if (e.data?.type !== "ow:get-brand") return;
21571
21778
  const template = deriveTemplateBrand();
21572
- const value = brandKitRef.current || (template ? JSON.stringify(template) : "");
21779
+ const fallback = template ?? (() => {
21780
+ const fonts = deriveTemplateFonts();
21781
+ return fonts ? { palette: AI_DEFAULT_BRAND.palette, fonts } : null;
21782
+ })();
21783
+ const value = brandKitRef.current || (fallback ? JSON.stringify(fallback) : "");
21573
21784
  postToParentRef.current({ type: "ow:brand-value", value });
21574
21785
  };
21575
21786
  window.addEventListener("message", handleGetBrand);
21787
+ const handleGetTemplateFonts = (e) => {
21788
+ if (e.data?.type !== "ow:get-template-fonts") return;
21789
+ const fonts = deriveTemplateFonts();
21790
+ postToParentRef.current({ type: "ow:template-fonts-value", value: fonts ? JSON.stringify(fonts) : "" });
21791
+ };
21792
+ window.addEventListener("message", handleGetTemplateFonts);
21793
+ const handleGetTemplateBrand = (e) => {
21794
+ if (e.data?.type !== "ow:get-template-brand") return;
21795
+ const brand = deriveTemplateBrand();
21796
+ postToParentRef.current({ type: "ow:template-brand-value", value: brand ? JSON.stringify(brand) : "" });
21797
+ };
21798
+ window.addEventListener("message", handleGetTemplateBrand);
21576
21799
  const handleMoveSection = (e) => {
21577
21800
  if (e.data?.type !== "ow:move-section") return;
21578
21801
  const instanceId = typeof e.data.instanceId === "string" ? e.data.instanceId : "";
@@ -21765,6 +21988,12 @@ function OhhwellsBridge() {
21765
21988
  closeLinkPopoverRef.current();
21766
21989
  return;
21767
21990
  }
21991
+ if (floatingPanelOpenRef.current) {
21992
+ setFloatingPanelRef.current(null);
21993
+ deselectRef.current();
21994
+ deactivateRef.current();
21995
+ return;
21996
+ }
21768
21997
  deselectRef.current();
21769
21998
  deactivateRef.current();
21770
21999
  clearMediaSelectionRef.current();
@@ -22053,8 +22282,12 @@ function OhhwellsBridge() {
22053
22282
  if (inserted) {
22054
22283
  const tracker = getSectionsTracker();
22055
22284
  postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
22056
- const h = document.body.scrollHeight;
22057
- if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
22285
+ const reportHeight = () => {
22286
+ const h = document.body.scrollHeight;
22287
+ if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
22288
+ };
22289
+ reportHeight();
22290
+ setTimeout(reportHeight, 500);
22058
22291
  }
22059
22292
  };
22060
22293
  const handleSwitchSchedule = (e) => {
@@ -22475,15 +22708,17 @@ function OhhwellsBridge() {
22475
22708
  window.removeEventListener("message", handleAiSetBrand);
22476
22709
  window.removeEventListener("message", handleAiSetStyles);
22477
22710
  window.removeEventListener("message", handleGetBrand);
22711
+ window.removeEventListener("message", handleGetTemplateFonts);
22712
+ window.removeEventListener("message", handleGetTemplateBrand);
22478
22713
  window.removeEventListener("message", handleMoveSection);
22479
22714
  window.removeEventListener("message", handlePanelDragging);
22480
22715
  window.removeEventListener("message", handleDeleteSection);
22481
22716
  window.removeEventListener("message", handleDuplicateSection);
22482
22717
  window.removeEventListener("message", handleDeactivate);
22483
- document.documentElement.removeAttribute("data-ohw-panel-dragging");
22484
22718
  window.removeEventListener("message", handleToastAction);
22485
22719
  window.removeEventListener("message", handleFormCount);
22486
22720
  window.removeEventListener("message", handleUiEscape);
22721
+ document.documentElement.removeAttribute("data-ohw-panel-dragging");
22487
22722
  autoSaveTimers.current.forEach(clearTimeout);
22488
22723
  autoSaveTimers.current.clear();
22489
22724
  if (imageUnhoverTimerRef.current) clearTimeout(imageUnhoverTimerRef.current);
@@ -22686,7 +22921,7 @@ function OhhwellsBridge() {
22686
22921
  postToParent2({
22687
22922
  type: "ow:ready",
22688
22923
  version: "1",
22689
- bridgeVersion: "0.1.110",
22924
+ bridgeVersion: "0.1.112",
22690
22925
  path: pathname,
22691
22926
  nodes: collectEditableNodes(editContentRef.current),
22692
22927
  sections
@@ -23302,7 +23537,7 @@ function OhhwellsBridge() {
23302
23537
  "span",
23303
23538
  {
23304
23539
  "data-ohw-form-count": "",
23305
- className: "ml-0.5 inline-flex h-[18px] min-w-[18px] items-center justify-center rounded-full bg-primary px-1.5 text-[11px] font-semibold text-primary-foreground",
23540
+ className: "ml-0.5 inline-flex h-[18px] min-w-[18px] items-center justify-center rounded-full bg-bridge-primary px-1.5 text-[11px] font-semibold text-primary-foreground",
23306
23541
  children: formPickCount
23307
23542
  }
23308
23543
  )
@@ -23540,11 +23775,11 @@ function OhhwellsBridge() {
23540
23775
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
23541
23776
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
23542
23777
  children: [
23543
- /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
23778
+ /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-bridge-primary", style: { height: 3 } }),
23544
23779
  /* @__PURE__ */ jsx33(
23545
23780
  Badge,
23546
23781
  {
23547
- className: "px-8 py-1 bg-primary hover:bg-primary text-primary-foreground text-xs font-medium shrink-0 rounded-full cursor-pointer pointer-events-auto",
23782
+ className: "px-8 py-1 bg-bridge-primary hover:bg-bridge-primary text-primary-foreground text-xs font-medium shrink-0 rounded-full cursor-pointer pointer-events-auto",
23548
23783
  onClick: () => {
23549
23784
  window.parent.postMessage(
23550
23785
  {
@@ -23558,7 +23793,7 @@ function OhhwellsBridge() {
23558
23793
  children: "Add Section"
23559
23794
  }
23560
23795
  ),
23561
- /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-primary", style: { height: 3 } })
23796
+ /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-bridge-primary", style: { height: 3 } })
23562
23797
  ]
23563
23798
  }
23564
23799
  ),
@@ -23606,6 +23841,59 @@ function OhhwellsBridge() {
23606
23841
  ) : null
23607
23842
  ] });
23608
23843
  }
23844
+
23845
+ // src/ui/EmptySection.tsx
23846
+ import Link3 from "next/link";
23847
+ import { Fragment as Fragment9, jsx as jsx34, jsxs as jsxs21 } from "react/jsx-runtime";
23848
+ function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey }) {
23849
+ return /* @__PURE__ */ jsxs21(Fragment9, { children: [
23850
+ /* @__PURE__ */ jsx34(
23851
+ "p",
23852
+ {
23853
+ style: {
23854
+ fontFamily: "var(--brand-font-body)",
23855
+ fontSize: "0.75rem",
23856
+ fontWeight: 500,
23857
+ letterSpacing: "0.15em",
23858
+ textTransform: "uppercase",
23859
+ color: "var(--brand-accent)",
23860
+ marginBottom: "1.5rem"
23861
+ },
23862
+ children: /* @__PURE__ */ jsx34(Link3, { href: homeHref, style: { color: "inherit" }, children: /* @__PURE__ */ jsx34("span", { ...eyebrowKey ? { "data-ohw-editable": "text", "data-ohw-key": eyebrowKey } : {}, children: "Home" }) })
23863
+ }
23864
+ ),
23865
+ /* @__PURE__ */ jsx34(
23866
+ "h1",
23867
+ {
23868
+ style: {
23869
+ fontFamily: "var(--brand-font-heading)",
23870
+ fontSize: "clamp(2rem, 3.5vw, 2.75rem)",
23871
+ lineHeight: 1.1,
23872
+ letterSpacing: "-0.025em",
23873
+ color: "var(--brand-text)",
23874
+ marginBottom: "1rem"
23875
+ },
23876
+ ...titleKey ? { "data-ohw-editable": "text", "data-ohw-key": titleKey, "data-ohw-max-length": 80 } : {},
23877
+ children: title
23878
+ }
23879
+ ),
23880
+ /* @__PURE__ */ jsx34(
23881
+ "p",
23882
+ {
23883
+ style: {
23884
+ fontFamily: "var(--brand-font-body)",
23885
+ fontSize: "1rem",
23886
+ lineHeight: 1.7,
23887
+ fontWeight: 300,
23888
+ color: "var(--brand-text-muted)",
23889
+ maxWidth: "340px"
23890
+ },
23891
+ ...subtitleKey ? { "data-ohw-editable": "text", "data-ohw-key": subtitleKey, "data-ohw-max-length": 160 } : {},
23892
+ children: "This page doesn't have any content yet."
23893
+ }
23894
+ )
23895
+ ] });
23896
+ }
23609
23897
  export {
23610
23898
  AI_DEFAULT_BRAND,
23611
23899
  AI_TREE_SCHEMA_VERSIONS,
@@ -23622,6 +23910,7 @@ export {
23622
23910
  DropdownMenuItem,
23623
23911
  DropdownMenuSeparator,
23624
23912
  DropdownMenuTrigger,
23913
+ EmptySection,
23625
23914
  ItemActionToolbar,
23626
23915
  ItemInteractionLayer,
23627
23916
  LinkEditorPanel,