@ohhwells/bridge 0.1.115 → 0.1.116

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.d.cts CHANGED
@@ -63,6 +63,27 @@ interface AiLayoutTree {
63
63
  align?: 'top' | 'center' | 'bottom' | 'stretch';
64
64
  }>;
65
65
  }
66
+ /** One uploaded weight file for a custom font family. */
67
+ interface AiCustomFontWeight {
68
+ weight: number;
69
+ /** Present when the file is a variable font with a `wght` axis — `weight` is the axis min,
70
+ * `weightEnd` the axis max, together spanning every weight the one file actually covers. */
71
+ weightEnd?: number;
72
+ url: string;
73
+ format: 'woff2' | 'woff' | 'truetype' | 'opentype';
74
+ }
75
+ /**
76
+ * A custom (non-Google) font family, grouped by its uploaded weight files so a bold heading
77
+ * actually resolves to a real bold file instead of the browser synthesizing fake bold from
78
+ * whichever single weight happens to be present.
79
+ */
80
+ interface AiCustomFont {
81
+ /** Collision-proof internal family name — what `fonts.heading`/`fonts.body` actually reference. */
82
+ family: string;
83
+ /** Human-readable name shown in the picker; hidden everywhere the internal `family` is used. */
84
+ label: string;
85
+ weights: AiCustomFontWeight[];
86
+ }
66
87
  /** The site brand kit the renderer styles from (editor Brand settings). */
67
88
  interface AiBrandKit {
68
89
  palette: {
@@ -75,6 +96,7 @@ interface AiBrandKit {
75
96
  heading: string;
76
97
  body: string;
77
98
  };
99
+ customFonts?: AiCustomFont[];
78
100
  }
79
101
  /**
80
102
  * Shape, padding and typography measured off a host-template CTA, so generated buttons read as the
package/dist/index.d.ts CHANGED
@@ -63,6 +63,27 @@ interface AiLayoutTree {
63
63
  align?: 'top' | 'center' | 'bottom' | 'stretch';
64
64
  }>;
65
65
  }
66
+ /** One uploaded weight file for a custom font family. */
67
+ interface AiCustomFontWeight {
68
+ weight: number;
69
+ /** Present when the file is a variable font with a `wght` axis — `weight` is the axis min,
70
+ * `weightEnd` the axis max, together spanning every weight the one file actually covers. */
71
+ weightEnd?: number;
72
+ url: string;
73
+ format: 'woff2' | 'woff' | 'truetype' | 'opentype';
74
+ }
75
+ /**
76
+ * A custom (non-Google) font family, grouped by its uploaded weight files so a bold heading
77
+ * actually resolves to a real bold file instead of the browser synthesizing fake bold from
78
+ * whichever single weight happens to be present.
79
+ */
80
+ interface AiCustomFont {
81
+ /** Collision-proof internal family name — what `fonts.heading`/`fonts.body` actually reference. */
82
+ family: string;
83
+ /** Human-readable name shown in the picker; hidden everywhere the internal `family` is used. */
84
+ label: string;
85
+ weights: AiCustomFontWeight[];
86
+ }
66
87
  /** The site brand kit the renderer styles from (editor Brand settings). */
67
88
  interface AiBrandKit {
68
89
  palette: {
@@ -75,6 +96,7 @@ interface AiBrandKit {
75
96
  heading: string;
76
97
  body: string;
77
98
  };
99
+ customFonts?: AiCustomFont[];
78
100
  }
79
101
  /**
80
102
  * Shape, padding and typography measured off a host-template CTA, so generated buttons read as the
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
@@ -2650,14 +2712,52 @@ function readRootVar(name) {
2650
2712
  if (typeof document === "undefined") return "";
2651
2713
  return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
2652
2714
  }
2715
+ function normalizeColorToHex(value) {
2716
+ if (!value || typeof document === "undefined") return value;
2717
+ const canvas = document.createElement("canvas");
2718
+ canvas.width = 1;
2719
+ canvas.height = 1;
2720
+ const ctx = canvas.getContext("2d");
2721
+ if (!ctx) return value;
2722
+ ctx.fillStyle = value;
2723
+ ctx.fillRect(0, 0, 1, 1);
2724
+ const [r2, g, b] = ctx.getImageData(0, 0, 1, 1).data;
2725
+ const toHex = (n) => n.toString(16).padStart(2, "0");
2726
+ return `#${toHex(r2)}${toHex(g)}${toHex(b)}`;
2727
+ }
2728
+ var GENERIC_FONT_KEYWORDS = /* @__PURE__ */ new Set([
2729
+ "serif",
2730
+ "sans-serif",
2731
+ "monospace",
2732
+ "cursive",
2733
+ "fantasy",
2734
+ "system-ui",
2735
+ "ui-serif",
2736
+ "ui-sans-serif",
2737
+ "ui-monospace",
2738
+ "ui-rounded",
2739
+ "math",
2740
+ "emoji",
2741
+ "fangsong"
2742
+ ]);
2743
+ function primaryFontFamily(stack) {
2744
+ const first = stack.split(",").map((part) => part.trim().replace(/^["']|["']$/g, "")).find((part) => part && !GENERIC_FONT_KEYWORDS.has(part.toLowerCase()));
2745
+ return first ?? "";
2746
+ }
2747
+ function humanizeFontFamily(name) {
2748
+ 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());
2749
+ }
2750
+ function readFontVar(name) {
2751
+ return humanizeFontFamily(primaryFontFamily(readRootVar(name)));
2752
+ }
2653
2753
  function deriveBrandOverride() {
2654
2754
  const dark = readRootVar("--ohw-brand-dark");
2655
2755
  const primary = readRootVar("--ohw-brand-primary");
2656
2756
  const light = readRootVar("--ohw-brand-light");
2657
2757
  if (!dark || !primary || !light) return null;
2658
2758
  const accent = readRootVar("--ohw-brand-accent");
2659
- const heading = readRootVar("--font-heading") || readRootVar("--font-display");
2660
- const body = readRootVar("--font-body");
2759
+ const heading = readFontVar("--font-heading") || readFontVar("--font-display");
2760
+ const body = readFontVar("--font-body");
2661
2761
  return {
2662
2762
  palette: { dark, primary, accent: accent || dark, light },
2663
2763
  fonts: {
@@ -2666,22 +2766,55 @@ function deriveBrandOverride() {
2666
2766
  }
2667
2767
  };
2668
2768
  }
2669
- function deriveTemplateBrand() {
2769
+ function deriveTemplateBrandLive() {
2670
2770
  const primary = readRootVar("--brand-primary") || readRootVar("--color-primary");
2671
2771
  const dark = readRootVar("--brand-text") || readRootVar("--color-dark");
2672
2772
  const light = readRootVar("--brand-background") || readRootVar("--color-light");
2673
2773
  if (!dark || !primary || !light) return null;
2674
2774
  const accent = readRootVar("--brand-accent") || readRootVar("--color-accent");
2675
- const heading = readRootVar("--brand-font-heading") || readRootVar("--font-heading") || readRootVar("--font-display");
2676
- const body = readRootVar("--brand-font-body") || readRootVar("--font-body");
2775
+ const heading = readFontVar("--brand-font-heading") || readFontVar("--font-heading") || readFontVar("--font-display");
2776
+ const body = readFontVar("--brand-font-body") || readFontVar("--font-body");
2677
2777
  return {
2678
- palette: { dark, primary, accent: accent || dark, light },
2778
+ palette: {
2779
+ dark: normalizeColorToHex(dark),
2780
+ primary: normalizeColorToHex(primary),
2781
+ accent: normalizeColorToHex(accent || dark),
2782
+ light: normalizeColorToHex(light)
2783
+ },
2679
2784
  fonts: {
2680
2785
  heading: heading || AI_DEFAULT_BRAND.fonts.heading,
2681
2786
  body: body || AI_DEFAULT_BRAND.fonts.body
2682
2787
  }
2683
2788
  };
2684
2789
  }
2790
+ function deriveTemplateFontsLive() {
2791
+ const heading = readFontVar("--brand-font-heading") || readFontVar("--font-heading") || readFontVar("--font-display");
2792
+ const body = readFontVar("--brand-font-body") || readFontVar("--font-body");
2793
+ if (!heading && !body) return null;
2794
+ return {
2795
+ heading: heading || AI_DEFAULT_BRAND.fonts.heading,
2796
+ body: body || AI_DEFAULT_BRAND.fonts.body
2797
+ };
2798
+ }
2799
+ var OVERRIDE_VAR_NAMES = [...BRAND_VAR_NAMES, ...LEGACY_BRAND_VAR_NAMES, ...FONT_VARS.heading, ...FONT_VARS.body];
2800
+ function withOverrideStripped(read) {
2801
+ const root = document.documentElement;
2802
+ const restore = OVERRIDE_VAR_NAMES.map((name) => [name, root.style.getPropertyValue(name)]);
2803
+ for (const name of OVERRIDE_VAR_NAMES) root.style.removeProperty(name);
2804
+ try {
2805
+ return read();
2806
+ } finally {
2807
+ for (const [name, value] of restore) if (value) root.style.setProperty(name, value);
2808
+ }
2809
+ }
2810
+ var TEMPLATE_BRAND_SNAPSHOT = typeof document === "undefined" ? null : withOverrideStripped(deriveTemplateBrandLive);
2811
+ var TEMPLATE_FONTS_SNAPSHOT = typeof document === "undefined" ? null : withOverrideStripped(deriveTemplateFontsLive);
2812
+ function deriveTemplateBrand() {
2813
+ return TEMPLATE_BRAND_SNAPSHOT;
2814
+ }
2815
+ function deriveTemplateFonts() {
2816
+ return TEMPLATE_FONTS_SNAPSHOT;
2817
+ }
2685
2818
  function deriveTemplateButtonStyle() {
2686
2819
  if (typeof document === "undefined") return null;
2687
2820
  const candidates = Array.from(
@@ -7179,12 +7312,12 @@ var cva = (base, config) => (props) => {
7179
7312
  import { Toggle as TogglePrimitive } from "radix-ui";
7180
7313
  import { jsx as jsx5 } from "react/jsx-runtime";
7181
7314
  var toggleVariants = cva(
7182
- "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",
7315
+ "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",
7183
7316
  {
7184
7317
  variants: {
7185
7318
  variant: {
7186
7319
  default: "bg-transparent border-0",
7187
- outline: "border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground"
7320
+ outline: "border border-input bg-transparent shadow-xs hover:bg-bridge-accent hover:text-accent-foreground"
7188
7321
  },
7189
7322
  size: {
7190
7323
  default: "px-2 py-1",
@@ -7277,10 +7410,10 @@ var DragHandle = React4.forwardRef(
7277
7410
  type,
7278
7411
  "data-slot": "drag-handle",
7279
7412
  className: cn(
7280
- "inline-flex h-7 w-4 shrink-0 items-center justify-center rounded-md transition-all duration-200 max-h-[30px]",
7413
+ "inline-flex h-7 w-4 shrink-0 items-center justify-center rounded-md transition-all duration-200 max-h-7.5",
7281
7414
  "bg-white border border-transparent text-stone-500 shadow-md cursor-grab",
7282
7415
  "enabled:hover:border enabled:hover:border-stone-200 enabled:hover:text-stone-950",
7283
- "enabled:active:border enabled:active:border-primary enabled:active:bg-primary-50 enabled:active:text-stone-950 enabled:active:shadow enabled:active:cursor-grabbing",
7416
+ "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",
7284
7417
  "disabled:cursor-not-allowed disabled:opacity-40 disabled:text-stone-950 disabled:pointer-events-none",
7285
7418
  className
7286
7419
  ),
@@ -7302,7 +7435,7 @@ var CustomToolbar = React5.forwardRef(({ className, onMouseDown, ...props }, ref
7302
7435
  "data-ohw-toolbar": "",
7303
7436
  className: cn(
7304
7437
  // Figma: bg background, radius 8, gap-1, p-0.5, shadow-md — no border
7305
- "inline-flex h-8 items-center gap-1 rounded-[var(--radius,0.5rem)] bg-background p-0.5 font-sans whitespace-nowrap shadow-md",
7438
+ "inline-flex h-8 items-center gap-1 rounded-(--radius,0.5rem) bg-background p-0.5 font-sans whitespace-nowrap shadow-md",
7306
7439
  className
7307
7440
  ),
7308
7441
  onMouseDown: (e) => {
@@ -7331,7 +7464,7 @@ var CustomToolbarButton = React5.forwardRef(
7331
7464
  type,
7332
7465
  className: cn(
7333
7466
  "inline-flex size-7 shrink-0 items-center justify-center rounded-[calc(var(--radius,0.5rem)-2px)] text-foreground transition-colors",
7334
- active ? "bg-primary text-primary-foreground" : "bg-transparent hover:bg-muted disabled:cursor-not-allowed disabled:text-muted-foreground disabled:opacity-60",
7467
+ active ? "bg-bridge-primary text-primary-foreground" : "bg-transparent hover:bg-muted disabled:cursor-not-allowed disabled:text-muted-foreground disabled:opacity-60",
7335
7468
  className
7336
7469
  ),
7337
7470
  ...props
@@ -8595,13 +8728,13 @@ function FormFieldToolbar({
8595
8728
  ]
8596
8729
  }
8597
8730
  ) }),
8598
- /* @__PURE__ */ jsx13(DropdownMenuContent, { align: "start", sideOffset: 8, className: "min-w-[190px] p-1", children: FIELD_TYPES.map((entry) => {
8731
+ /* @__PURE__ */ jsx13(DropdownMenuContent, { align: "start", sideOffset: 8, className: "min-w-47.5 p-1", children: FIELD_TYPES.map((entry) => {
8599
8732
  const Icon = TYPE_ICONS[entry.type];
8600
8733
  return /* @__PURE__ */ jsxs6(
8601
8734
  DropdownMenuItem,
8602
8735
  {
8603
8736
  onSelect: () => onTypeChange(entry.type),
8604
- className: "rounded-md py-2 text-[13px] " + (entry.type === type ? "bg-primary/10" : ""),
8737
+ className: "rounded-md py-2 text-[13px] " + (entry.type === type ? "bg-bridge-primary/10" : ""),
8605
8738
  children: [
8606
8739
  /* @__PURE__ */ jsx13(Icon, { size: 14, strokeWidth: 1.75, className: "shrink-0", "aria-hidden": true }),
8607
8740
  entry.label
@@ -8618,7 +8751,7 @@ function FormFieldToolbar({
8618
8751
  type: "button",
8619
8752
  "aria-pressed": required,
8620
8753
  onClick: onRequiredToggle,
8621
- 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"),
8754
+ 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"),
8622
8755
  "data-ohw-field-required-toggle": "",
8623
8756
  children: [
8624
8757
  /* @__PURE__ */ jsx13(Asterisk, { size: 14, strokeWidth: 2, "aria-hidden": true }),
@@ -8638,7 +8771,7 @@ function FormFieldToolbar({
8638
8771
  children: /* @__PURE__ */ jsx13(MoreHorizontal2, { size: 15, "aria-hidden": true })
8639
8772
  }
8640
8773
  ) }),
8641
- /* @__PURE__ */ jsxs6(DropdownMenuContent, { align: "start", sideOffset: 8, className: "min-w-[170px] p-1", children: [
8774
+ /* @__PURE__ */ jsxs6(DropdownMenuContent, { align: "start", sideOffset: 8, className: "min-w-42.5 p-1", children: [
8642
8775
  /* @__PURE__ */ jsxs6(DropdownMenuItem, { onSelect: onDuplicate, className: "rounded-md py-2 text-[13px]", children: [
8643
8776
  /* @__PURE__ */ jsx13(Copy2, { size: 14, strokeWidth: 1.75, className: "shrink-0", "aria-hidden": true }),
8644
8777
  "Duplicate"
@@ -8666,7 +8799,7 @@ function FieldTypePicker({ onPick }) {
8666
8799
  {
8667
8800
  type: "button",
8668
8801
  onClick: () => onPick(entry.type),
8669
- 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",
8802
+ 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",
8670
8803
  children: [
8671
8804
  /* @__PURE__ */ jsx13(Icon, { size: 26, strokeWidth: 1.5, "aria-hidden": true }),
8672
8805
  entry.label
@@ -8692,7 +8825,7 @@ var buttonVariants = cva(
8692
8825
  {
8693
8826
  variants: {
8694
8827
  variant: {
8695
- default: "bg-primary text-primary-foreground hover:opacity-90",
8828
+ default: "bg-bridge-primary text-primary-foreground hover:opacity-90",
8696
8829
  outline: "border border-border bg-background text-foreground shadow-sm hover:bg-muted/80",
8697
8830
  ghost: "min-w-0 px-3 py-2 text-foreground hover:bg-muted/50"
8698
8831
  },
@@ -8892,8 +9025,8 @@ function MediaOverlay({
8892
9025
  pointerEvents: hover.hasTextOverlap ? "none" : "auto",
8893
9026
  // Selected: a firm component ring with no wash, so the image reads as chosen rather
8894
9027
  // than hovered. Hover keeps the existing tinted preview.
8895
- boxShadow: selected ? "inset 0 0 0 2px var(--color-primary)" : "inset 0 0 0 1.5px var(--color-primary)",
8896
- background: selected ? "transparent" : "color-mix(in srgb, var(--color-primary) 20%, transparent)"
9028
+ boxShadow: selected ? "inset 0 0 0 2px var(--color-bridge-primary)" : "inset 0 0 0 1.5px var(--color-bridge-primary)",
9029
+ background: selected ? "transparent" : "color-mix(in srgb, var(--color-bridge-primary) 20%, transparent)"
8897
9030
  },
8898
9031
  onClick: () => onSelect && !selected ? onSelect(hover.key) : onReplace(hover.key),
8899
9032
  children: [
@@ -8987,8 +9120,8 @@ function CarouselOverlay({
8987
9120
  height: rect.height,
8988
9121
  zIndex: 2147483646,
8989
9122
  pointerEvents: "auto",
8990
- boxShadow: "inset 0 0 0 1.5px var(--color-primary)",
8991
- background: "color-mix(in srgb, var(--color-primary) 20%, transparent)"
9123
+ boxShadow: "inset 0 0 0 1.5px var(--color-bridge-primary)",
9124
+ background: "color-mix(in srgb, var(--color-bridge-primary) 20%, transparent)"
8992
9125
  },
8993
9126
  onClick: () => onEdit(hover.key),
8994
9127
  children: /* @__PURE__ */ jsxs8(
@@ -9910,7 +10043,7 @@ function SectionTreeItem({
9910
10043
  /* @__PURE__ */ jsx20(
9911
10044
  "div",
9912
10045
  {
9913
- className: "mr-[-1px] h-9 w-2 shrink-0 rounded-bl-sm border-b border-l border-border mb-4",
10046
+ className: "-mr-px h-9 w-2 shrink-0 rounded-bl-sm border-b border-l border-border mb-4",
9914
10047
  "aria-hidden": true
9915
10048
  }
9916
10049
  ),
@@ -9929,7 +10062,7 @@ function SectionTreeItem({
9929
10062
  className: cn(
9930
10063
  "flex h-9 min-w-0 flex-1 items-center gap-2 rounded-md border border-border bg-background p-3",
9931
10064
  interactive && "cursor-pointer hover:bg-muted/30",
9932
- interactive && selected && "border-primary"
10065
+ interactive && selected && "border-bridge-primary"
9933
10066
  ),
9934
10067
  children: [
9935
10068
  /* @__PURE__ */ jsx20(
@@ -10059,7 +10192,7 @@ function UrlOrPageInput({
10059
10192
  };
10060
10193
  const fieldClassName = cn(
10061
10194
  "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]",
10062
- urlError ? "border-destructive shadow-[0_0_0_1px_var(--ohw-destructive)]" : isFocused ? "border-primary shadow-[0_0_0_1px_var(--ohw-primary)]" : "border-input"
10195
+ 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"
10063
10196
  );
10064
10197
  return /* @__PURE__ */ jsxs13("div", { className: "flex w-full flex-col gap-2 p-0", children: [
10065
10198
  /* @__PURE__ */ jsx23(Label, { htmlFor: inputId, className: cn(urlError && "text-destructive"), children: "Destination" }),
@@ -14268,7 +14401,7 @@ function DisplaySwitch({
14268
14401
  onClick: () => onChange(!checked),
14269
14402
  className: cn(
14270
14403
  "relative h-5 w-9 shrink-0 rounded-full transition-colors",
14271
- checked ? "bg-primary" : "bg-primary-50",
14404
+ checked ? "bg-bridge-primary" : "bg-primary-50",
14272
14405
  disabled ? "cursor-default opacity-50" : "cursor-pointer"
14273
14406
  ),
14274
14407
  children: /* @__PURE__ */ jsx28(
@@ -14276,7 +14409,7 @@ function DisplaySwitch({
14276
14409
  {
14277
14410
  className: cn(
14278
14411
  "absolute top-0.5 h-4 w-4 rounded-full bg-white transition-all",
14279
- checked ? "left-[1.125rem]" : "left-0.5"
14412
+ checked ? "left-4.5" : "left-0.5"
14280
14413
  )
14281
14414
  }
14282
14415
  )
@@ -15911,7 +16044,7 @@ var badgeVariants = cva(
15911
16044
  {
15912
16045
  variants: {
15913
16046
  variant: {
15914
- default: "border-transparent bg-primary text-primary-foreground",
16047
+ default: "border-transparent bg-bridge-primary text-primary-foreground",
15915
16048
  secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
15916
16049
  destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
15917
16050
  outline: "text-foreground"
@@ -19381,7 +19514,9 @@ function OhhwellsBridge() {
19381
19514
  }, [isEditMode]);
19382
19515
  useEffect13(() => {
19383
19516
  if (isEditMode || fetchState !== "done") return;
19517
+ console.log("env", process.env.NEXT_PUBLIC_FLOWOPS_API_URL);
19384
19518
  const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
19519
+ console.log({ apiUrl, subdomain });
19385
19520
  bindPublishedForms(apiUrl, subdomain, contentCache.get(subdomain) ?? {});
19386
19521
  }, [isEditMode, fetchState, subdomain]);
19387
19522
  useEffect13(() => {
@@ -21731,10 +21866,26 @@ function OhhwellsBridge() {
21731
21866
  const handleGetBrand = (e) => {
21732
21867
  if (e.data?.type !== "ow:get-brand") return;
21733
21868
  const template = deriveTemplateBrand();
21734
- const value = brandKitRef.current || (template ? JSON.stringify(template) : "");
21869
+ const fallback = template ?? (() => {
21870
+ const fonts = deriveTemplateFonts();
21871
+ return fonts ? { palette: AI_DEFAULT_BRAND.palette, fonts } : null;
21872
+ })();
21873
+ const value = brandKitRef.current || (fallback ? JSON.stringify(fallback) : "");
21735
21874
  postToParentRef.current({ type: "ow:brand-value", value });
21736
21875
  };
21737
21876
  window.addEventListener("message", handleGetBrand);
21877
+ const handleGetTemplateFonts = (e) => {
21878
+ if (e.data?.type !== "ow:get-template-fonts") return;
21879
+ const fonts = deriveTemplateFonts();
21880
+ postToParentRef.current({ type: "ow:template-fonts-value", value: fonts ? JSON.stringify(fonts) : "" });
21881
+ };
21882
+ window.addEventListener("message", handleGetTemplateFonts);
21883
+ const handleGetTemplateBrand = (e) => {
21884
+ if (e.data?.type !== "ow:get-template-brand") return;
21885
+ const brand = deriveTemplateBrand();
21886
+ postToParentRef.current({ type: "ow:template-brand-value", value: brand ? JSON.stringify(brand) : "" });
21887
+ };
21888
+ window.addEventListener("message", handleGetTemplateBrand);
21738
21889
  const handleMoveSection = (e) => {
21739
21890
  if (e.data?.type !== "ow:move-section") return;
21740
21891
  const instanceId = typeof e.data.instanceId === "string" ? e.data.instanceId : "";
@@ -22637,6 +22788,8 @@ function OhhwellsBridge() {
22637
22788
  window.removeEventListener("message", handleAiSetBrand);
22638
22789
  window.removeEventListener("message", handleAiSetStyles);
22639
22790
  window.removeEventListener("message", handleGetBrand);
22791
+ window.removeEventListener("message", handleGetTemplateFonts);
22792
+ window.removeEventListener("message", handleGetTemplateBrand);
22640
22793
  window.removeEventListener("message", handleMoveSection);
22641
22794
  window.removeEventListener("message", handlePanelDragging);
22642
22795
  window.removeEventListener("message", handleDeleteSection);
@@ -22848,7 +23001,7 @@ function OhhwellsBridge() {
22848
23001
  postToParent2({
22849
23002
  type: "ow:ready",
22850
23003
  version: "1",
22851
- bridgeVersion: "0.1.114",
23004
+ bridgeVersion: "0.1.115",
22852
23005
  path: pathname,
22853
23006
  nodes: collectEditableNodes(editContentRef.current),
22854
23007
  sections
@@ -23464,7 +23617,7 @@ function OhhwellsBridge() {
23464
23617
  "span",
23465
23618
  {
23466
23619
  "data-ohw-form-count": "",
23467
- 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",
23620
+ 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",
23468
23621
  children: formPickCount
23469
23622
  }
23470
23623
  )
@@ -23702,11 +23855,11 @@ function OhhwellsBridge() {
23702
23855
  className: "fixed left-0 w-full z-2147483646 flex items-center pointer-events-none",
23703
23856
  style: { top: sectionGap.y, transform: "translateY(-50%)" },
23704
23857
  children: [
23705
- /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-primary", style: { height: 3 } }),
23858
+ /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-bridge-primary", style: { height: 3 } }),
23706
23859
  /* @__PURE__ */ jsx33(
23707
23860
  Badge,
23708
23861
  {
23709
- 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",
23862
+ 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",
23710
23863
  onClick: () => {
23711
23864
  window.parent.postMessage(
23712
23865
  {
@@ -23720,7 +23873,7 @@ function OhhwellsBridge() {
23720
23873
  children: "Add Section"
23721
23874
  }
23722
23875
  ),
23723
- /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-primary", style: { height: 3 } })
23876
+ /* @__PURE__ */ jsx33("div", { className: "flex-1 bg-bridge-primary", style: { height: 3 } })
23724
23877
  ]
23725
23878
  }
23726
23879
  ),