@ohhwells/bridge 0.1.122 → 0.1.123

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -104,6 +104,10 @@ function enforceLinkHrefs() {
104
104
 
105
105
  // src/lib/ai-tree.ts
106
106
  var AI_TREE_SCHEMA_VERSIONS = [1];
107
+ var BUTTON_FILL_TOKENS = ["dark", "primary", "accent", "light"];
108
+ var BUTTON_STYLES = ["solid", "outline", "ghost"];
109
+ var BUTTON_RADII = ["square", "rounded", "pill"];
110
+ var BUTTON_SIZES = ["s", "m", "l"];
107
111
  var AI_DEFAULT_BRAND = {
108
112
  palette: { dark: "#0C0A09", primary: "#0078E5", accent: "#747271", light: "#ECECEB" },
109
113
  fonts: {
@@ -363,6 +367,118 @@ var FONT_VARS = {
363
367
  var BRAND_FONT_LINK_ID = "ohw-brand-fonts";
364
368
  var CUSTOM_FONT_STYLE_ID = "ohw-brand-custom-fonts";
365
369
  var CUSTOM_FONT_FORMATS = /* @__PURE__ */ new Set(["woff2", "woff", "truetype", "opentype"]);
370
+ var BUTTON_RADIUS_PX = {
371
+ square: 0,
372
+ rounded: AI_TREE_TOKENS.radiusButton,
373
+ pill: 999
374
+ };
375
+ var BUTTON_SIZE_SPEC = {
376
+ s: {
377
+ paddingX: `${AI_TREE_TOKENS.spacing4}px`,
378
+ paddingY: `${AI_TREE_TOKENS.spacing2}px`,
379
+ fontSize: "14px"
380
+ },
381
+ m: {
382
+ paddingX: `${AI_TREE_TOKENS.spacing6}px`,
383
+ paddingY: `${AI_TREE_TOKENS.spacing3}px`,
384
+ fontSize: `${AI_TREE_TOKENS.type.button.size}px`
385
+ },
386
+ l: {
387
+ paddingX: `${AI_TREE_TOKENS.spacing8}px`,
388
+ paddingY: `${AI_TREE_TOKENS.spacing4}px`,
389
+ fontSize: "18px"
390
+ }
391
+ };
392
+ var BUTTON_SIZE_SCALE = { s: 0.85, m: 1, l: 1.25 };
393
+ var roundHalf = (n) => Math.round(n * 2) / 2;
394
+ function measureTemplateButton(variant) {
395
+ const root = document.documentElement;
396
+ const el = document.querySelector(`.btn--${variant}, .btn-${variant}`);
397
+ if (!el) return null;
398
+ const sheet = document.getElementById(BUTTON_RULES_STYLE_ID);
399
+ const rules = sheet?.textContent ?? "";
400
+ const saved = BUTTON_VAR_NAMES.map((name) => [name, root.style.getPropertyValue(name)]);
401
+ if (sheet) sheet.textContent = "";
402
+ for (const [name] of saved) root.style.removeProperty(name);
403
+ const cs = getComputedStyle(el);
404
+ const base = {
405
+ fontSize: parseFloat(cs.fontSize),
406
+ top: parseFloat(cs.paddingTop),
407
+ right: parseFloat(cs.paddingRight),
408
+ bottom: parseFloat(cs.paddingBottom),
409
+ left: parseFloat(cs.paddingLeft)
410
+ };
411
+ if (sheet) sheet.textContent = rules;
412
+ for (const [name, value] of saved) if (value) root.style.setProperty(name, value);
413
+ return Object.values(base).every(Number.isFinite) && base.fontSize > 0 ? base : null;
414
+ }
415
+ function scaledButtonSize(size, base) {
416
+ if (!base) return BUTTON_SIZE_SPEC[size];
417
+ const k = BUTTON_SIZE_SCALE[size];
418
+ const px2 = (n) => `${roundHalf(n * k)}px`;
419
+ return {
420
+ // Each side scaled on its own, so a button with uneven padding (say, room for an icon on one
421
+ // side) keeps its shape; the x/y vars are the averages for templates that read those instead.
422
+ padding: `${px2(base.top)} ${px2(base.right)} ${px2(base.bottom)} ${px2(base.left)}`,
423
+ paddingX: px2((base.left + base.right) / 2),
424
+ paddingY: px2((base.top + base.bottom) / 2),
425
+ fontSize: `${roundHalf(base.fontSize * k)}px`
426
+ };
427
+ }
428
+ var BUTTON_VAR_ROLES = ["bg", "text", "border", "radius", "padding-x", "padding-y", "padding", "font-size"];
429
+ var BUTTON_VAR_NAMES = ["primary", "secondary"].flatMap(
430
+ (variant) => BUTTON_VAR_ROLES.map((role) => `--brand-button-${variant}-${role}`)
431
+ );
432
+ var ROLES_BY_FIELD = {
433
+ fill: ["bg"],
434
+ style: ["bg", "border"],
435
+ textColor: ["text"],
436
+ radius: ["radius"],
437
+ size: ["padding-x", "padding-y", "padding", "font-size"]
438
+ };
439
+ function touchedButtonRoles(variant, kit) {
440
+ if (!kit.touched) return new Set(BUTTON_VAR_ROLES);
441
+ const fields = kit.touched.buttons?.[variant] ?? [];
442
+ const outline = fields.includes("style") && kit.buttons?.[variant]?.style === "outline";
443
+ return new Set(
444
+ fields.flatMap((f) => {
445
+ if (outline && (f === "fill" || f === "style")) return ["border"];
446
+ return ROLES_BY_FIELD[f] ?? [];
447
+ })
448
+ );
449
+ }
450
+ function buttonPalette(kit) {
451
+ return kit.palette ?? {
452
+ dark: "var(--brand-text)",
453
+ primary: "var(--brand-primary)",
454
+ accent: "var(--brand-accent)",
455
+ light: "var(--brand-background)"
456
+ };
457
+ }
458
+ function buttonVariantVars(variant, style, roles, palette) {
459
+ const fill = palette[style.fill];
460
+ const text = palette[style.textColor];
461
+ const size = scaledButtonSize(style.size, roles.has("font-size") ? measureTemplateButton(variant) : null);
462
+ const { bg, border } = !roles.has("border") ? { bg: fill, border: "transparent" } : style.style === "outline" ? { bg: "transparent", border: fill } : style.style === "ghost" ? { bg: "transparent", border: "transparent" } : { bg: fill, border: "transparent" };
463
+ const all = {
464
+ [`--brand-button-${variant}-bg`]: bg,
465
+ [`--brand-button-${variant}-text`]: text,
466
+ [`--brand-button-${variant}-border`]: border,
467
+ [`--brand-button-${variant}-radius`]: `${BUTTON_RADIUS_PX[style.radius]}px`,
468
+ [`--brand-button-${variant}-padding-x`]: size.paddingX,
469
+ [`--brand-button-${variant}-padding-y`]: size.paddingY,
470
+ ...size.padding ? { [`--brand-button-${variant}-padding`]: size.padding } : {},
471
+ [`--brand-button-${variant}-font-size`]: size.fontSize
472
+ };
473
+ const prefix = `--brand-button-${variant}-`;
474
+ return Object.fromEntries(Object.entries(all).filter(([name]) => roles.has(name.slice(prefix.length))));
475
+ }
476
+ function buttonStyleVars(kit) {
477
+ return {
478
+ ...buttonVariantVars("primary", kit.buttons.primary, touchedButtonRoles("primary", kit), buttonPalette(kit)),
479
+ ...buttonVariantVars("secondary", kit.buttons.secondary, touchedButtonRoles("secondary", kit), buttonPalette(kit))
480
+ };
481
+ }
366
482
  function brandColorVars(palette) {
367
483
  const { dark, primary, accent, light } = palette;
368
484
  const mix = (a, aPct, b) => `color-mix(in srgb, ${a} ${aPct}%, ${b})`;
@@ -390,6 +506,11 @@ function brandColorVars(palette) {
390
506
  "--brand-on-primary": light
391
507
  };
392
508
  }
509
+ function isButtonStyle(value) {
510
+ if (!value || typeof value !== "object") return false;
511
+ const v = value;
512
+ return BUTTON_FILL_TOKENS.includes(v.fill) && BUTTON_FILL_TOKENS.includes(v.textColor) && BUTTON_STYLES.includes(v.style) && BUTTON_RADII.includes(v.radius) && BUTTON_SIZES.includes(v.size);
513
+ }
393
514
  function parseCustomFontWeight(raw) {
394
515
  if (typeof raw !== "object" || raw === null) return null;
395
516
  const w = raw;
@@ -409,6 +530,15 @@ function parseCustomFont(raw) {
409
530
  if (weights.length === 0) return null;
410
531
  return { family: f.family, label: f.label, weights };
411
532
  }
533
+ var BUTTON_FIELDS = Object.keys(ROLES_BY_FIELD);
534
+ function parseTouchedButtons(raw) {
535
+ const out = {};
536
+ for (const variant of ["primary", "secondary"]) {
537
+ const list = raw?.[variant];
538
+ if (Array.isArray(list)) out[variant] = BUTTON_FIELDS.filter((f) => list.includes(f));
539
+ }
540
+ return out;
541
+ }
412
542
  function parseBrandKit(raw) {
413
543
  if (!raw) return null;
414
544
  try {
@@ -418,11 +548,21 @@ function parseBrandKit(raw) {
418
548
  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;
419
549
  const fonts = f && typeof f.heading === "string" && typeof f.body === "string" ? { heading: f.heading, body: f.body } : void 0;
420
550
  const customFonts = Array.isArray(parsed?.customFonts) ? parsed.customFonts.map(parseCustomFont).filter((c) => c !== null) : void 0;
421
- if (!palette && !fonts && !customFonts) return null;
551
+ const b = parsed?.buttons;
552
+ const buttons = b && isButtonStyle(b.primary) && isButtonStyle(b.secondary) ? { primary: b.primary, secondary: b.secondary } : void 0;
553
+ if (!palette && !fonts && !customFonts && !buttons) return null;
422
554
  return {
423
555
  ...palette ? { palette } : {},
424
556
  ...fonts ? { fonts } : {},
425
- ...customFonts ? { customFonts } : {}
557
+ ...customFonts ? { customFonts } : {},
558
+ ...buttons ? { buttons } : {},
559
+ ...parsed.touched && typeof parsed.touched === "object" ? {
560
+ touched: {
561
+ palette: parsed.touched.palette === true,
562
+ fonts: parsed.touched.fonts === true,
563
+ buttons: parseTouchedButtons(parsed.touched.buttons)
564
+ }
565
+ } : {}
426
566
  };
427
567
  } catch {
428
568
  return null;
@@ -451,6 +591,39 @@ function loadBrandFonts(families) {
451
591
  }
452
592
  if (link.href !== href) link.href = href;
453
593
  }
594
+ var BUTTON_RULES_STYLE_ID = "ohw-brand-button-rules";
595
+ function buttonRulesCss(kit) {
596
+ const rule = (variant) => {
597
+ const roles = touchedButtonRoles(variant, kit);
598
+ const v = (role) => `var(--brand-button-${variant}-${role})`;
599
+ const decls = [];
600
+ if (roles.has("bg")) decls.push(`background:${v("bg")} !important`);
601
+ if (roles.has("text")) decls.push(`color:${v("text")} !important`);
602
+ if (roles.has("border")) decls.push(`border:1.5px solid ${v("border")} !important`);
603
+ if (roles.has("radius")) decls.push(`border-radius:${v("radius")} !important`);
604
+ if (roles.has("padding-x")) decls.push(`padding:var(--brand-button-${variant}-padding, ${v("padding-y")} ${v("padding-x")}) !important`);
605
+ if (roles.has("font-size")) decls.push(`font-size:${v("font-size")} !important`);
606
+ if (decls.length === 0) return "";
607
+ const selectors = [`html .btn--${variant}`, `html .btn-${variant}`];
608
+ const labelRule = roles.has("text") ? `${selectors.map((x) => `${x} *`).join(",")}{color:inherit !important}` : "";
609
+ return `${selectors.join(",")}{${decls.join(";")}}` + labelRule + (roles.has("bg") ? `${selectors.map((x) => `${x}:hover`).join(",")}{opacity:0.88}` : "");
610
+ };
611
+ return rule("primary") + rule("secondary");
612
+ }
613
+ function syncButtonRules(kit) {
614
+ let el = document.getElementById(BUTTON_RULES_STYLE_ID);
615
+ const css = kit ? buttonRulesCss(kit) : "";
616
+ if (!css) {
617
+ el?.remove();
618
+ return;
619
+ }
620
+ if (!el) {
621
+ el = document.createElement("style");
622
+ el.id = BUTTON_RULES_STYLE_ID;
623
+ document.head.appendChild(el);
624
+ }
625
+ if (el.textContent !== css) el.textContent = css;
626
+ }
454
627
  function escapeCssString(value) {
455
628
  return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
456
629
  }
@@ -476,28 +649,47 @@ function loadCustomFonts(fonts) {
476
649
  function applyBrandToDom(kit) {
477
650
  const root = document.documentElement;
478
651
  if (!kit) {
479
- for (const name of [...BRAND_VAR_NAMES, ...LEGACY_BRAND_VAR_NAMES]) root.style.removeProperty(name);
652
+ for (const name of [...BRAND_VAR_NAMES, ...LEGACY_BRAND_VAR_NAMES, ...BUTTON_VAR_NAMES]) {
653
+ root.style.removeProperty(name);
654
+ }
480
655
  for (const name of [...FONT_VARS.heading, ...FONT_VARS.body]) root.style.removeProperty(name);
481
656
  document.getElementById(BRAND_FONT_LINK_ID)?.remove();
482
657
  document.getElementById(CUSTOM_FONT_STYLE_ID)?.remove();
658
+ syncButtonRules(null);
483
659
  return;
484
660
  }
661
+ const applyPalette = kit.touched ? kit.touched.palette === true : true;
662
+ const applyFonts = kit.touched ? kit.touched.fonts === true : true;
485
663
  if (kit.palette) {
486
- for (const [name, value] of Object.entries(brandColorVars(kit.palette))) root.style.setProperty(name, value);
664
+ if (applyPalette) {
665
+ for (const [name, value] of Object.entries(brandColorVars(kit.palette))) root.style.setProperty(name, value);
666
+ } else {
667
+ for (const name of [...BRAND_VAR_NAMES, ...LEGACY_BRAND_VAR_NAMES]) root.style.removeProperty(name);
668
+ }
487
669
  }
670
+ if (kit.buttons) {
671
+ for (const name of BUTTON_VAR_NAMES) root.style.removeProperty(name);
672
+ for (const [name, value] of Object.entries(buttonStyleVars({ ...kit, buttons: kit.buttons }))) root.style.setProperty(name, value);
673
+ }
674
+ syncButtonRules(kit.buttons ? kit : null);
488
675
  if (kit.customFonts) {
489
676
  loadCustomFonts(kit.customFonts);
490
677
  }
491
678
  if (kit.fonts) {
492
- const heading = quoteFamily(kit.fonts.heading);
493
- const body = quoteFamily(kit.fonts.body);
494
- for (const name of FONT_VARS.heading) root.style.setProperty(name, heading);
495
- for (const name of FONT_VARS.body) root.style.setProperty(name, body);
496
- const customFamilies = new Set((kit.customFonts ?? []).map((f) => f.family));
497
- const googleFamilies = [familyOf(kit.fonts.heading), familyOf(kit.fonts.body)].filter(
498
- (f) => !customFamilies.has(f)
499
- );
500
- loadBrandFonts(googleFamilies);
679
+ if (applyFonts) {
680
+ const heading = quoteFamily(kit.fonts.heading);
681
+ const body = quoteFamily(kit.fonts.body);
682
+ for (const name of FONT_VARS.heading) root.style.setProperty(name, heading);
683
+ for (const name of FONT_VARS.body) root.style.setProperty(name, body);
684
+ const customFamilies = new Set((kit.customFonts ?? []).map((f) => f.family));
685
+ const googleFamilies = [familyOf(kit.fonts.heading), familyOf(kit.fonts.body)].filter(
686
+ (f) => !customFamilies.has(f)
687
+ );
688
+ loadBrandFonts(googleFamilies);
689
+ } else {
690
+ for (const name of [...FONT_VARS.heading, ...FONT_VARS.body]) root.style.removeProperty(name);
691
+ document.getElementById(BRAND_FONT_LINK_ID)?.remove();
692
+ }
501
693
  }
502
694
  }
503
695
 
@@ -647,7 +839,6 @@ var NODE_PROPS = [
647
839
  "color",
648
840
  "font-family",
649
841
  "font-size",
650
- "background",
651
842
  "text-align",
652
843
  "justify-content",
653
844
  "align-items"
@@ -697,9 +888,6 @@ function clearNodeProps(root) {
697
888
  h.removeAttribute(NODE_WROTE_ATTR);
698
889
  }
699
890
  }
700
- function buttonSurfaceOf(el) {
701
- return el.closest("a, button") ?? el;
702
- }
703
891
  function alignSubjectOf(el) {
704
892
  const button = el.closest('[data-ohw-role="button"]');
705
893
  if (button?.parentElement) return button.parentElement;
@@ -817,18 +1005,6 @@ function applyStylesToDom(store) {
817
1005
  );
818
1006
  subject.setAttribute(NODE_WROTE_ATTR, "");
819
1007
  }
820
- if (override.buttonBackground !== void 0 || override.buttonText !== void 0) {
821
- const surface = buttonSurfaceOf(el);
822
- if (override.buttonBackground !== void 0) {
823
- saveInline(surface, "background");
824
- surface.style.setProperty("background", override.buttonBackground, "important");
825
- }
826
- if (override.buttonText !== void 0) {
827
- saveInline(surface, "color");
828
- surface.style.setProperty("color", override.buttonText, "important");
829
- }
830
- surface.setAttribute(NODE_WROTE_ATTR, "");
831
- }
832
1008
  }
833
1009
  }
834
1010
  for (const [sectionId, override] of Object.entries(store.sections)) {
@@ -1414,7 +1590,59 @@ var AI_MOBILE_CSS = [
1414
1590
  "[data-ai-grid]{grid-template-columns:repeat(2, 1fr) !important}",
1415
1591
  "}"
1416
1592
  ].join("");
1417
- function buttonShellStyle(ctx, fullWidth) {
1593
+ function buttonSizeSpec(size) {
1594
+ switch (size) {
1595
+ case "s":
1596
+ return { padding: `${AI_TREE_TOKENS.spacing2}px ${AI_TREE_TOKENS.spacing4}px`, fontSize: 14 };
1597
+ case "l":
1598
+ return { padding: `${AI_TREE_TOKENS.spacing4}px ${AI_TREE_TOKENS.spacing8}px`, fontSize: 18 };
1599
+ case "m":
1600
+ default:
1601
+ return { padding: `${AI_TREE_TOKENS.spacing3}px ${AI_TREE_TOKENS.spacing6}px`, fontSize: AI_TREE_TOKENS.type.button.size };
1602
+ }
1603
+ }
1604
+ function buttonRadiusPx(radius) {
1605
+ switch (radius) {
1606
+ case "square":
1607
+ return 0;
1608
+ case "pill":
1609
+ return 999;
1610
+ case "rounded":
1611
+ default:
1612
+ return AI_TREE_TOKENS.radiusButton;
1613
+ }
1614
+ }
1615
+ function resolveButtonToken(brand, token) {
1616
+ return brand.palette[token];
1617
+ }
1618
+ function buttonColorStyle(ctx, variant) {
1619
+ const token = ctx.brand.buttons?.[variant];
1620
+ if (!token) return null;
1621
+ const fill = resolveButtonToken(ctx.brand, token.fill);
1622
+ const text = resolveButtonToken(ctx.brand, token.textColor);
1623
+ switch (token.style) {
1624
+ case "outline":
1625
+ return { background: "transparent", border: `1px solid ${fill}`, color: text };
1626
+ case "ghost":
1627
+ return { background: "transparent", border: "1px solid transparent", color: text };
1628
+ case "solid":
1629
+ default:
1630
+ return { background: fill, color: text };
1631
+ }
1632
+ }
1633
+ function buttonShellStyle(ctx, variant, fullWidth) {
1634
+ const buttonToken = ctx.brand.buttons?.[variant];
1635
+ if (buttonToken) {
1636
+ const { padding, fontSize } = buttonSizeSpec(buttonToken.size);
1637
+ return {
1638
+ borderRadius: buttonRadiusPx(buttonToken.radius),
1639
+ padding,
1640
+ fontFamily: ctx.brand.fonts.body,
1641
+ fontSize,
1642
+ lineHeight: AI_TREE_TOKENS.type.button.line,
1643
+ fontWeight: AI_TREE_TOKENS.type.button.weight
1644
+ };
1645
+ }
1418
1646
  const bs = ctx.buttonStyle;
1419
1647
  if (bs) {
1420
1648
  return {
@@ -1610,12 +1838,16 @@ function ButtonEl({
1610
1838
  secondaryColor
1611
1839
  }) {
1612
1840
  const secondary = slots.variant === "secondary";
1841
+ const variant = secondary ? "secondary" : "primary";
1842
+ const tokenColorStyle = buttonColorStyle(ctx, variant);
1613
1843
  const href = str(slots.href);
1614
1844
  const linkAttrs = ctx.keyFor && path ? { "data-ohw-href-key": ctx.keyFor(`${path}.href`), "data-ohw-role": "button" } : {};
1615
1845
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1616
1846
  "a",
1617
1847
  {
1618
1848
  href: href || void 0,
1849
+ className: `btn btn--${variant}`,
1850
+ "data-ohw-btn-variant": variant,
1619
1851
  ...linkAttrs,
1620
1852
  style: {
1621
1853
  display: fullWidth ? "flex" : "inline-flex",
@@ -1625,13 +1857,13 @@ function ButtonEl({
1625
1857
  textDecoration: "none",
1626
1858
  cursor: "pointer",
1627
1859
  whiteSpace: "nowrap",
1628
- ...buttonShellStyle(ctx, fullWidth),
1629
- ...secondary ? { border: `1px solid ${secondaryColor ?? ctx.brand.palette.dark}`, color: secondaryColor ?? ctx.brand.palette.dark } : (
1860
+ ...buttonShellStyle(ctx, variant, fullWidth),
1861
+ ...tokenColorStyle ?? (secondary ? { border: `1px solid ${secondaryColor ?? ctx.brand.palette.dark}`, color: secondaryColor ?? ctx.brand.palette.dark } : (
1630
1862
  // Off-band, prefer the template button's measured fill/label pair — a template may
1631
1863
  // fill its CTAs with any token (hvac: accent bg, primary text). On an accent band
1632
1864
  // the flipped palette keeps contrast, so the brand-driven colours stay.
1633
1865
  !ctx.buttonLabel && ctx.buttonStyle?.background ? { background: ctx.buttonStyle.background, color: ctx.buttonStyle.color } : { background: ctx.brand.palette.primary, color: ctx.buttonLabel ?? primaryButtonLabel(ctx.brand) }
1634
- )
1866
+ ))
1635
1867
  },
1636
1868
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ...path ? textAttrs(ctx, `${path}.label`) : {}, children: str(slots.label) })
1637
1869
  }
@@ -2853,18 +3085,21 @@ function renderNode(node, ctx, path) {
2853
3085
  "button",
2854
3086
  {
2855
3087
  type: "submit",
3088
+ className: "btn btn--primary",
3089
+ "data-ohw-btn-variant": "primary",
3090
+ "data-ohw-role": "button",
2856
3091
  style: {
2857
3092
  alignSelf: submitAlign,
2858
3093
  border: "none",
2859
3094
  cursor: "pointer",
2860
3095
  // Shape/padding/typography follow the host template's own buttons.
2861
- ...buttonShellStyle(ctx),
3096
+ ...buttonShellStyle(ctx, "primary"),
2862
3097
  // Brand-styled: the template button's measured fill/label pair off-band, else
2863
3098
  // primary fill with a brand-derived label so it reads on custom palettes.
2864
- ...!ctx.buttonLabel && ctx.buttonStyle?.background ? { background: ctx.buttonStyle.background, color: ctx.buttonStyle.color } : {
3099
+ ...buttonColorStyle(ctx, "primary") ?? (!ctx.buttonLabel && ctx.buttonStyle?.background ? { background: ctx.buttonStyle.background, color: ctx.buttonStyle.color } : {
2865
3100
  background: ctx.brand.palette.primary,
2866
3101
  color: ctx.buttonLabel ?? primaryButtonLabel(ctx.brand)
2867
- }
3102
+ })
2868
3103
  },
2869
3104
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ...textAttrs(ctx, `${path}.c${i}.label`), children: str(cs.label) })
2870
3105
  },
@@ -16842,6 +17077,9 @@ function guardTemplateHover(el) {
16842
17077
  observer.observe(el, { attributes: true, attributeFilter: ["style", "class"] });
16843
17078
  paintObserverByEl.set(el, observer);
16844
17079
  }
17080
+ function paintedTokensOf(el) {
17081
+ return paintLockByEl.get(el)?.tokens ?? null;
17082
+ }
16845
17083
  function applyButtonStyle(el, assignment, tokens) {
16846
17084
  paintLockByEl.set(el, { assignment, tokens });
16847
17085
  paintingButton = true;
@@ -17180,12 +17418,9 @@ function duplicateButton(source, content) {
17180
17418
  const label = buttonLabel(source);
17181
17419
  if (href) copy.setAttribute("href", href);
17182
17420
  const sourceAssignment = readAssignments(content)[sourceId] ?? assignmentFor(source, content, tokens);
17183
- const copyAssignment = {
17184
- variant: "secondary",
17185
- size: sourceAssignment.size
17186
- };
17187
- stampVariantClass(copy, "secondary");
17188
- applyButtonStyle(copy, copyAssignment, tokens);
17421
+ const copyAssignment = { ...sourceAssignment };
17422
+ stampVariantClass(copy, copyAssignment.variant);
17423
+ applyButtonStyle(copy, copyAssignment, paintedTokensOf(source) ?? tokens);
17189
17424
  const copyLabelEl = labelElement2(copy);
17190
17425
  if (copyLabelEl) copyLabelEl.textContent = label;
17191
17426
  let collection = buttonCollectionOf(source);
@@ -26636,7 +26871,7 @@ function OhhwellsBridge() {
26636
26871
  postToParent2({
26637
26872
  type: "ow:ready",
26638
26873
  version: "1",
26639
- bridgeVersion: "0.1.121",
26874
+ bridgeVersion: "0.1.122",
26640
26875
  path: pathname,
26641
26876
  nodes: collectEditableNodes(editContentRef.current),
26642
26877
  sections