@spectare-personalisation/react 0.2.1 → 0.4.0
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 +241 -52
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +167 -5
- package/dist/index.d.ts +167 -5
- package/dist/index.js +229 -53
- package/dist/index.js.map +1 -1
- package/dist/tokens.css +78 -0
- package/dist/tokens.css.map +1 -0
- package/dist/tokens.d.cts +2 -0
- package/dist/tokens.d.ts +2 -0
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -7,6 +7,7 @@ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
|
7
7
|
|
|
8
8
|
// src/types.ts
|
|
9
9
|
var COMPONENT_ZONES = {
|
|
10
|
+
AnnouncementBar: "announce",
|
|
10
11
|
HeroStatement: "hero",
|
|
11
12
|
ImageCtaHero: "hero",
|
|
12
13
|
AtomCard: "body",
|
|
@@ -24,23 +25,102 @@ function filterAssemblyByZone(assembly, zone) {
|
|
|
24
25
|
return effectiveZone === zone;
|
|
25
26
|
});
|
|
26
27
|
}
|
|
28
|
+
|
|
29
|
+
// src/tokens.ts
|
|
30
|
+
var DEFAULT_ACCENT = "#60a5fa";
|
|
31
|
+
var SP_ROOT = "sp-root";
|
|
32
|
+
function palette(accent) {
|
|
33
|
+
return {
|
|
34
|
+
"--sp-accent": accent,
|
|
35
|
+
"--sp-accent-bg": `color-mix(in srgb,${accent} 10%,transparent)`,
|
|
36
|
+
"--sp-accent-border": `color-mix(in srgb,${accent} 20%,transparent)`,
|
|
37
|
+
"--sp-bg": "#18181b",
|
|
38
|
+
"--sp-surface": "#09090b",
|
|
39
|
+
"--sp-border": "#27272a",
|
|
40
|
+
"--sp-text": "#f4f4f5",
|
|
41
|
+
"--sp-text-sub": "#d4d4d8",
|
|
42
|
+
"--sp-text-strong": "#e4e4e7",
|
|
43
|
+
"--sp-text-muted": "#82828b",
|
|
44
|
+
"--sp-text-dim": "#6b6b74",
|
|
45
|
+
"--sp-text-body": "#a1a1aa",
|
|
46
|
+
"--sp-positive": "#6ee7b7",
|
|
47
|
+
"--sp-cta-bg": "#fff",
|
|
48
|
+
"--sp-cta-text": "#09090b",
|
|
49
|
+
"--sp-divider": "rgba(255,255,255,0.06)",
|
|
50
|
+
"--sp-code-bg": "rgba(255,255,255,0.07)",
|
|
51
|
+
"--sp-code-border": "rgba(255,255,255,0.08)",
|
|
52
|
+
"--sp-pre-bg": "rgba(0,0,0,0.4)"
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function rules(unit) {
|
|
56
|
+
const gap = unit === "px" ? "20px" : "1.25rem";
|
|
57
|
+
return [
|
|
58
|
+
".sp-root .sp-prose p{margin-bottom:.625rem}",
|
|
59
|
+
".sp-root .sp-prose p:last-child{margin-bottom:0}",
|
|
60
|
+
".sp-root .sp-prose ul{list-style:disc;padding-left:1.25rem;margin-bottom:.625rem}",
|
|
61
|
+
".sp-root .sp-prose ol{list-style:decimal;padding-left:1.25rem;margin-bottom:.625rem}",
|
|
62
|
+
".sp-root .sp-prose li{margin-bottom:.25rem}",
|
|
63
|
+
".sp-root .sp-prose strong{color:var(--sp-text-strong);font-weight:600}",
|
|
64
|
+
".sp-root .sp-prose h2,.sp-root .sp-prose h3{color:var(--sp-text);font-weight:600;margin:.875rem 0 .375rem;font-size:1rem}",
|
|
65
|
+
".sp-root .sp-prose code{background:var(--sp-code-bg);border:1px solid var(--sp-code-border);padding:.1rem .35rem;border-radius:.25rem;font-size:.82em;color:var(--sp-text)}",
|
|
66
|
+
".sp-root .sp-prose blockquote{border-left:3px solid var(--sp-border);padding-left:1rem;color:var(--sp-text-muted);margin:.75rem 0}",
|
|
67
|
+
`.sp-root .sp-pair{display:grid;grid-template-columns:1fr 1fr;gap:${gap}}`,
|
|
68
|
+
"@media(max-width:640px){.sp-root .sp-pair{grid-template-columns:1fr}}"
|
|
69
|
+
].join("");
|
|
70
|
+
}
|
|
71
|
+
function buildTokenCss({ accent = DEFAULT_ACCENT, unit = "px", scope = `.${SP_ROOT}` } = {}) {
|
|
72
|
+
const decls = Object.entries(palette(accent)).map(([k, v]) => `${k}:${v}`).join(";");
|
|
73
|
+
return `${scope}{${decls}}${rules(unit)}`;
|
|
74
|
+
}
|
|
27
75
|
function renderMarkdown(content) {
|
|
28
76
|
const raw = marked.parse(content != null ? content : "", { async: false });
|
|
29
77
|
return DOMPurify.sanitize(raw, { USE_PROFILES: { html: true } });
|
|
30
78
|
}
|
|
31
79
|
var HALF_COMPONENTS = /* @__PURE__ */ new Set(["TestimonialCard"]);
|
|
32
|
-
|
|
33
|
-
|
|
80
|
+
var flexItems = (a) => a === "center" ? "center" : a === "right" ? "flex-end" : "flex-start";
|
|
81
|
+
function AnnouncementBar({ text, link }) {
|
|
82
|
+
if (!text) return null;
|
|
83
|
+
return /* @__PURE__ */ jsxs("div", { style: {
|
|
84
|
+
display: "flex",
|
|
85
|
+
alignItems: "center",
|
|
86
|
+
justifyContent: "center",
|
|
87
|
+
gap: "10px",
|
|
88
|
+
flexWrap: "wrap",
|
|
89
|
+
textAlign: "center",
|
|
90
|
+
background: "var(--sp-accent-bg)",
|
|
91
|
+
border: "1px solid var(--sp-accent-border)",
|
|
92
|
+
borderRadius: "8px",
|
|
93
|
+
padding: "8px 16px",
|
|
94
|
+
fontSize: "14px",
|
|
95
|
+
lineHeight: 1.4,
|
|
96
|
+
color: "var(--sp-text-sub)"
|
|
97
|
+
}, children: [
|
|
98
|
+
/* @__PURE__ */ jsx("span", { children: text }),
|
|
99
|
+
(link == null ? void 0 : link.href) && (link == null ? void 0 : link.label) && /* @__PURE__ */ jsxs("a", { href: link.href, style: { color: "var(--sp-accent)", fontWeight: 600, textDecoration: "none", whiteSpace: "nowrap" }, children: [
|
|
100
|
+
link.label,
|
|
101
|
+
" \u2192"
|
|
102
|
+
] })
|
|
103
|
+
] });
|
|
104
|
+
}
|
|
105
|
+
function HeroStatement({ headline, subheading, align = "left" }) {
|
|
106
|
+
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: flexItems(align), textAlign: align, paddingTop: "24px", paddingBottom: "24px" }, children: [
|
|
34
107
|
/* @__PURE__ */ jsx("h2", { style: { fontSize: "32px", fontWeight: 700, letterSpacing: "-0.025em", lineHeight: 1.2, color: "var(--sp-text)", margin: "0 0 12px" }, children: headline }),
|
|
35
108
|
subheading && /* @__PURE__ */ jsx("p", { style: { fontSize: "18px", color: "var(--sp-text-sub)", lineHeight: 1.6, maxWidth: "672px", margin: 0 }, children: subheading })
|
|
36
109
|
] });
|
|
37
110
|
}
|
|
38
111
|
var ATOM_LAYOUT_DEFAULT = ["title", "content", "stats", "cta"];
|
|
39
|
-
|
|
112
|
+
var ATOM_CLAMP_LINES = 4;
|
|
113
|
+
var clampStyle = {
|
|
114
|
+
display: "-webkit-box",
|
|
115
|
+
WebkitLineClamp: ATOM_CLAMP_LINES,
|
|
116
|
+
WebkitBoxOrient: "vertical",
|
|
117
|
+
overflow: "hidden"
|
|
118
|
+
};
|
|
119
|
+
function AtomCard({ title, content, layout, stats = [], ctaText, ctaUrl, full }) {
|
|
40
120
|
const order = layout && layout.length ? layout : ATOM_LAYOUT_DEFAULT;
|
|
41
121
|
const element = (name) => {
|
|
42
122
|
if (name === "title") return title ? /* @__PURE__ */ jsx("h3", { style: { fontSize: "16px", fontWeight: 600, color: "var(--sp-text)", margin: "0 0 12px" }, children: title }, "title") : null;
|
|
43
|
-
if (name === "content") return content ? /* @__PURE__ */ jsx("div", { className: "sp-prose", style: { fontSize: "15px", color: "var(--sp-text-sub)", lineHeight: 1.6 }, dangerouslySetInnerHTML: { __html: renderMarkdown(content) } }, "content") : null;
|
|
123
|
+
if (name === "content") return content ? /* @__PURE__ */ jsx("div", { className: "sp-prose", style: { fontSize: "15px", color: "var(--sp-text-sub)", lineHeight: 1.6, ...full ? {} : clampStyle }, dangerouslySetInnerHTML: { __html: renderMarkdown(content) } }, "content") : null;
|
|
44
124
|
if (name === "stats") return stats.length ? /* @__PURE__ */ jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: "20px", margin: "0 0 12px", paddingBottom: "12px", borderBottom: "1px solid var(--sp-border)" }, children: stats.map((s, i) => /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "2px" }, children: [
|
|
45
125
|
/* @__PURE__ */ jsx("span", { style: { fontSize: "22px", fontWeight: 700, color: "var(--sp-text)", lineHeight: 1, fontVariantNumeric: "tabular-nums" }, children: s.value }),
|
|
46
126
|
/* @__PURE__ */ jsx("span", { style: { fontSize: "11px", color: "var(--sp-text-muted)", textTransform: "uppercase", letterSpacing: "0.06em" }, children: s.label })
|
|
@@ -111,24 +191,24 @@ function TestimonialCard({ quote, name, role, company }) {
|
|
|
111
191
|
] })
|
|
112
192
|
] });
|
|
113
193
|
}
|
|
114
|
-
function ImageCtaHero({ src, alt = "", headline, subheading, ctaLabel, ctaHref }) {
|
|
194
|
+
function ImageCtaHero({ src, alt = "", headline, subheading, ctaLabel, ctaHref, align = "left" }) {
|
|
115
195
|
if (!src) return null;
|
|
116
196
|
return /* @__PURE__ */ jsxs("div", { style: { borderRadius: "12px", overflow: "hidden", position: "relative", width: "100%", paddingBottom: "56.25%" }, children: [
|
|
117
197
|
/* @__PURE__ */ jsx("img", { src, alt, style: { position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover" } }),
|
|
118
198
|
/* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, background: "rgba(0,0,0,0.45)" } }),
|
|
119
199
|
/* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, background: "linear-gradient(to top, rgba(0,0,0,0.85) 0%, rgba(0,0,0,0.2) 60%, transparent 100%)" } }),
|
|
120
|
-
/* @__PURE__ */ jsxs("div", { style: { position: "absolute", bottom: 0, left: 0, right: 0, padding: "28px" }, children: [
|
|
200
|
+
/* @__PURE__ */ jsxs("div", { style: { position: "absolute", bottom: 0, left: 0, right: 0, padding: "28px", textAlign: align }, children: [
|
|
121
201
|
headline && /* @__PURE__ */ jsx("h2", { style: { fontSize: "24px", fontWeight: 700, color: "#fff", lineHeight: 1.2, margin: "0 0 8px", letterSpacing: "-0.02em" }, children: headline }),
|
|
122
202
|
subheading && /* @__PURE__ */ jsx("p", { style: { fontSize: "15px", color: "rgba(255,255,255,0.75)", lineHeight: 1.5, margin: "0 0 20px" }, children: subheading }),
|
|
123
203
|
ctaHref && ctaLabel && /* @__PURE__ */ jsx("a", { href: ctaHref, style: { display: "inline-block", background: "#fff", color: "#09090b", padding: "8px 20px", borderRadius: "8px", fontWeight: 600, fontSize: "14px", textDecoration: "none" }, children: ctaLabel })
|
|
124
204
|
] })
|
|
125
205
|
] });
|
|
126
206
|
}
|
|
127
|
-
function CtaStrip({ headline, primary, secondary }) {
|
|
128
|
-
return /* @__PURE__ */ jsxs("div", { style: { border: "1px solid var(--sp-border)", borderRadius: "12px", padding: "32px", textAlign:
|
|
207
|
+
function CtaStrip({ headline, primary, secondary, align = "center" }) {
|
|
208
|
+
return /* @__PURE__ */ jsxs("div", { style: { border: "1px solid var(--sp-border)", borderRadius: "12px", padding: "32px", textAlign: align, background: "var(--sp-bg)" }, children: [
|
|
129
209
|
/* @__PURE__ */ jsx("h3", { style: { fontSize: "24px", fontWeight: 700, color: "var(--sp-text)", margin: "0 0 24px" }, children: headline }),
|
|
130
|
-
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent:
|
|
131
|
-
/* @__PURE__ */ jsx("a", { href: primary.href, style: { background: "
|
|
210
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: flexItems(align), gap: "16px", flexWrap: "wrap" }, children: [
|
|
211
|
+
/* @__PURE__ */ jsx("a", { href: primary.href, style: { background: "var(--sp-cta-bg)", color: "var(--sp-cta-text)", padding: "10px 24px", borderRadius: "12px", fontWeight: 600, fontSize: "15px", textDecoration: "none" }, children: primary.label }),
|
|
132
212
|
secondary && /* @__PURE__ */ jsxs("a", { href: secondary.href, style: { fontSize: "15px", color: "var(--sp-text-sub)", textDecoration: "none" }, children: [
|
|
133
213
|
secondary.label,
|
|
134
214
|
" \u2192"
|
|
@@ -136,14 +216,17 @@ function CtaStrip({ headline, primary, secondary }) {
|
|
|
136
216
|
] })
|
|
137
217
|
] });
|
|
138
218
|
}
|
|
139
|
-
function renderSingle(item, brand) {
|
|
219
|
+
function renderSingle(item, brand, align) {
|
|
140
220
|
var _a, _b;
|
|
141
221
|
const s = (_a = item.slots) != null ? _a : {};
|
|
142
222
|
switch (item.component) {
|
|
223
|
+
case "AnnouncementBar":
|
|
224
|
+
return /* @__PURE__ */ jsx(AnnouncementBar, { text: s.text, link: s.link });
|
|
143
225
|
case "HeroStatement":
|
|
144
|
-
return /* @__PURE__ */ jsx(HeroStatement, { headline: s.headline, subheading: s.subheading });
|
|
226
|
+
return /* @__PURE__ */ jsx(HeroStatement, { headline: s.headline, subheading: s.subheading, align });
|
|
227
|
+
// A full-width card has nothing beside it, so there is nothing for a clamp to protect.
|
|
145
228
|
case "AtomCard":
|
|
146
|
-
return /* @__PURE__ */ jsx(AtomCard, { title: s.title, content: s.content, layout: s.layout, stats: s.stats, ctaText: s.ctaText, ctaUrl: s.ctaUrl });
|
|
229
|
+
return /* @__PURE__ */ jsx(AtomCard, { title: s.title, content: s.content, layout: s.layout, stats: s.stats, ctaText: s.ctaText, ctaUrl: s.ctaUrl, full: s.full || resolvedSize(item) === "full" });
|
|
147
230
|
case "StatGrid":
|
|
148
231
|
return /* @__PURE__ */ jsx(StatGrid, { stats: s.stats, caption: s.caption });
|
|
149
232
|
case "CodeBlock":
|
|
@@ -155,11 +238,23 @@ function renderSingle(item, brand) {
|
|
|
155
238
|
case "TestimonialCard":
|
|
156
239
|
return /* @__PURE__ */ jsx(TestimonialCard, { quote: s.quote, name: s.name, role: s.role, company: s.company });
|
|
157
240
|
case "ImageCtaHero":
|
|
158
|
-
return /* @__PURE__ */ jsx(ImageCtaHero, { src: s.src, alt: s.alt, headline: s.headline, subheading: s.subheading, ctaLabel: s.ctaLabel, ctaHref: s.ctaHref });
|
|
241
|
+
return /* @__PURE__ */ jsx(ImageCtaHero, { src: s.src, alt: s.alt, headline: s.headline, subheading: s.subheading, ctaLabel: s.ctaLabel, ctaHref: s.ctaHref, align });
|
|
159
242
|
case "CtaStrip":
|
|
160
|
-
return /* @__PURE__ */ jsx(CtaStrip, { headline: s.headline, primary: s.primary, secondary: s.secondary });
|
|
161
|
-
|
|
162
|
-
|
|
243
|
+
return /* @__PURE__ */ jsx(CtaStrip, { headline: s.headline, primary: s.primary, secondary: s.secondary, align });
|
|
244
|
+
// An unrecognised component name used to render nothing at all. A component needs a
|
|
245
|
+
// renderer in three places plus the WordPress plugin, so missing one made its content
|
|
246
|
+
// silently absent on that surface only: no error, no warning, just a shorter page that
|
|
247
|
+
// looks deliberate. That is the failure mode to remove before the registry grows.
|
|
248
|
+
//
|
|
249
|
+
// Falls back to AtomCard when the slots carry something readable, since almost every
|
|
250
|
+
// component is built from an atom and title/content is the shape they share. When there
|
|
251
|
+
// is genuinely nothing to show it still renders nothing, because an empty card is worse
|
|
252
|
+
// than a gap, but the warning fires either way.
|
|
253
|
+
default: {
|
|
254
|
+
console.warn(`[spectare] ComponentRenderer: no renderer for "${item.component}", falling back to AtomCard`);
|
|
255
|
+
if (!s.title && !s.content) return null;
|
|
256
|
+
return /* @__PURE__ */ jsx(AtomCard, { title: s.title, content: s.content, stats: s.stats, ctaText: s.ctaText, ctaUrl: s.ctaUrl, full: true });
|
|
257
|
+
}
|
|
163
258
|
}
|
|
164
259
|
}
|
|
165
260
|
function resolvedSize(item) {
|
|
@@ -191,39 +286,51 @@ function XrayBadge({ name, size }) {
|
|
|
191
286
|
/* @__PURE__ */ jsx("span", { style: { fontSize: "10px", fontFamily: "monospace", color: "#71717a", lineHeight: 1 }, children: size })
|
|
192
287
|
] });
|
|
193
288
|
}
|
|
194
|
-
|
|
195
|
-
function ComponentRenderer({ assembly, brand, xray = false, zone }) {
|
|
289
|
+
function ComponentRenderer({ assembly, brand, xray = false, zone, align, accent }) {
|
|
196
290
|
const filtered = zone ? filterAssemblyByZone(assembly, zone) : assembly;
|
|
197
291
|
const layout = buildLayout(filtered);
|
|
198
|
-
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "20px", fontSize: "16px" }, children: [
|
|
199
|
-
/* @__PURE__ */ jsx("style", { dangerouslySetInnerHTML: { __html:
|
|
292
|
+
return /* @__PURE__ */ jsxs("div", { className: SP_ROOT, style: { display: "flex", flexDirection: "column", gap: "20px", fontSize: "16px" }, children: [
|
|
293
|
+
/* @__PURE__ */ jsx("style", { dangerouslySetInnerHTML: { __html: buildTokenCss({ accent }) } }),
|
|
200
294
|
layout.map((group, i) => {
|
|
201
295
|
if (group.type === "full") {
|
|
202
296
|
return /* @__PURE__ */ jsxs("div", { style: xray ? { position: "relative" } : void 0, children: [
|
|
203
297
|
xray && /* @__PURE__ */ jsx(XrayBadge, { name: group.item.component, size: "full" }),
|
|
204
|
-
renderSingle(group.item, brand)
|
|
298
|
+
renderSingle(group.item, brand, align)
|
|
205
299
|
] }, i);
|
|
206
300
|
}
|
|
207
301
|
if (group.type === "pair") {
|
|
208
302
|
return /* @__PURE__ */ jsxs("div", { className: "sp-pair", children: [
|
|
209
303
|
/* @__PURE__ */ jsxs("div", { style: xray ? { position: "relative" } : void 0, children: [
|
|
210
304
|
xray && /* @__PURE__ */ jsx(XrayBadge, { name: group.items[0].component, size: "half" }),
|
|
211
|
-
renderSingle(group.items[0], brand)
|
|
305
|
+
renderSingle(group.items[0], brand, align)
|
|
212
306
|
] }),
|
|
213
307
|
/* @__PURE__ */ jsxs("div", { style: xray ? { position: "relative" } : void 0, children: [
|
|
214
308
|
xray && /* @__PURE__ */ jsx(XrayBadge, { name: group.items[1].component, size: "half" }),
|
|
215
|
-
renderSingle(group.items[1], brand)
|
|
309
|
+
renderSingle(group.items[1], brand, align)
|
|
216
310
|
] })
|
|
217
311
|
] }, i);
|
|
218
312
|
}
|
|
219
313
|
return /* @__PURE__ */ jsxs("div", { style: xray ? { position: "relative" } : void 0, children: [
|
|
220
314
|
xray && /* @__PURE__ */ jsx(XrayBadge, { name: group.item.component, size: "full" }),
|
|
221
|
-
renderSingle(group.item, brand)
|
|
315
|
+
renderSingle(group.item, brand, align)
|
|
222
316
|
] }, i);
|
|
223
317
|
})
|
|
224
318
|
] });
|
|
225
319
|
}
|
|
226
|
-
var
|
|
320
|
+
var VARIANT_KEY = (orgSlug) => `_spectare_variant_${orgSlug}`;
|
|
321
|
+
function readVariantId(orgSlug) {
|
|
322
|
+
try {
|
|
323
|
+
return sessionStorage.getItem(VARIANT_KEY(orgSlug));
|
|
324
|
+
} catch (e) {
|
|
325
|
+
return null;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
function writeVariantId(orgSlug, variantId) {
|
|
329
|
+
try {
|
|
330
|
+
sessionStorage.setItem(VARIANT_KEY(orgSlug), variantId);
|
|
331
|
+
} catch (e) {
|
|
332
|
+
}
|
|
333
|
+
}
|
|
227
334
|
function buildSummary(pageHint, orgSlug) {
|
|
228
335
|
const parts = [pageHint];
|
|
229
336
|
const referrer = document.referrer;
|
|
@@ -275,31 +382,39 @@ function PersonalisedSection({
|
|
|
275
382
|
style,
|
|
276
383
|
initialAssembly,
|
|
277
384
|
initialIntent,
|
|
278
|
-
|
|
385
|
+
initialVariantId,
|
|
386
|
+
directSummary,
|
|
387
|
+
zone,
|
|
388
|
+
align,
|
|
389
|
+
accent
|
|
279
390
|
}) {
|
|
280
391
|
const [assembly, setAssembly] = useState(initialAssembly != null ? initialAssembly : []);
|
|
281
392
|
const [loading, setLoading] = useState(!initialAssembly);
|
|
282
393
|
const [orgName, setOrgName] = useState(void 0);
|
|
394
|
+
const [debugMode, setDebugMode] = useState(false);
|
|
395
|
+
const [debugIntent, setDebugIntent] = useState(null);
|
|
283
396
|
const tokenRef = useRef(null);
|
|
284
397
|
const conversionFiredRef = useRef(false);
|
|
398
|
+
useEffect(() => {
|
|
399
|
+
setDebugMode(new URLSearchParams(window.location.search).has("spectare_debug"));
|
|
400
|
+
if (initialIntent) setDebugIntent(initialIntent);
|
|
401
|
+
}, [initialIntent]);
|
|
402
|
+
useEffect(() => {
|
|
403
|
+
if (initialVariantId) writeVariantId(orgSlug, initialVariantId);
|
|
404
|
+
}, [orgSlug, initialVariantId]);
|
|
285
405
|
useEffect(() => {
|
|
286
406
|
let cancelled = false;
|
|
287
407
|
async function load() {
|
|
288
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
408
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
289
409
|
try {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
}).filter(Boolean).join("_");
|
|
295
|
-
const tokenKey = `_spectare_token_${orgSlug}${utmSuffix ? "_" + utmSuffix : ""}`;
|
|
296
|
-
let token = sessionStorage.getItem(tokenKey);
|
|
297
|
-
if (!token) {
|
|
298
|
-
const summary = buildSummary(pageHint != null ? pageHint : `Visiting ${document.title}`, orgSlug);
|
|
410
|
+
let token = null;
|
|
411
|
+
if (directSummary) {
|
|
412
|
+
setLoading(true);
|
|
413
|
+
setAssembly([]);
|
|
299
414
|
const res2 = await fetch(`${baseUrl}/api/qualify/${orgSlug}`, {
|
|
300
415
|
method: "POST",
|
|
301
416
|
headers: { "Content-Type": "application/json" },
|
|
302
|
-
body: JSON.stringify({ summary })
|
|
417
|
+
body: JSON.stringify({ summary: directSummary, direct: true })
|
|
303
418
|
});
|
|
304
419
|
if (!res2.ok || cancelled) {
|
|
305
420
|
setLoading(false);
|
|
@@ -307,8 +422,30 @@ function PersonalisedSection({
|
|
|
307
422
|
}
|
|
308
423
|
const data = await res2.json();
|
|
309
424
|
token = (_a = data.token) != null ? _a : null;
|
|
310
|
-
|
|
311
|
-
|
|
425
|
+
} else {
|
|
426
|
+
const p = new URLSearchParams(window.location.search);
|
|
427
|
+
const utmSuffix = ["utm_source", "utm_medium", "utm_campaign", "utm_term"].map((k) => {
|
|
428
|
+
var _a2;
|
|
429
|
+
return (_a2 = p.get(k)) != null ? _a2 : "";
|
|
430
|
+
}).filter(Boolean).join("_");
|
|
431
|
+
const tokenKey = `_spectare_token_${orgSlug}${utmSuffix ? "_" + utmSuffix : ""}`;
|
|
432
|
+
token = sessionStorage.getItem(tokenKey);
|
|
433
|
+
if (!token) {
|
|
434
|
+
const summary = buildSummary(pageHint != null ? pageHint : `Visiting ${document.title}`, orgSlug);
|
|
435
|
+
const res2 = await fetch(`${baseUrl}/api/qualify/${orgSlug}`, {
|
|
436
|
+
method: "POST",
|
|
437
|
+
headers: { "Content-Type": "application/json" },
|
|
438
|
+
body: JSON.stringify({ summary })
|
|
439
|
+
});
|
|
440
|
+
if (!res2.ok || cancelled) {
|
|
441
|
+
setLoading(false);
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
const data = await res2.json();
|
|
445
|
+
token = (_b = data.token) != null ? _b : null;
|
|
446
|
+
if (token) sessionStorage.setItem(tokenKey, token);
|
|
447
|
+
if (data.orgName) setOrgName(data.orgName);
|
|
448
|
+
}
|
|
312
449
|
}
|
|
313
450
|
if (!token || cancelled) {
|
|
314
451
|
setLoading(false);
|
|
@@ -318,9 +455,19 @@ function PersonalisedSection({
|
|
|
318
455
|
let res = await fetch(`${baseUrl}/api/assemble/components`, {
|
|
319
456
|
method: "POST",
|
|
320
457
|
headers: { "Content-Type": "application/json" },
|
|
321
|
-
|
|
458
|
+
// Direct: flag it so the server cost-caps the build, and omit the seen-variant
|
|
459
|
+
// exclusion so it can read the shared cache instead of forcing a fresh build.
|
|
460
|
+
body: JSON.stringify(
|
|
461
|
+
directSummary ? { token, orgSlug, direct: true } : { token, orgSlug, excludeVariantId: readVariantId(orgSlug) }
|
|
462
|
+
)
|
|
322
463
|
});
|
|
323
|
-
if (res.status === 401 && !cancelled) {
|
|
464
|
+
if (res.status === 401 && !cancelled && !directSummary) {
|
|
465
|
+
const p = new URLSearchParams(window.location.search);
|
|
466
|
+
const utmSuffix = ["utm_source", "utm_medium", "utm_campaign", "utm_term"].map((k) => {
|
|
467
|
+
var _a2;
|
|
468
|
+
return (_a2 = p.get(k)) != null ? _a2 : "";
|
|
469
|
+
}).filter(Boolean).join("_");
|
|
470
|
+
const tokenKey = `_spectare_token_${orgSlug}${utmSuffix ? "_" + utmSuffix : ""}`;
|
|
324
471
|
sessionStorage.removeItem(tokenKey);
|
|
325
472
|
const summary = buildSummary(pageHint != null ? pageHint : `Visiting ${document.title}`, orgSlug);
|
|
326
473
|
const qRes = await fetch(`${baseUrl}/api/qualify/${orgSlug}`, {
|
|
@@ -333,7 +480,7 @@ function PersonalisedSection({
|
|
|
333
480
|
return;
|
|
334
481
|
}
|
|
335
482
|
const qData = await qRes.json();
|
|
336
|
-
token = (
|
|
483
|
+
token = (_c = qData.token) != null ? _c : null;
|
|
337
484
|
if (token) {
|
|
338
485
|
sessionStorage.setItem(tokenKey, token);
|
|
339
486
|
tokenRef.current = token;
|
|
@@ -345,14 +492,14 @@ function PersonalisedSection({
|
|
|
345
492
|
res = await fetch(`${baseUrl}/api/assemble/components`, {
|
|
346
493
|
method: "POST",
|
|
347
494
|
headers: { "Content-Type": "application/json" },
|
|
348
|
-
body: JSON.stringify({ token, orgSlug })
|
|
495
|
+
body: JSON.stringify({ token, orgSlug, excludeVariantId: readVariantId(orgSlug) })
|
|
349
496
|
});
|
|
350
497
|
}
|
|
351
498
|
if (!res.ok || cancelled) {
|
|
352
499
|
setLoading(false);
|
|
353
500
|
return;
|
|
354
501
|
}
|
|
355
|
-
const contentType = (
|
|
502
|
+
const contentType = (_d = res.headers.get("content-type")) != null ? _d : "";
|
|
356
503
|
if (contentType.includes("text/event-stream")) {
|
|
357
504
|
const reader = res.body.getReader();
|
|
358
505
|
const decoder = new TextDecoder();
|
|
@@ -364,13 +511,26 @@ function PersonalisedSection({
|
|
|
364
511
|
if (done || cancelled) break;
|
|
365
512
|
buf += decoder.decode(value, { stream: true });
|
|
366
513
|
const lines = buf.split("\n");
|
|
367
|
-
buf = (
|
|
514
|
+
buf = (_e = lines.pop()) != null ? _e : "";
|
|
368
515
|
for (const line of lines) {
|
|
369
516
|
if (!line.startsWith("data: ")) continue;
|
|
370
517
|
const payload = line.slice(6).trim();
|
|
371
518
|
if (payload === "[DONE]") break;
|
|
372
519
|
try {
|
|
373
|
-
const
|
|
520
|
+
const parsed = JSON.parse(payload);
|
|
521
|
+
if (parsed._variant && typeof parsed._variant === "string") {
|
|
522
|
+
writeVariantId(orgSlug, parsed._variant);
|
|
523
|
+
continue;
|
|
524
|
+
}
|
|
525
|
+
if (parsed._error) {
|
|
526
|
+
if (!loadingCleared) {
|
|
527
|
+
setLoading(false);
|
|
528
|
+
loadingCleared = true;
|
|
529
|
+
}
|
|
530
|
+
continue;
|
|
531
|
+
}
|
|
532
|
+
if (!parsed.component) continue;
|
|
533
|
+
const comp = parsed;
|
|
374
534
|
if (!loadingCleared) {
|
|
375
535
|
setLoading(false);
|
|
376
536
|
loadingCleared = true;
|
|
@@ -386,10 +546,14 @@ function PersonalisedSection({
|
|
|
386
546
|
}
|
|
387
547
|
} else {
|
|
388
548
|
const data = await res.json();
|
|
549
|
+
if (data.variantId) writeVariantId(orgSlug, data.variantId);
|
|
389
550
|
if (cancelled) return;
|
|
390
|
-
const clientAudience = (
|
|
391
|
-
const clientStage = (
|
|
551
|
+
const clientAudience = (_g = (_f = data.intent) == null ? void 0 : _f.userContext) == null ? void 0 : _g.audience;
|
|
552
|
+
const clientStage = (_i = (_h = data.intent) == null ? void 0 : _h.userContext) == null ? void 0 : _i.stage;
|
|
392
553
|
const ssrMatches = initialIntent && clientAudience === initialIntent.audience && clientStage === initialIntent.stage;
|
|
554
|
+
if (clientAudience && clientStage) {
|
|
555
|
+
setDebugIntent({ audience: clientAudience, stage: clientStage });
|
|
556
|
+
}
|
|
393
557
|
if (!ssrMatches && Array.isArray(data.assembly) && data.assembly.length > 0) {
|
|
394
558
|
setAssembly(data.assembly);
|
|
395
559
|
}
|
|
@@ -403,7 +567,7 @@ function PersonalisedSection({
|
|
|
403
567
|
return () => {
|
|
404
568
|
cancelled = true;
|
|
405
569
|
};
|
|
406
|
-
}, [orgSlug, baseUrl, pageHint, initialAssembly, initialIntent]);
|
|
570
|
+
}, [orgSlug, baseUrl, pageHint, initialAssembly, initialIntent, directSummary]);
|
|
407
571
|
useEffect(() => {
|
|
408
572
|
if (!assembly.length || conversionFiredRef.current) return;
|
|
409
573
|
const token = tokenRef.current;
|
|
@@ -448,6 +612,7 @@ function PersonalisedSection({
|
|
|
448
612
|
token,
|
|
449
613
|
orgSlug,
|
|
450
614
|
url,
|
|
615
|
+
variantId: readVariantId(orgSlug),
|
|
451
616
|
...atomsShown.length ? { atomsShown } : {},
|
|
452
617
|
...visitPath.length > 1 ? { visitPath } : {},
|
|
453
618
|
...data.goalName ? { goalName: data.goalName } : {},
|
|
@@ -459,13 +624,24 @@ function PersonalisedSection({
|
|
|
459
624
|
});
|
|
460
625
|
}, [assembly, orgSlug, baseUrl]);
|
|
461
626
|
if (!loading && assembly.length === 0) return null;
|
|
627
|
+
const debugStyle = debugMode ? { outline: "2px solid rgba(96,165,250,0.5)", borderRadius: "0.5rem", position: "relative", padding: "0.5rem", ...style } : style;
|
|
462
628
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
463
|
-
/* @__PURE__ */ jsx("style", { dangerouslySetInnerHTML: { __html: CSS_TOKENS } }),
|
|
464
629
|
/* @__PURE__ */ jsx("style", { dangerouslySetInnerHTML: { __html: "@keyframes sp-pulse{0%,100%{opacity:1}50%{opacity:0.4}}" } }),
|
|
465
|
-
/* @__PURE__ */
|
|
630
|
+
/* @__PURE__ */ jsxs("div", { className, style: debugStyle, children: [
|
|
631
|
+
debugMode && /* @__PURE__ */ jsxs("div", { style: { position: "absolute", top: "-1px", right: "-1px", zIndex: 20, display: "flex", alignItems: "center", gap: "0.375rem", background: "rgba(96,165,250,0.9)", borderRadius: "0 0.5rem 0 0.375rem", padding: "0.25rem 0.625rem", fontSize: "0.625rem", fontFamily: "monospace", color: "#fff", pointerEvents: "none", userSelect: "none" }, children: [
|
|
632
|
+
"Personalised by Spectare",
|
|
633
|
+
debugIntent && /* @__PURE__ */ jsxs("span", { style: { opacity: 0.75 }, children: [
|
|
634
|
+
" ",
|
|
635
|
+
debugIntent.audience,
|
|
636
|
+
"/",
|
|
637
|
+
debugIntent.stage
|
|
638
|
+
] })
|
|
639
|
+
] }),
|
|
640
|
+
loading && assembly.length === 0 ? /* @__PURE__ */ jsx(Shimmer, {}) : /* @__PURE__ */ jsx(ComponentRenderer, { assembly: zone ? filterAssemblyByZone(assembly, zone) : assembly, brand: brand != null ? brand : orgName, align, accent })
|
|
641
|
+
] })
|
|
466
642
|
] });
|
|
467
643
|
}
|
|
468
644
|
|
|
469
|
-
export { ComponentRenderer, PersonalisedSection, filterAssemblyByZone };
|
|
645
|
+
export { AnnouncementBar, AtomCard, CodeBlock, ComparisonTable, ComponentRenderer, CtaStrip, DEFAULT_ACCENT, FeatureList, HeroStatement, ImageCtaHero, PersonalisedSection, SP_ROOT, StatGrid, TestimonialCard, buildTokenCss, filterAssemblyByZone };
|
|
470
646
|
//# sourceMappingURL=index.js.map
|
|
471
647
|
//# sourceMappingURL=index.js.map
|