@ohhwells/bridge 0.1.95 → 0.1.96
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 +65 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -3
- package/dist/index.d.ts +17 -3
- package/dist/index.js +65 -25
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -76,6 +76,20 @@ interface AiBrandKit {
|
|
|
76
76
|
body: string;
|
|
77
77
|
};
|
|
78
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* Shape, padding and typography measured off a host-template CTA, so generated buttons read as the
|
|
81
|
+
* site's own button rather than a default. Colour stays brand-driven (band-aware); everything here
|
|
82
|
+
* follows the template button. Null when the page has no template button to measure.
|
|
83
|
+
*/
|
|
84
|
+
interface TemplateButtonStyle {
|
|
85
|
+
radius: string;
|
|
86
|
+
padding: string;
|
|
87
|
+
fontFamily: string;
|
|
88
|
+
fontSize: string;
|
|
89
|
+
fontWeight: string;
|
|
90
|
+
letterSpacing: string;
|
|
91
|
+
textTransform: string;
|
|
92
|
+
}
|
|
79
93
|
/** Section System defaults — used when a site has no brand kit override. */
|
|
80
94
|
declare const AI_DEFAULT_BRAND: AiBrandKit;
|
|
81
95
|
/** Design tokens from the Section System Blocks page (px unless noted). */
|
|
@@ -143,8 +157,8 @@ declare function isRenderableTree(value: unknown): value is AiLayoutTree;
|
|
|
143
157
|
interface AiTreeRendererProps {
|
|
144
158
|
tree: AiLayoutTree;
|
|
145
159
|
brand?: AiBrandKit;
|
|
146
|
-
/** The host template's button
|
|
147
|
-
|
|
160
|
+
/** The host template's button shape/padding/typography; generated CTAs adopt it. */
|
|
161
|
+
buttonStyle?: TemplateButtonStyle | null;
|
|
148
162
|
/** Resolves a media ref to a URL; null renders a placeholder. */
|
|
149
163
|
resolveMedia?: (ref: string) => string | null;
|
|
150
164
|
/**
|
|
@@ -154,7 +168,7 @@ interface AiTreeRendererProps {
|
|
|
154
168
|
*/
|
|
155
169
|
editKeyPrefix?: string;
|
|
156
170
|
}
|
|
157
|
-
declare function AiTreeRenderer({ tree, brand,
|
|
171
|
+
declare function AiTreeRenderer({ tree, brand, buttonStyle, resolveMedia, editKeyPrefix, }: AiTreeRendererProps): react_jsx_runtime.JSX.Element | null;
|
|
158
172
|
|
|
159
173
|
type LinkPage = {
|
|
160
174
|
path: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -76,6 +76,20 @@ interface AiBrandKit {
|
|
|
76
76
|
body: string;
|
|
77
77
|
};
|
|
78
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* Shape, padding and typography measured off a host-template CTA, so generated buttons read as the
|
|
81
|
+
* site's own button rather than a default. Colour stays brand-driven (band-aware); everything here
|
|
82
|
+
* follows the template button. Null when the page has no template button to measure.
|
|
83
|
+
*/
|
|
84
|
+
interface TemplateButtonStyle {
|
|
85
|
+
radius: string;
|
|
86
|
+
padding: string;
|
|
87
|
+
fontFamily: string;
|
|
88
|
+
fontSize: string;
|
|
89
|
+
fontWeight: string;
|
|
90
|
+
letterSpacing: string;
|
|
91
|
+
textTransform: string;
|
|
92
|
+
}
|
|
79
93
|
/** Section System defaults — used when a site has no brand kit override. */
|
|
80
94
|
declare const AI_DEFAULT_BRAND: AiBrandKit;
|
|
81
95
|
/** Design tokens from the Section System Blocks page (px unless noted). */
|
|
@@ -143,8 +157,8 @@ declare function isRenderableTree(value: unknown): value is AiLayoutTree;
|
|
|
143
157
|
interface AiTreeRendererProps {
|
|
144
158
|
tree: AiLayoutTree;
|
|
145
159
|
brand?: AiBrandKit;
|
|
146
|
-
/** The host template's button
|
|
147
|
-
|
|
160
|
+
/** The host template's button shape/padding/typography; generated CTAs adopt it. */
|
|
161
|
+
buttonStyle?: TemplateButtonStyle | null;
|
|
148
162
|
/** Resolves a media ref to a URL; null renders a placeholder. */
|
|
149
163
|
resolveMedia?: (ref: string) => string | null;
|
|
150
164
|
/**
|
|
@@ -154,7 +168,7 @@ interface AiTreeRendererProps {
|
|
|
154
168
|
*/
|
|
155
169
|
editKeyPrefix?: string;
|
|
156
170
|
}
|
|
157
|
-
declare function AiTreeRenderer({ tree, brand,
|
|
171
|
+
declare function AiTreeRenderer({ tree, brand, buttonStyle, resolveMedia, editKeyPrefix, }: AiTreeRendererProps): react_jsx_runtime.JSX.Element | null;
|
|
158
172
|
|
|
159
173
|
type LinkPage = {
|
|
160
174
|
path: string;
|
package/dist/index.js
CHANGED
|
@@ -879,6 +879,25 @@ var FEATURE_LINE_CSS = [
|
|
|
879
879
|
`background-color:currentColor;-webkit-mask:url("${CHECK_MASK}") no-repeat 0 0/24px 24px;`,
|
|
880
880
|
`mask:url("${CHECK_MASK}") no-repeat 0 0/24px 24px}`
|
|
881
881
|
].join("");
|
|
882
|
+
function buttonShellStyle(ctx, fullWidth) {
|
|
883
|
+
const bs = ctx.buttonStyle;
|
|
884
|
+
if (bs) {
|
|
885
|
+
return {
|
|
886
|
+
borderRadius: bs.radius,
|
|
887
|
+
...bs.padding ? { padding: bs.padding } : {},
|
|
888
|
+
...bs.fontFamily ? { fontFamily: bs.fontFamily } : { fontFamily: ctx.brand.fonts.body },
|
|
889
|
+
...bs.fontSize ? { fontSize: bs.fontSize } : {},
|
|
890
|
+
...bs.fontWeight ? { fontWeight: bs.fontWeight } : {},
|
|
891
|
+
...bs.letterSpacing && bs.letterSpacing !== "normal" ? { letterSpacing: bs.letterSpacing } : {},
|
|
892
|
+
...bs.textTransform && bs.textTransform !== "none" ? { textTransform: bs.textTransform } : {}
|
|
893
|
+
};
|
|
894
|
+
}
|
|
895
|
+
return {
|
|
896
|
+
borderRadius: AI_TREE_TOKENS.radiusButton,
|
|
897
|
+
padding: fullWidth ? `${AI_TREE_TOKENS.spacing2}px ${AI_TREE_TOKENS.spacing4}px` : `${AI_TREE_TOKENS.spacing3}px ${AI_TREE_TOKENS.spacing6}px`,
|
|
898
|
+
...typeStyle(AI_TREE_TOKENS.type.button, ctx.brand.fonts.body)
|
|
899
|
+
};
|
|
900
|
+
}
|
|
882
901
|
function hexLuminance(color) {
|
|
883
902
|
const m = /^#([0-9a-f]{6})$/i.exec(color.trim());
|
|
884
903
|
if (!m) return null;
|
|
@@ -895,6 +914,12 @@ function hexContrast(a, b) {
|
|
|
895
914
|
const [hi, lo] = la > lb ? [la, lb] : [lb, la];
|
|
896
915
|
return (hi + 0.05) / (lo + 0.05);
|
|
897
916
|
}
|
|
917
|
+
function primaryButtonLabel(brand) {
|
|
918
|
+
const darkC = hexContrast(brand.palette.primary, brand.palette.dark);
|
|
919
|
+
const lightC = hexContrast(brand.palette.primary, AI_TREE_TOKENS.textPrimaryForeground);
|
|
920
|
+
if (darkC === null || lightC === null) return AI_TREE_TOKENS.textPrimaryForeground;
|
|
921
|
+
return darkC > lightC ? brand.palette.dark : AI_TREE_TOKENS.textPrimaryForeground;
|
|
922
|
+
}
|
|
898
923
|
function accentBandContext(brand) {
|
|
899
924
|
const p = brand.palette;
|
|
900
925
|
const lightWins = (hexContrast(p.light, p.primary) ?? 99) >= (hexContrast(p.dark, p.primary) ?? 0);
|
|
@@ -994,12 +1019,10 @@ function ButtonEl({
|
|
|
994
1019
|
width: fullWidth ? "100%" : void 0,
|
|
995
1020
|
alignItems: "center",
|
|
996
1021
|
justifyContent: "center",
|
|
997
|
-
padding: fullWidth ? `${AI_TREE_TOKENS.spacing2}px ${AI_TREE_TOKENS.spacing4}px` : `${AI_TREE_TOKENS.spacing3}px ${AI_TREE_TOKENS.spacing6}px`,
|
|
998
|
-
borderRadius: AI_TREE_TOKENS.radiusButton,
|
|
999
1022
|
textDecoration: "none",
|
|
1000
1023
|
cursor: "pointer",
|
|
1001
|
-
...
|
|
1002
|
-
...secondary ? { border: `1px solid ${ctx.brand.palette.dark}`, color: ctx.brand.palette.dark } : { background: ctx.brand.palette.primary, color: ctx.buttonLabel ??
|
|
1024
|
+
...buttonShellStyle(ctx, fullWidth),
|
|
1025
|
+
...secondary ? { border: `1px solid ${ctx.brand.palette.dark}`, color: ctx.brand.palette.dark } : { background: ctx.brand.palette.primary, color: ctx.buttonLabel ?? primaryButtonLabel(ctx.brand) }
|
|
1003
1026
|
},
|
|
1004
1027
|
children: /* @__PURE__ */ jsx("span", { ...path ? textAttrs(ctx, `${path}.label`) : {}, children: str(slots.label) })
|
|
1005
1028
|
}
|
|
@@ -2039,15 +2062,12 @@ function renderNode(node, ctx, path) {
|
|
|
2039
2062
|
alignSelf: submitAlign,
|
|
2040
2063
|
border: "none",
|
|
2041
2064
|
cursor: "pointer",
|
|
2042
|
-
padding
|
|
2043
|
-
|
|
2044
|
-
// CTA); 8px only when the page has no template button to match.
|
|
2045
|
-
borderRadius: ctx.buttonRadius ?? 8,
|
|
2065
|
+
// Shape/padding/typography follow the host template's own buttons.
|
|
2066
|
+
...buttonShellStyle(ctx),
|
|
2046
2067
|
// Brand-styled: primary fill, brand-derived label colour (not a fixed token) so it
|
|
2047
2068
|
// reads correctly on custom palettes.
|
|
2048
2069
|
background: ctx.brand.palette.primary,
|
|
2049
|
-
color: ctx.buttonLabel ?? ctx.brand
|
|
2050
|
-
...typeStyle(AI_TREE_TOKENS.type.button, ctx.brand.fonts.body)
|
|
2070
|
+
color: ctx.buttonLabel ?? primaryButtonLabel(ctx.brand)
|
|
2051
2071
|
},
|
|
2052
2072
|
children: /* @__PURE__ */ jsx("span", { ...textAttrs(ctx, `${path}.c${i}.label`), children: str(cs.label) })
|
|
2053
2073
|
},
|
|
@@ -2082,7 +2102,7 @@ function renderNode(node, ctx, path) {
|
|
|
2082
2102
|
function AiTreeRenderer({
|
|
2083
2103
|
tree,
|
|
2084
2104
|
brand,
|
|
2085
|
-
|
|
2105
|
+
buttonStyle,
|
|
2086
2106
|
resolveMedia,
|
|
2087
2107
|
editKeyPrefix
|
|
2088
2108
|
}) {
|
|
@@ -2098,7 +2118,7 @@ function AiTreeRenderer({
|
|
|
2098
2118
|
cardSurface: blockBrand.palette.light.toUpperCase() === AI_DEFAULT_BRAND.palette.light.toUpperCase() ? "#ECECEB" : `color-mix(in srgb, ${blockBrand.palette.light} 90%, ${blockBrand.palette.dark})`,
|
|
2099
2119
|
keyFor: editKeyPrefix ? (path) => `${editKeyPrefix}.${path}` : null,
|
|
2100
2120
|
sectionAlignment: (tree.settings ?? {}).alignment === "center" ? "center" : "left",
|
|
2101
|
-
|
|
2121
|
+
buttonStyle,
|
|
2102
2122
|
...band ? { buttonLabel: band.buttonLabel } : {}
|
|
2103
2123
|
};
|
|
2104
2124
|
const settings = tree.settings ?? {};
|
|
@@ -2228,13 +2248,13 @@ function deriveBrandOverride() {
|
|
|
2228
2248
|
};
|
|
2229
2249
|
}
|
|
2230
2250
|
function deriveTemplateBrand() {
|
|
2231
|
-
const
|
|
2232
|
-
const
|
|
2233
|
-
const light = readRootVar("--color-light");
|
|
2251
|
+
const primary = readRootVar("--brand-primary") || readRootVar("--color-primary");
|
|
2252
|
+
const dark = readRootVar("--brand-text") || readRootVar("--color-dark");
|
|
2253
|
+
const light = readRootVar("--brand-background") || readRootVar("--color-light");
|
|
2234
2254
|
if (!dark || !primary || !light) return null;
|
|
2235
|
-
const accent = readRootVar("--color-accent");
|
|
2236
|
-
const heading = readRootVar("--font-heading") || readRootVar("--font-display");
|
|
2237
|
-
const body = readRootVar("--font-body");
|
|
2255
|
+
const accent = readRootVar("--brand-accent") || readRootVar("--color-accent");
|
|
2256
|
+
const heading = readRootVar("--brand-font-heading") || readRootVar("--font-heading") || readRootVar("--font-display");
|
|
2257
|
+
const body = readRootVar("--brand-font-body") || readRootVar("--font-body");
|
|
2238
2258
|
return {
|
|
2239
2259
|
palette: { dark, primary, accent: accent || dark, light },
|
|
2240
2260
|
fonts: {
|
|
@@ -2243,12 +2263,32 @@ function deriveTemplateBrand() {
|
|
|
2243
2263
|
}
|
|
2244
2264
|
};
|
|
2245
2265
|
}
|
|
2246
|
-
function
|
|
2266
|
+
function deriveTemplateButtonStyle() {
|
|
2247
2267
|
if (typeof document === "undefined") return null;
|
|
2248
|
-
const btn = document.
|
|
2268
|
+
const btn = Array.from(document.querySelectorAll('[data-ohw-role="button"]')).find(
|
|
2269
|
+
(el) => !el.closest(`[${CONTAINER_ATTR}]`)
|
|
2270
|
+
);
|
|
2249
2271
|
if (!btn) return null;
|
|
2250
|
-
const
|
|
2251
|
-
|
|
2272
|
+
const cs = getComputedStyle(btn);
|
|
2273
|
+
const corners = [
|
|
2274
|
+
cs.borderTopLeftRadius,
|
|
2275
|
+
cs.borderTopRightRadius,
|
|
2276
|
+
cs.borderBottomRightRadius,
|
|
2277
|
+
cs.borderBottomLeftRadius
|
|
2278
|
+
].map((v) => v || "0px");
|
|
2279
|
+
const radius = corners.every((v) => v === corners[0]) ? corners[0] : corners.join(" ");
|
|
2280
|
+
const px = (v) => parseFloat(v) || 0;
|
|
2281
|
+
const padY = Math.max(px(cs.paddingTop), px(cs.paddingBottom));
|
|
2282
|
+
const padX = Math.max(px(cs.paddingLeft), px(cs.paddingRight));
|
|
2283
|
+
return {
|
|
2284
|
+
radius: radius || "10px",
|
|
2285
|
+
padding: `${padY}px ${padX}px`,
|
|
2286
|
+
fontFamily: cs.fontFamily || "",
|
|
2287
|
+
fontSize: cs.fontSize || "",
|
|
2288
|
+
fontWeight: cs.fontWeight || "",
|
|
2289
|
+
letterSpacing: cs.letterSpacing || "",
|
|
2290
|
+
textTransform: cs.textTransform || ""
|
|
2291
|
+
};
|
|
2252
2292
|
}
|
|
2253
2293
|
var mounted = /* @__PURE__ */ new Map();
|
|
2254
2294
|
function findTemplateSection(id) {
|
|
@@ -2419,7 +2459,7 @@ function applyAiSectionsToDom(state, options) {
|
|
|
2419
2459
|
if (typeof document === "undefined") return;
|
|
2420
2460
|
const brandOverride = deriveBrandOverride();
|
|
2421
2461
|
const templateBrand = deriveTemplateBrand();
|
|
2422
|
-
const
|
|
2462
|
+
const templateButtonStyle = deriveTemplateButtonStyle();
|
|
2423
2463
|
const brandKey = brandOverride ? JSON.stringify(brandOverride) : "";
|
|
2424
2464
|
const pagePath = window.location.pathname;
|
|
2425
2465
|
const pageSections = state.sections.filter((entry) => (entry.path ?? "/") === pagePath);
|
|
@@ -2459,7 +2499,7 @@ function applyAiSectionsToDom(state, options) {
|
|
|
2459
2499
|
{
|
|
2460
2500
|
tree: entry.tree,
|
|
2461
2501
|
brand: brandOverride ?? entry.brand ?? options?.brand ?? templateBrand ?? void 0,
|
|
2462
|
-
|
|
2502
|
+
buttonStyle: templateButtonStyle,
|
|
2463
2503
|
resolveMedia,
|
|
2464
2504
|
editKeyPrefix: `ai.${entry.id}`
|
|
2465
2505
|
}
|
|
@@ -21602,7 +21642,7 @@ function OhhwellsBridge() {
|
|
|
21602
21642
|
postToParent2({
|
|
21603
21643
|
type: "ow:ready",
|
|
21604
21644
|
version: "1",
|
|
21605
|
-
bridgeVersion: "0.1.
|
|
21645
|
+
bridgeVersion: "0.1.95",
|
|
21606
21646
|
path: pathname,
|
|
21607
21647
|
nodes: collectEditableNodes(editContentRef.current),
|
|
21608
21648
|
sections
|