@ohhwells/bridge 0.1.79 → 0.1.80

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
@@ -542,8 +542,6 @@ var AI_MOBILE_CSS = [
542
542
  // Group containers flatten to a column on phones; span placements come along for free.
543
543
  "[data-ai-group]{display:flex !important;flex-direction:column !important}",
544
544
  "[data-ai-group] > *{grid-column:auto !important}",
545
- // The 50:50 form collapses to a single stacked column on phones.
546
- "[data-ai-form]{grid-template-columns:1fr !important}",
547
545
  "[data-ai-section] img{max-width:100%}",
548
546
  "}",
549
547
  "@media (min-width: 769px) and (max-width: 1024px){",
@@ -1640,9 +1638,9 @@ function renderNode(node, ctx, path) {
1640
1638
  const fieldStyle = {
1641
1639
  width: "100%",
1642
1640
  boxSizing: "border-box",
1643
- border: `1px solid ${ctx.brand.palette.accent}`,
1644
- borderRadius: AI_TREE_TOKENS.radiusButton,
1645
- padding: "12px 14px",
1641
+ border: `1px solid color-mix(in srgb, ${ctx.brand.palette.dark} 45%, #ffffff)`,
1642
+ borderRadius: 0,
1643
+ padding: 12,
1646
1644
  background: "#fff",
1647
1645
  color: ctx.brand.palette.dark,
1648
1646
  outline: "none",
@@ -1650,81 +1648,90 @@ function renderNode(node, ctx, path) {
1650
1648
  };
1651
1649
  const labelStyle = {
1652
1650
  ...typeStyle(AI_TREE_TOKENS.type.bodyMBold, ctx.brand.fonts.body),
1653
- color: ctx.brand.palette.dark
1651
+ color: ctx.brand.palette.dark,
1652
+ textAlign: "left",
1653
+ width: "100%"
1654
1654
  };
1655
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1656
- "form",
1657
- {
1658
- ...formAttrs,
1659
- "data-ai-form": "",
1660
- style: {
1661
- display: "grid",
1662
- gridTemplateColumns: "repeat(2, minmax(0, 1fr))",
1663
- columnGap: 64,
1664
- rowGap: AI_TREE_TOKENS.spacing4
1665
- },
1666
- children: (node.children ?? []).map((child, i) => {
1667
- if (child.type === "input") {
1668
- const cs2 = child.slots ?? {};
1669
- const kind = str(cs2.kind);
1670
- const label = str(cs2.label);
1671
- const placeholder = str(cs2.placeholder);
1672
- const name = label.toLowerCase().replace(/[^a-z0-9]+/gu, "-").replace(/^-+|-+$/gu, "") || `field-${i}`;
1673
- const isTextarea = kind === "textarea";
1674
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1675
- "div",
1676
- {
1677
- style: {
1678
- display: "flex",
1679
- flexDirection: "column",
1680
- gap: 8,
1681
- ...isTextarea ? { gridColumn: "1 / -1", maxWidth: 780 } : {}
1655
+ const centered = ctx.sectionAlignment === "center";
1656
+ const submitAlign = centered ? "center" : "flex-start";
1657
+ const children = node.children ?? [];
1658
+ return (
1659
+ // 32px between the field group and the submit. In a stacked (centered) section the form is
1660
+ // capped at 780px and centered — the section's 12-col grid would otherwise leave it hugging
1661
+ // the left edge; a split section lets it fill its own column.
1662
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1663
+ "form",
1664
+ {
1665
+ ...formAttrs,
1666
+ "data-ai-form": "",
1667
+ style: {
1668
+ display: "flex",
1669
+ flexDirection: "column",
1670
+ gap: 32,
1671
+ width: "100%",
1672
+ ...centered ? { maxWidth: 780, marginLeft: "auto", marginRight: "auto" } : {}
1673
+ },
1674
+ children: [
1675
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: 24, width: "100%", alignItems: "flex-start" }, children: children.map((child, i) => {
1676
+ if (child.type !== "input") return null;
1677
+ const cs = child.slots ?? {};
1678
+ const kind = str(cs.kind);
1679
+ const label = str(cs.label);
1680
+ const placeholder = str(cs.placeholder);
1681
+ const required = cs.required === true;
1682
+ const name = label.toLowerCase().replace(/[^a-z0-9]+/gu, "-").replace(/^-+|-+$/gu, "") || `field-${i}`;
1683
+ const isTextarea = kind === "textarea";
1684
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 8, width: "100%" }, children: [
1685
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { ...textAttrs(ctx, `${path}.c${i}.label`), style: labelStyle, children: label }),
1686
+ isTextarea ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1687
+ "textarea",
1688
+ {
1689
+ name,
1690
+ placeholder,
1691
+ required,
1692
+ style: { ...fieldStyle, height: 180, resize: "vertical" }
1693
+ }
1694
+ ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1695
+ "input",
1696
+ {
1697
+ name,
1698
+ type: kind === "email" ? "email" : "text",
1699
+ placeholder,
1700
+ required,
1701
+ style: { ...fieldStyle, height: 48 }
1702
+ }
1703
+ )
1704
+ ] }, i);
1705
+ }) }),
1706
+ children.map((child, i) => {
1707
+ if (child.type === "input") return null;
1708
+ const cs = child.slots ?? {};
1709
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1710
+ "button",
1711
+ {
1712
+ type: "submit",
1713
+ style: {
1714
+ alignSelf: submitAlign,
1715
+ border: "none",
1716
+ cursor: "pointer",
1717
+ padding: "12px 24px",
1718
+ // Corner radius follows the host template's own buttons (measured from a template
1719
+ // CTA); 8px only when the page has no template button to match.
1720
+ borderRadius: ctx.buttonRadius ?? 8,
1721
+ // Brand-styled: primary fill, brand-derived label colour (not a fixed token) so it
1722
+ // reads correctly on custom palettes.
1723
+ background: ctx.brand.palette.primary,
1724
+ color: ctx.buttonLabel ?? ctx.brand.palette.light,
1725
+ ...typeStyle(AI_TREE_TOKENS.type.button, ctx.brand.fonts.body)
1726
+ },
1727
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ...textAttrs(ctx, `${path}.c${i}.label`), children: str(cs.label) })
1682
1728
  },
1683
- children: [
1684
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { ...textAttrs(ctx, `${path}.c${i}.label`), style: labelStyle, children: label }),
1685
- isTextarea ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1686
- "textarea",
1687
- {
1688
- name,
1689
- placeholder,
1690
- style: { ...fieldStyle, height: 140, resize: "vertical" }
1691
- }
1692
- ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1693
- "input",
1694
- {
1695
- name,
1696
- type: kind === "email" ? "email" : "text",
1697
- placeholder,
1698
- style: { ...fieldStyle, height: 48 }
1699
- }
1700
- )
1701
- ]
1702
- },
1703
- i
1704
- );
1705
- }
1706
- const cs = child.slots ?? {};
1707
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1708
- "button",
1709
- {
1710
- type: "submit",
1711
- style: {
1712
- gridColumn: "1 / -1",
1713
- justifySelf: "start",
1714
- border: "none",
1715
- cursor: "pointer",
1716
- padding: `${AI_TREE_TOKENS.spacing3}px ${AI_TREE_TOKENS.spacing6}px`,
1717
- borderRadius: AI_TREE_TOKENS.radiusButton,
1718
- background: ctx.brand.palette.primary,
1719
- color: AI_TREE_TOKENS.textPrimaryForeground,
1720
- ...typeStyle(AI_TREE_TOKENS.type.button, ctx.brand.fonts.body)
1721
- },
1722
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ...textAttrs(ctx, `${path}.c${i}.label`), children: str(cs.label) })
1723
- },
1724
- i
1725
- );
1726
- })
1727
- }
1729
+ i
1730
+ );
1731
+ })
1732
+ ]
1733
+ }
1734
+ )
1728
1735
  );
1729
1736
  }
1730
1737
  case "schedule-widget":
@@ -1747,7 +1754,13 @@ function renderNode(node, ctx, path) {
1747
1754
  return null;
1748
1755
  }
1749
1756
  }
1750
- function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1757
+ function AiTreeRenderer({
1758
+ tree,
1759
+ brand,
1760
+ buttonRadius,
1761
+ resolveMedia,
1762
+ editKeyPrefix
1763
+ }) {
1751
1764
  if (!isRenderableTree(tree)) {
1752
1765
  return null;
1753
1766
  }
@@ -1759,6 +1772,8 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1759
1772
  resolveMedia: resolveMedia ?? (() => null),
1760
1773
  cardSurface: blockBrand.palette.light.toUpperCase() === AI_DEFAULT_BRAND.palette.light.toUpperCase() ? "#ECECEB" : `color-mix(in srgb, ${blockBrand.palette.light} 90%, ${blockBrand.palette.dark})`,
1761
1774
  keyFor: editKeyPrefix ? (path) => `${editKeyPrefix}.${path}` : null,
1775
+ sectionAlignment: (tree.settings ?? {}).alignment === "center" ? "center" : "left",
1776
+ buttonRadius,
1762
1777
  ...band ? { buttonLabel: band.buttonLabel } : {}
1763
1778
  };
1764
1779
  const settings = tree.settings ?? {};
@@ -1888,6 +1903,13 @@ function deriveTemplateBrand() {
1888
1903
  }
1889
1904
  };
1890
1905
  }
1906
+ function deriveTemplateButtonRadius() {
1907
+ if (typeof document === "undefined") return null;
1908
+ const btn = document.querySelector('[data-ohw-role="button"]');
1909
+ if (!btn) return null;
1910
+ const radius = getComputedStyle(btn).borderTopLeftRadius;
1911
+ return radius || null;
1912
+ }
1891
1913
  var mounted = /* @__PURE__ */ new Map();
1892
1914
  function findTemplateSection(id) {
1893
1915
  for (const el of document.querySelectorAll(`[data-ohw-section="${CSS.escape(id)}"]`)) {
@@ -2039,6 +2061,7 @@ function applyAiSectionsToDom(state, options) {
2039
2061
  if (typeof document === "undefined") return;
2040
2062
  const brandOverride = deriveBrandOverride();
2041
2063
  const templateBrand = deriveTemplateBrand();
2064
+ const templateButtonRadius = deriveTemplateButtonRadius();
2042
2065
  const brandKey = brandOverride ? JSON.stringify(brandOverride) : "";
2043
2066
  const pagePath = window.location.pathname;
2044
2067
  const pageSections = state.sections.filter((entry) => (entry.path ?? "/") === pagePath);
@@ -2078,6 +2101,7 @@ function applyAiSectionsToDom(state, options) {
2078
2101
  {
2079
2102
  tree: entry.tree,
2080
2103
  brand: brandOverride ?? entry.brand ?? options?.brand ?? templateBrand ?? void 0,
2104
+ buttonRadius: templateButtonRadius,
2081
2105
  resolveMedia,
2082
2106
  editKeyPrefix: `ai.${entry.id}`
2083
2107
  }
@@ -21063,7 +21087,7 @@ function OhhwellsBridge() {
21063
21087
  postToParent2({
21064
21088
  type: "ow:ready",
21065
21089
  version: "1",
21066
- bridgeVersion: "0.1.78",
21090
+ bridgeVersion: "0.1.79",
21067
21091
  path: pathname,
21068
21092
  nodes: collectEditableNodes(editContentRef.current),
21069
21093
  sections