@ohhwells/bridge 0.1.76-next.230 → 0.1.77-next.232

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
@@ -543,8 +543,6 @@ var AI_MOBILE_CSS = [
543
543
  // Group containers flatten to a column on phones; span placements come along for free.
544
544
  "[data-ai-group]{display:flex !important;flex-direction:column !important}",
545
545
  "[data-ai-group] > *{grid-column:auto !important}",
546
- // The 50:50 form collapses to a single stacked column on phones.
547
- "[data-ai-form]{grid-template-columns:1fr !important}",
548
546
  "[data-ai-section] img{max-width:100%}",
549
547
  "}",
550
548
  "@media (min-width: 769px) and (max-width: 1024px){",
@@ -1661,9 +1659,9 @@ function renderNode(node, ctx, path) {
1661
1659
  const fieldStyle = {
1662
1660
  width: "100%",
1663
1661
  boxSizing: "border-box",
1664
- border: `1px solid ${ctx.brand.palette.accent}`,
1665
- borderRadius: AI_TREE_TOKENS.radiusButton,
1666
- padding: "12px 14px",
1662
+ border: `1px solid color-mix(in srgb, ${ctx.brand.palette.dark} 45%, #ffffff)`,
1663
+ borderRadius: 0,
1664
+ padding: 12,
1667
1665
  background: "#fff",
1668
1666
  color: ctx.brand.palette.dark,
1669
1667
  outline: "none",
@@ -1671,81 +1669,90 @@ function renderNode(node, ctx, path) {
1671
1669
  };
1672
1670
  const labelStyle = {
1673
1671
  ...typeStyle(AI_TREE_TOKENS.type.bodyMBold, ctx.brand.fonts.body),
1674
- color: ctx.brand.palette.dark
1672
+ color: ctx.brand.palette.dark,
1673
+ textAlign: "left",
1674
+ width: "100%"
1675
1675
  };
1676
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1677
- "form",
1678
- {
1679
- ...formAttrs,
1680
- "data-ai-form": "",
1681
- style: {
1682
- display: "grid",
1683
- gridTemplateColumns: "repeat(2, minmax(0, 1fr))",
1684
- columnGap: 64,
1685
- rowGap: AI_TREE_TOKENS.spacing4
1686
- },
1687
- children: (node.children ?? []).map((child, i) => {
1688
- if (child.type === "input") {
1689
- const cs2 = child.slots ?? {};
1690
- const kind = str(cs2.kind);
1691
- const label = str(cs2.label);
1692
- const placeholder = str(cs2.placeholder);
1693
- const name = label.toLowerCase().replace(/[^a-z0-9]+/gu, "-").replace(/^-+|-+$/gu, "") || `field-${i}`;
1694
- const isTextarea = kind === "textarea";
1695
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1696
- "div",
1697
- {
1698
- style: {
1699
- display: "flex",
1700
- flexDirection: "column",
1701
- gap: 8,
1702
- ...isTextarea ? { gridColumn: "1 / -1", maxWidth: 780 } : {}
1676
+ const centered = ctx.sectionAlignment === "center";
1677
+ const submitAlign = centered ? "center" : "flex-start";
1678
+ const children = node.children ?? [];
1679
+ return (
1680
+ // 32px between the field group and the submit. In a stacked (centered) section the form is
1681
+ // capped at 780px and centered — the section's 12-col grid would otherwise leave it hugging
1682
+ // the left edge; a split section lets it fill its own column.
1683
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1684
+ "form",
1685
+ {
1686
+ ...formAttrs,
1687
+ "data-ai-form": "",
1688
+ style: {
1689
+ display: "flex",
1690
+ flexDirection: "column",
1691
+ gap: 32,
1692
+ width: "100%",
1693
+ ...centered ? { maxWidth: 780, marginLeft: "auto", marginRight: "auto" } : {}
1694
+ },
1695
+ children: [
1696
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: 24, width: "100%", alignItems: "flex-start" }, children: children.map((child, i) => {
1697
+ if (child.type !== "input") return null;
1698
+ const cs = child.slots ?? {};
1699
+ const kind = str(cs.kind);
1700
+ const label = str(cs.label);
1701
+ const placeholder = str(cs.placeholder);
1702
+ const required = cs.required === true;
1703
+ const name = label.toLowerCase().replace(/[^a-z0-9]+/gu, "-").replace(/^-+|-+$/gu, "") || `field-${i}`;
1704
+ const isTextarea = kind === "textarea";
1705
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 8, width: "100%" }, children: [
1706
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { ...textAttrs(ctx, `${path}.c${i}.label`), style: labelStyle, children: label }),
1707
+ isTextarea ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1708
+ "textarea",
1709
+ {
1710
+ name,
1711
+ placeholder,
1712
+ required,
1713
+ style: { ...fieldStyle, height: 180, resize: "vertical" }
1714
+ }
1715
+ ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1716
+ "input",
1717
+ {
1718
+ name,
1719
+ type: kind === "email" ? "email" : "text",
1720
+ placeholder,
1721
+ required,
1722
+ style: { ...fieldStyle, height: 48 }
1723
+ }
1724
+ )
1725
+ ] }, i);
1726
+ }) }),
1727
+ children.map((child, i) => {
1728
+ if (child.type === "input") return null;
1729
+ const cs = child.slots ?? {};
1730
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1731
+ "button",
1732
+ {
1733
+ type: "submit",
1734
+ style: {
1735
+ alignSelf: submitAlign,
1736
+ border: "none",
1737
+ cursor: "pointer",
1738
+ padding: "12px 24px",
1739
+ // Corner radius follows the host template's own buttons (measured from a template
1740
+ // CTA); 8px only when the page has no template button to match.
1741
+ borderRadius: ctx.buttonRadius ?? 8,
1742
+ // Brand-styled: primary fill, brand-derived label colour (not a fixed token) so it
1743
+ // reads correctly on custom palettes.
1744
+ background: ctx.brand.palette.primary,
1745
+ color: ctx.buttonLabel ?? ctx.brand.palette.light,
1746
+ ...typeStyle(AI_TREE_TOKENS.type.button, ctx.brand.fonts.body)
1747
+ },
1748
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ...textAttrs(ctx, `${path}.c${i}.label`), children: str(cs.label) })
1703
1749
  },
1704
- children: [
1705
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { ...textAttrs(ctx, `${path}.c${i}.label`), style: labelStyle, children: label }),
1706
- isTextarea ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1707
- "textarea",
1708
- {
1709
- name,
1710
- placeholder,
1711
- style: { ...fieldStyle, height: 140, resize: "vertical" }
1712
- }
1713
- ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1714
- "input",
1715
- {
1716
- name,
1717
- type: kind === "email" ? "email" : "text",
1718
- placeholder,
1719
- style: { ...fieldStyle, height: 48 }
1720
- }
1721
- )
1722
- ]
1723
- },
1724
- i
1725
- );
1726
- }
1727
- const cs = child.slots ?? {};
1728
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1729
- "button",
1730
- {
1731
- type: "submit",
1732
- style: {
1733
- gridColumn: "1 / -1",
1734
- justifySelf: "start",
1735
- border: "none",
1736
- cursor: "pointer",
1737
- padding: `${AI_TREE_TOKENS.spacing3}px ${AI_TREE_TOKENS.spacing6}px`,
1738
- borderRadius: AI_TREE_TOKENS.radiusButton,
1739
- background: ctx.brand.palette.primary,
1740
- color: AI_TREE_TOKENS.textPrimaryForeground,
1741
- ...typeStyle(AI_TREE_TOKENS.type.button, ctx.brand.fonts.body)
1742
- },
1743
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ...textAttrs(ctx, `${path}.c${i}.label`), children: str(cs.label) })
1744
- },
1745
- i
1746
- );
1747
- })
1748
- }
1750
+ i
1751
+ );
1752
+ })
1753
+ ]
1754
+ }
1755
+ )
1749
1756
  );
1750
1757
  }
1751
1758
  case "schedule-widget":
@@ -1768,7 +1775,13 @@ function renderNode(node, ctx, path) {
1768
1775
  return null;
1769
1776
  }
1770
1777
  }
1771
- function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1778
+ function AiTreeRenderer({
1779
+ tree,
1780
+ brand,
1781
+ buttonRadius,
1782
+ resolveMedia,
1783
+ editKeyPrefix
1784
+ }) {
1772
1785
  if (!isRenderableTree(tree)) {
1773
1786
  return null;
1774
1787
  }
@@ -1780,6 +1793,8 @@ function AiTreeRenderer({ tree, brand, resolveMedia, editKeyPrefix }) {
1780
1793
  resolveMedia: resolveMedia ?? (() => null),
1781
1794
  cardSurface: blockBrand.palette.light.toUpperCase() === AI_DEFAULT_BRAND.palette.light.toUpperCase() ? "#ECECEB" : `color-mix(in srgb, ${blockBrand.palette.light} 90%, ${blockBrand.palette.dark})`,
1782
1795
  keyFor: editKeyPrefix ? (path) => `${editKeyPrefix}.${path}` : null,
1796
+ sectionAlignment: (tree.settings ?? {}).alignment === "center" ? "center" : "left",
1797
+ buttonRadius,
1783
1798
  ...band ? { buttonLabel: band.buttonLabel } : {}
1784
1799
  };
1785
1800
  const settings = tree.settings ?? {};
@@ -1911,6 +1926,13 @@ function deriveTemplateBrand() {
1911
1926
  }
1912
1927
  };
1913
1928
  }
1929
+ function deriveTemplateButtonRadius() {
1930
+ if (typeof document === "undefined") return null;
1931
+ const btn = document.querySelector('[data-ohw-role="button"]');
1932
+ if (!btn) return null;
1933
+ const radius = getComputedStyle(btn).borderTopLeftRadius;
1934
+ return radius || null;
1935
+ }
1914
1936
  var mounted = /* @__PURE__ */ new Map();
1915
1937
  function findTemplateSection(id) {
1916
1938
  for (const el of document.querySelectorAll(`[data-ohw-section="${CSS.escape(id)}"]`)) {
@@ -2062,6 +2084,7 @@ function applyAiSectionsToDom(state, options) {
2062
2084
  if (typeof document === "undefined") return;
2063
2085
  const brandOverride = deriveBrandOverride();
2064
2086
  const templateBrand = deriveTemplateBrand();
2087
+ const templateButtonRadius = deriveTemplateButtonRadius();
2065
2088
  const brandKey = brandOverride ? JSON.stringify(brandOverride) : "";
2066
2089
  const pagePath = window.location.pathname;
2067
2090
  const pageSections = state.sections.filter((entry) => (entry.path ?? "/") === pagePath);
@@ -2101,6 +2124,7 @@ function applyAiSectionsToDom(state, options) {
2101
2124
  {
2102
2125
  tree: entry.tree,
2103
2126
  brand: brandOverride ?? entry.brand ?? options?.brand ?? templateBrand ?? void 0,
2127
+ buttonRadius: templateButtonRadius,
2104
2128
  resolveMedia,
2105
2129
  editKeyPrefix: `ai.${entry.id}`
2106
2130
  }
@@ -21109,7 +21133,7 @@ function OhhwellsBridge() {
21109
21133
  postToParent2({
21110
21134
  type: "ow:ready",
21111
21135
  version: "1",
21112
- bridgeVersion: "0.1.76",
21136
+ bridgeVersion: "0.1.77",
21113
21137
  path: pathname,
21114
21138
  nodes: collectEditableNodes(editContentRef.current),
21115
21139
  sections