@sentientui/react 0.5.0 → 0.7.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/README.md +17 -3
- package/dist/index.d.cts +11 -2
- package/dist/index.d.ts +11 -2
- package/dist/index.js +1 -514
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -478
- package/dist/index.mjs.map +1 -1
- package/dist/next/adaptive-root-client.js +1 -82
- package/dist/next/adaptive-root-client.js.map +1 -1
- package/dist/next/adaptive-root.d.ts +2 -1
- package/dist/next/adaptive-root.js +1 -152
- package/dist/next/adaptive-root.js.map +1 -1
- package/dist/server.d.cts +4 -4
- package/dist/server.d.ts +4 -4
- package/dist/server.js +1 -87
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +1 -51
- package/dist/server.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ SENTIENT_API_KEY=pk_your_key
|
|
|
26
26
|
NEXT_PUBLIC_SENTIENT_API_KEY=pk_your_key
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
The SDK points at `https://sentient-
|
|
29
|
+
The SDK points at `https://api.sentient-ui.com` (the hosted API) automatically — no ingest URL configuration required.
|
|
30
30
|
|
|
31
31
|
## Quick start — Next.js App Router (recommended)
|
|
32
32
|
|
|
@@ -134,6 +134,8 @@ Accepts the same `apiKey`, `context`, `consent`, `ssrFallback`, `onAssignment`,
|
|
|
134
134
|
| `id` | `string` | Unique component identifier within your project. |
|
|
135
135
|
| `variants` | `Record<string, ReactNode>` | Map of variant ID → content. Any two or more keys; the bandit explores them all. |
|
|
136
136
|
| `goal` | `string \| GoalConfig` | Conversion goal. A string is a click-goal label; an object is an explicit `GoalConfig`. |
|
|
137
|
+
| `agentDataByVariant` | `Record<string, unknown>` *(optional)* | Structured data keyed by variant ID that AI agents can consume via `GET /v1/agent/layout`. Only the assigned variant's entry is sent to the server. Preferred over `agentData`. |
|
|
138
|
+
| `agentData` | `unknown` *(optional, deprecated)* | Deprecated in favour of `agentDataByVariant`. Single value stored once regardless of which variant is shown; kept for backward compatibility. |
|
|
137
139
|
| `clientOnly` | `boolean` | Render nothing on the server; resolve on the client only. Use for cookie-dependent slots. |
|
|
138
140
|
|
|
139
141
|
#### Goal types
|
|
@@ -159,9 +161,21 @@ goal={{
|
|
|
159
161
|
{ type: 'click' },
|
|
160
162
|
],
|
|
161
163
|
}}
|
|
164
|
+
|
|
165
|
+
// Weighted composite — each step fires immediately as it completes, with a fractional reward.
|
|
166
|
+
// Use for multi-step funnels where partial completion still signals quality.
|
|
167
|
+
// Steps are independent: step 2 can fire before step 1.
|
|
168
|
+
goal={{
|
|
169
|
+
type: 'weighted_composite',
|
|
170
|
+
steps: [
|
|
171
|
+
{ goal: { type: 'scroll_depth', threshold: 0.5 }, name: 'viewed_pricing', weight: 0.2 },
|
|
172
|
+
{ goal: { type: 'click' }, name: 'clicked_cta', weight: 0.4 },
|
|
173
|
+
{ goal: { type: 'form_submit' }, name: 'signed_up', weight: 1.0 },
|
|
174
|
+
],
|
|
175
|
+
}}
|
|
162
176
|
```
|
|
163
177
|
|
|
164
|
-
Each goal fires at most once per variant mount.
|
|
178
|
+
Each goal fires at most once per variant mount. `WeightedCompositeGoal` fires each step's reward independently; `CompositeGoal` waits for all sub-goals and fires reward `1.0` once.
|
|
165
179
|
|
|
166
180
|
### `<AdaptiveText>`
|
|
167
181
|
|
|
@@ -221,7 +235,7 @@ export async function getServerSideProps({ req }) {
|
|
|
221
235
|
{
|
|
222
236
|
cookies: req.cookies,
|
|
223
237
|
apiKey: process.env.SENTIENT_API_KEY!,
|
|
224
|
-
baseUrl: 'https://sentient-
|
|
238
|
+
baseUrl: 'https://api.sentient-ui.com/v1',
|
|
225
239
|
origin: process.env.NEXT_PUBLIC_APP_URL,
|
|
226
240
|
},
|
|
227
241
|
);
|
package/dist/index.d.cts
CHANGED
|
@@ -88,7 +88,16 @@ type CompositeGoal = {
|
|
|
88
88
|
type: 'composite';
|
|
89
89
|
all: GoalConfig[];
|
|
90
90
|
};
|
|
91
|
-
type
|
|
91
|
+
type WeightedStep = {
|
|
92
|
+
goal: GoalConfig;
|
|
93
|
+
name: string;
|
|
94
|
+
weight: number;
|
|
95
|
+
};
|
|
96
|
+
type WeightedCompositeGoal = {
|
|
97
|
+
type: 'weighted_composite';
|
|
98
|
+
steps: WeightedStep[];
|
|
99
|
+
};
|
|
100
|
+
type GoalConfig = ScrollDepthGoal | ClickGoal | FormSubmitGoal | CompositeGoal | WeightedCompositeGoal;
|
|
92
101
|
type AdaptiveProps = {
|
|
93
102
|
id: string;
|
|
94
103
|
variants: Record<string, ReactNode>;
|
|
@@ -171,4 +180,4 @@ declare function update(componentId: string, weights: ComponentWeights): void;
|
|
|
171
180
|
*/
|
|
172
181
|
declare function getWeights(componentId: string): ComponentWeights | null;
|
|
173
182
|
|
|
174
|
-
export { Adaptive, type AdaptiveProps, AdaptiveProvider, type AdaptiveProviderProps, AdaptiveText, type AdaptiveTextProps, type AssignmentState, type ClickGoal, type ComponentWeights, type CompositeGoal, type FormSubmitGoal, type GoalConfig, type ScrollDepthGoal, type SsrFallback, type VariantWeight, getWeights, subscribe as subscribeWeights, update as updateWeights, useAssignment, useInitialAssignments, useLayoutOrder, useSentient };
|
|
183
|
+
export { Adaptive, type AdaptiveProps, AdaptiveProvider, type AdaptiveProviderProps, AdaptiveText, type AdaptiveTextProps, type AssignmentState, type ClickGoal, type ComponentWeights, type CompositeGoal, type FormSubmitGoal, type GoalConfig, type ScrollDepthGoal, type SsrFallback, type VariantWeight, type WeightedCompositeGoal, type WeightedStep, getWeights, subscribe as subscribeWeights, update as updateWeights, useAssignment, useInitialAssignments, useLayoutOrder, useSentient };
|
package/dist/index.d.ts
CHANGED
|
@@ -88,7 +88,16 @@ type CompositeGoal = {
|
|
|
88
88
|
type: 'composite';
|
|
89
89
|
all: GoalConfig[];
|
|
90
90
|
};
|
|
91
|
-
type
|
|
91
|
+
type WeightedStep = {
|
|
92
|
+
goal: GoalConfig;
|
|
93
|
+
name: string;
|
|
94
|
+
weight: number;
|
|
95
|
+
};
|
|
96
|
+
type WeightedCompositeGoal = {
|
|
97
|
+
type: 'weighted_composite';
|
|
98
|
+
steps: WeightedStep[];
|
|
99
|
+
};
|
|
100
|
+
type GoalConfig = ScrollDepthGoal | ClickGoal | FormSubmitGoal | CompositeGoal | WeightedCompositeGoal;
|
|
92
101
|
type AdaptiveProps = {
|
|
93
102
|
id: string;
|
|
94
103
|
variants: Record<string, ReactNode>;
|
|
@@ -171,4 +180,4 @@ declare function update(componentId: string, weights: ComponentWeights): void;
|
|
|
171
180
|
*/
|
|
172
181
|
declare function getWeights(componentId: string): ComponentWeights | null;
|
|
173
182
|
|
|
174
|
-
export { Adaptive, type AdaptiveProps, AdaptiveProvider, type AdaptiveProviderProps, AdaptiveText, type AdaptiveTextProps, type AssignmentState, type ClickGoal, type ComponentWeights, type CompositeGoal, type FormSubmitGoal, type GoalConfig, type ScrollDepthGoal, type SsrFallback, type VariantWeight, getWeights, subscribe as subscribeWeights, update as updateWeights, useAssignment, useInitialAssignments, useLayoutOrder, useSentient };
|
|
183
|
+
export { Adaptive, type AdaptiveProps, AdaptiveProvider, type AdaptiveProviderProps, AdaptiveText, type AdaptiveTextProps, type AssignmentState, type ClickGoal, type ComponentWeights, type CompositeGoal, type FormSubmitGoal, type GoalConfig, type ScrollDepthGoal, type SsrFallback, type VariantWeight, type WeightedCompositeGoal, type WeightedStep, getWeights, subscribe as subscribeWeights, update as updateWeights, useAssignment, useInitialAssignments, useLayoutOrder, useSentient };
|
package/dist/index.js
CHANGED
|
@@ -1,516 +1,3 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
"use strict";
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
-
var __export = (target, all) => {
|
|
8
|
-
for (var name in all)
|
|
9
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
-
};
|
|
11
|
-
var __copyProps = (to, from, except, desc) => {
|
|
12
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
-
for (let key of __getOwnPropNames(from))
|
|
14
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
-
}
|
|
17
|
-
return to;
|
|
18
|
-
};
|
|
19
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
-
|
|
21
|
-
// src/index.ts
|
|
22
|
-
var src_exports = {};
|
|
23
|
-
__export(src_exports, {
|
|
24
|
-
Adaptive: () => Adaptive,
|
|
25
|
-
AdaptiveProvider: () => AdaptiveProvider,
|
|
26
|
-
AdaptiveText: () => AdaptiveText,
|
|
27
|
-
detectSegment: () => import_core2.deriveSessionSegment,
|
|
28
|
-
getWeights: () => getWeights,
|
|
29
|
-
subscribeWeights: () => subscribe,
|
|
30
|
-
updateWeights: () => update,
|
|
31
|
-
useAssignment: () => useAssignment,
|
|
32
|
-
useInitialAssignments: () => useInitialAssignments,
|
|
33
|
-
useLayoutOrder: () => useLayoutOrder,
|
|
34
|
-
useSentient: () => useSentient
|
|
35
|
-
});
|
|
36
|
-
module.exports = __toCommonJS(src_exports);
|
|
37
|
-
|
|
38
|
-
// src/provider.tsx
|
|
39
|
-
var import_react = require("react");
|
|
40
|
-
var import_core = require("@sentientui/core");
|
|
41
|
-
var import_jsx_runtime = require("react/jsx-runtime");
|
|
42
|
-
var AdaptiveContext = (0, import_react.createContext)({
|
|
43
|
-
client: null,
|
|
44
|
-
apiKey: "",
|
|
45
|
-
initialAssignments: {},
|
|
46
|
-
sessionSegment: "desktop:direct",
|
|
47
|
-
ssrFallback: "first",
|
|
48
|
-
onAssignment: void 0,
|
|
49
|
-
initialLayoutOrder: null
|
|
50
|
-
});
|
|
51
|
-
function AdaptiveProvider(props) {
|
|
52
|
-
var _a, _b, _c, _d;
|
|
53
|
-
const [client, setClient] = (0, import_react.useState)(null);
|
|
54
|
-
(0, import_react.useEffect)(() => {
|
|
55
|
-
if (props.consent === false && !props.preConsentBehavior) {
|
|
56
|
-
setClient((prev) => {
|
|
57
|
-
prev == null ? void 0 : prev.destroy();
|
|
58
|
-
return null;
|
|
59
|
-
});
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
const c = (0, import_core.init)({
|
|
63
|
-
apiKey: props.apiKey,
|
|
64
|
-
context: props.context,
|
|
65
|
-
debug: props.debug,
|
|
66
|
-
initialAssignments: props.initialAssignments,
|
|
67
|
-
sessionSegment: props.sessionSegment,
|
|
68
|
-
consent: props.consent,
|
|
69
|
-
preConsentBehavior: props.preConsentBehavior
|
|
70
|
-
});
|
|
71
|
-
setClient(c);
|
|
72
|
-
return () => {
|
|
73
|
-
c.destroy();
|
|
74
|
-
};
|
|
75
|
-
}, [props.consent]);
|
|
76
|
-
const ssrFallback = (_a = props.ssrFallback) != null ? _a : "first";
|
|
77
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
78
|
-
AdaptiveContext.Provider,
|
|
79
|
-
{
|
|
80
|
-
value: {
|
|
81
|
-
client,
|
|
82
|
-
apiKey: props.apiKey,
|
|
83
|
-
initialAssignments: (_b = props.initialAssignments) != null ? _b : {},
|
|
84
|
-
sessionSegment: (_c = props.sessionSegment) != null ? _c : "desktop:direct",
|
|
85
|
-
ssrFallback,
|
|
86
|
-
onAssignment: props.onAssignment,
|
|
87
|
-
initialLayoutOrder: (_d = props.initialLayoutOrder) != null ? _d : null
|
|
88
|
-
},
|
|
89
|
-
children: props.children
|
|
90
|
-
}
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
function useSentient() {
|
|
94
|
-
return (0, import_react.useContext)(AdaptiveContext).client;
|
|
95
|
-
}
|
|
96
|
-
function useAdaptiveApiKey() {
|
|
97
|
-
return (0, import_react.useContext)(AdaptiveContext).apiKey;
|
|
98
|
-
}
|
|
99
|
-
function useInitialAssignments() {
|
|
100
|
-
return (0, import_react.useContext)(AdaptiveContext).initialAssignments;
|
|
101
|
-
}
|
|
102
|
-
function useSessionSegment() {
|
|
103
|
-
return (0, import_react.useContext)(AdaptiveContext).sessionSegment;
|
|
104
|
-
}
|
|
105
|
-
function useSsrFallback() {
|
|
106
|
-
return (0, import_react.useContext)(AdaptiveContext).ssrFallback;
|
|
107
|
-
}
|
|
108
|
-
function useOnAssignment() {
|
|
109
|
-
return (0, import_react.useContext)(AdaptiveContext).onAssignment;
|
|
110
|
-
}
|
|
111
|
-
function useLayoutOrder() {
|
|
112
|
-
return (0, import_react.useContext)(AdaptiveContext).initialLayoutOrder;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// src/adaptive.tsx
|
|
116
|
-
var import_react3 = require("react");
|
|
117
|
-
|
|
118
|
-
// src/use-assignment.ts
|
|
119
|
-
var import_react2 = require("react");
|
|
120
|
-
|
|
121
|
-
// src/weights-store.ts
|
|
122
|
-
var store = /* @__PURE__ */ new Map();
|
|
123
|
-
var listeners = /* @__PURE__ */ new Map();
|
|
124
|
-
function subscribe(componentId, cb) {
|
|
125
|
-
let set = listeners.get(componentId);
|
|
126
|
-
if (!set) {
|
|
127
|
-
set = /* @__PURE__ */ new Set();
|
|
128
|
-
listeners.set(componentId, set);
|
|
129
|
-
}
|
|
130
|
-
set.add(cb);
|
|
131
|
-
return () => {
|
|
132
|
-
set.delete(cb);
|
|
133
|
-
if (set.size === 0) listeners.delete(componentId);
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
function update(componentId, weights) {
|
|
137
|
-
store.set(componentId, weights);
|
|
138
|
-
const set = listeners.get(componentId);
|
|
139
|
-
if (!set) return;
|
|
140
|
-
for (const cb of set) {
|
|
141
|
-
try {
|
|
142
|
-
cb(weights);
|
|
143
|
-
} catch (e) {
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
function getWeights(componentId) {
|
|
148
|
-
var _a;
|
|
149
|
-
return (_a = store.get(componentId)) != null ? _a : null;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// src/use-assignment.ts
|
|
153
|
-
function getDevOverride(componentId) {
|
|
154
|
-
var _a;
|
|
155
|
-
if (typeof window === "undefined") return null;
|
|
156
|
-
const global = (_a = window.__sentient_overrides) == null ? void 0 : _a[componentId];
|
|
157
|
-
if (global) return global;
|
|
158
|
-
try {
|
|
159
|
-
const params = new URLSearchParams(window.location.search);
|
|
160
|
-
for (const raw of params.getAll("sentient_variant")) {
|
|
161
|
-
const sep = raw.indexOf(":");
|
|
162
|
-
if (sep === -1) continue;
|
|
163
|
-
if (raw.slice(0, sep) === componentId) return raw.slice(sep + 1);
|
|
164
|
-
}
|
|
165
|
-
} catch (e) {
|
|
166
|
-
}
|
|
167
|
-
return null;
|
|
168
|
-
}
|
|
169
|
-
function pickFromWeights(weights, variantIds) {
|
|
170
|
-
var _a;
|
|
171
|
-
let best = null;
|
|
172
|
-
for (const v of weights.variants) {
|
|
173
|
-
if (!variantIds.includes(v.variantId)) continue;
|
|
174
|
-
if (!best || v.avgReward > best.avgReward) {
|
|
175
|
-
best = { variantId: v.variantId, avgReward: v.avgReward };
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
return (_a = best == null ? void 0 : best.variantId) != null ? _a : null;
|
|
179
|
-
}
|
|
180
|
-
function useAssignment(componentId, variantIds, agentData, agentDataByVariant) {
|
|
181
|
-
const initialAssignments = useInitialAssignments();
|
|
182
|
-
const ssrFallback = useSsrFallback();
|
|
183
|
-
const client = useSentient();
|
|
184
|
-
const segment = useSessionSegment();
|
|
185
|
-
const onAssignment = useOnAssignment();
|
|
186
|
-
const assignmentReportedRef = (0, import_react2.useRef)(null);
|
|
187
|
-
const devOverride = getDevOverride(componentId);
|
|
188
|
-
const overrideVariant = devOverride && variantIds.includes(devOverride) ? devOverride : null;
|
|
189
|
-
if (overrideVariant) {
|
|
190
|
-
console.info(`[sentient] override active: ${componentId} -> ${overrideVariant}`);
|
|
191
|
-
}
|
|
192
|
-
const initial = (() => {
|
|
193
|
-
var _a, _b;
|
|
194
|
-
if (overrideVariant) {
|
|
195
|
-
return { variantId: overrideVariant, content: null, isLoading: false };
|
|
196
|
-
}
|
|
197
|
-
if (!client) {
|
|
198
|
-
const preloaded = initialAssignments[componentId];
|
|
199
|
-
if (preloaded && variantIds.includes(preloaded)) {
|
|
200
|
-
return { variantId: preloaded, content: null, isLoading: false };
|
|
201
|
-
}
|
|
202
|
-
if (ssrFallback === "first" && variantIds.length > 0) {
|
|
203
|
-
return { variantId: variantIds[0], content: null, isLoading: false };
|
|
204
|
-
}
|
|
205
|
-
return { variantId: null, content: null, isLoading: true };
|
|
206
|
-
}
|
|
207
|
-
const cached = client.getAssignment(componentId, segment);
|
|
208
|
-
if (cached && (variantIds.includes(cached.variantId) || cached.content)) {
|
|
209
|
-
return { variantId: cached.variantId, content: (_a = cached.content) != null ? _a : null, isLoading: false };
|
|
210
|
-
}
|
|
211
|
-
const weights = getWeights(componentId);
|
|
212
|
-
if (weights) {
|
|
213
|
-
const chosen = pickFromWeights(weights, variantIds);
|
|
214
|
-
if (chosen) return { variantId: chosen, content: null, isLoading: false };
|
|
215
|
-
}
|
|
216
|
-
return { variantId: (_b = variantIds[0]) != null ? _b : null, content: null, isLoading: false };
|
|
217
|
-
})();
|
|
218
|
-
const [state, setState] = (0, import_react2.useState)(initial);
|
|
219
|
-
const reportAssignment = (variantId) => {
|
|
220
|
-
if (!onAssignment) return;
|
|
221
|
-
if (assignmentReportedRef.current === variantId) return;
|
|
222
|
-
assignmentReportedRef.current = variantId;
|
|
223
|
-
onAssignment(componentId, variantId);
|
|
224
|
-
};
|
|
225
|
-
(0, import_react2.useEffect)(() => {
|
|
226
|
-
var _a;
|
|
227
|
-
if (overrideVariant) return;
|
|
228
|
-
if (!client) return;
|
|
229
|
-
const cached = client.getAssignment(componentId, segment);
|
|
230
|
-
if (cached && (variantIds.includes(cached.variantId) || cached.content)) {
|
|
231
|
-
setState({ variantId: cached.variantId, content: (_a = cached.content) != null ? _a : null, isLoading: false });
|
|
232
|
-
reportAssignment(cached.variantId);
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
setState((prev) => {
|
|
236
|
-
var _a2;
|
|
237
|
-
return prev.variantId ? prev : { variantId: (_a2 = variantIds[0]) != null ? _a2 : null, content: null, isLoading: false };
|
|
238
|
-
});
|
|
239
|
-
}, [overrideVariant, client, componentId, segment]);
|
|
240
|
-
(0, import_react2.useEffect)(() => {
|
|
241
|
-
if (overrideVariant) return;
|
|
242
|
-
if (!client) return;
|
|
243
|
-
const cached = client.getAssignment(componentId, segment);
|
|
244
|
-
if (cached && variantIds.includes(cached.variantId)) return;
|
|
245
|
-
let cancelled = false;
|
|
246
|
-
void client.assign(componentId, variantIds, agentData, agentDataByVariant).then((result) => {
|
|
247
|
-
var _a;
|
|
248
|
-
if (cancelled) return;
|
|
249
|
-
if (!result) return;
|
|
250
|
-
if (!variantIds.includes(result.variantId) && !result.content) return;
|
|
251
|
-
setState({ variantId: result.variantId, content: (_a = result.content) != null ? _a : null, isLoading: false });
|
|
252
|
-
reportAssignment(result.variantId);
|
|
253
|
-
});
|
|
254
|
-
return () => {
|
|
255
|
-
cancelled = true;
|
|
256
|
-
};
|
|
257
|
-
}, [overrideVariant, client, componentId, segment]);
|
|
258
|
-
(0, import_react2.useEffect)(() => {
|
|
259
|
-
if (overrideVariant) return;
|
|
260
|
-
if (!client) return;
|
|
261
|
-
return subscribe(componentId, (weights) => {
|
|
262
|
-
var _a;
|
|
263
|
-
const cached = client.getAssignment(componentId, segment);
|
|
264
|
-
if (cached && (variantIds.includes(cached.variantId) || cached.content)) {
|
|
265
|
-
setState({ variantId: cached.variantId, content: (_a = cached.content) != null ? _a : null, isLoading: false });
|
|
266
|
-
return;
|
|
267
|
-
}
|
|
268
|
-
const chosen = pickFromWeights(weights, variantIds);
|
|
269
|
-
if (chosen) setState({ variantId: chosen, content: null, isLoading: false });
|
|
270
|
-
});
|
|
271
|
-
}, [overrideVariant, client, componentId, segment]);
|
|
272
|
-
if (overrideVariant) {
|
|
273
|
-
return { variantId: overrideVariant, content: null, isLoading: false };
|
|
274
|
-
}
|
|
275
|
-
return state;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// src/adaptive.tsx
|
|
279
|
-
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
280
|
-
function normalize(goal) {
|
|
281
|
-
if (typeof goal === "string") return { type: "click" };
|
|
282
|
-
return goal;
|
|
283
|
-
}
|
|
284
|
-
function isClickableTarget(el) {
|
|
285
|
-
if (!(el instanceof Element)) return false;
|
|
286
|
-
const tag = el.tagName.toLowerCase();
|
|
287
|
-
if (tag === "a" || tag === "button") return true;
|
|
288
|
-
const role = el.getAttribute("role");
|
|
289
|
-
return role === "button";
|
|
290
|
-
}
|
|
291
|
-
function findClickable(start, container) {
|
|
292
|
-
let cursor = start;
|
|
293
|
-
while (cursor && cursor !== container) {
|
|
294
|
-
if (isClickableTarget(cursor)) return true;
|
|
295
|
-
cursor = cursor.parentElement;
|
|
296
|
-
}
|
|
297
|
-
return false;
|
|
298
|
-
}
|
|
299
|
-
function AdaptiveImpl(props) {
|
|
300
|
-
var _a, _b;
|
|
301
|
-
const client = useSentient();
|
|
302
|
-
const apiKey = useAdaptiveApiKey();
|
|
303
|
-
const variantIds = (0, import_react3.useMemo)(() => Object.keys(props.variants), [props.variants]);
|
|
304
|
-
const { variantId, content } = useAssignment(props.id, variantIds, props.agentData, props.agentDataByVariant);
|
|
305
|
-
const containerRef = (0, import_react3.useRef)(null);
|
|
306
|
-
const [mounted, setMounted] = (0, import_react3.useState)(false);
|
|
307
|
-
(0, import_react3.useEffect)(() => {
|
|
308
|
-
setMounted(true);
|
|
309
|
-
}, []);
|
|
310
|
-
const goalFiredRef = (0, import_react3.useRef)(false);
|
|
311
|
-
const assignTrackedRef = (0, import_react3.useRef)(null);
|
|
312
|
-
const goal = (0, import_react3.useMemo)(() => normalize(props.goal), [props.goal]);
|
|
313
|
-
const goalLabel = typeof props.goal === "string" ? props.goal : goal.type;
|
|
314
|
-
(0, import_react3.useEffect)(() => {
|
|
315
|
-
var _a2, _b2;
|
|
316
|
-
if (!client || !variantId || !apiKey) return;
|
|
317
|
-
if (assignTrackedRef.current === variantId) return;
|
|
318
|
-
const rawHtml = (_b2 = (_a2 = containerRef.current) == null ? void 0 : _a2.innerHTML) != null ? _b2 : "";
|
|
319
|
-
if (!rawHtml && !mounted) return;
|
|
320
|
-
assignTrackedRef.current = variantId;
|
|
321
|
-
client.track({
|
|
322
|
-
projectId: apiKey,
|
|
323
|
-
componentId: props.id,
|
|
324
|
-
variantId,
|
|
325
|
-
eventType: "variant_assigned",
|
|
326
|
-
payload: rawHtml ? { previewHtml: rawHtml.slice(0, 3e4) } : {}
|
|
327
|
-
});
|
|
328
|
-
}, [client, variantId, apiKey, props.id, mounted]);
|
|
329
|
-
(0, import_react3.useEffect)(() => {
|
|
330
|
-
goalFiredRef.current = false;
|
|
331
|
-
}, [variantId]);
|
|
332
|
-
(0, import_react3.useEffect)(() => {
|
|
333
|
-
if (!client || !variantId) return;
|
|
334
|
-
const node = containerRef.current;
|
|
335
|
-
if (!node) return;
|
|
336
|
-
let timerId = null;
|
|
337
|
-
let hoverStart = 0;
|
|
338
|
-
const onEnter = () => {
|
|
339
|
-
hoverStart = Date.now();
|
|
340
|
-
timerId = setTimeout(() => {
|
|
341
|
-
client.track({
|
|
342
|
-
projectId: apiKey,
|
|
343
|
-
componentId: props.id,
|
|
344
|
-
variantId,
|
|
345
|
-
eventType: "cursor_signal",
|
|
346
|
-
payload: { hoverDuration: Date.now() - hoverStart }
|
|
347
|
-
});
|
|
348
|
-
timerId = null;
|
|
349
|
-
}, 800);
|
|
350
|
-
};
|
|
351
|
-
const onLeave = () => {
|
|
352
|
-
if (timerId !== null) {
|
|
353
|
-
clearTimeout(timerId);
|
|
354
|
-
timerId = null;
|
|
355
|
-
}
|
|
356
|
-
};
|
|
357
|
-
node.addEventListener("mouseenter", onEnter);
|
|
358
|
-
node.addEventListener("mouseleave", onLeave);
|
|
359
|
-
return () => {
|
|
360
|
-
node.removeEventListener("mouseenter", onEnter);
|
|
361
|
-
node.removeEventListener("mouseleave", onLeave);
|
|
362
|
-
if (timerId !== null) clearTimeout(timerId);
|
|
363
|
-
};
|
|
364
|
-
}, [client, variantId, apiKey, props.id]);
|
|
365
|
-
(0, import_react3.useEffect)(() => {
|
|
366
|
-
if (!client || !variantId) return;
|
|
367
|
-
const node = containerRef.current;
|
|
368
|
-
if (!node) return;
|
|
369
|
-
const fireGoal = () => {
|
|
370
|
-
if (goalFiredRef.current) return;
|
|
371
|
-
goalFiredRef.current = true;
|
|
372
|
-
client.track({
|
|
373
|
-
projectId: apiKey,
|
|
374
|
-
componentId: props.id,
|
|
375
|
-
variantId,
|
|
376
|
-
eventType: "goal_achieved",
|
|
377
|
-
goalType: goalLabel,
|
|
378
|
-
payload: { reward: 1 }
|
|
379
|
-
});
|
|
380
|
-
};
|
|
381
|
-
const subgoals = goal.type === "composite" ? goal.all : [goal];
|
|
382
|
-
const remaining = new Set(subgoals.map((_, i) => i));
|
|
383
|
-
const checkComposite = (idx) => {
|
|
384
|
-
remaining.delete(idx);
|
|
385
|
-
if (remaining.size === 0) fireGoal();
|
|
386
|
-
};
|
|
387
|
-
const cleanups = [];
|
|
388
|
-
subgoals.forEach((sub, idx) => {
|
|
389
|
-
if (sub.type === "click") {
|
|
390
|
-
const onClick = (e) => {
|
|
391
|
-
const target = e.target;
|
|
392
|
-
if (!(target instanceof Element)) return;
|
|
393
|
-
if (!findClickable(target, node)) return;
|
|
394
|
-
if (goal.type === "composite") checkComposite(idx);
|
|
395
|
-
else fireGoal();
|
|
396
|
-
};
|
|
397
|
-
node.addEventListener("click", onClick);
|
|
398
|
-
cleanups.push(() => node.removeEventListener("click", onClick));
|
|
399
|
-
return;
|
|
400
|
-
}
|
|
401
|
-
if (sub.type === "form_submit") {
|
|
402
|
-
const onSubmit = (e) => {
|
|
403
|
-
if (!(e.target instanceof HTMLFormElement)) return;
|
|
404
|
-
if (!node.contains(e.target)) return;
|
|
405
|
-
if (goal.type === "composite") checkComposite(idx);
|
|
406
|
-
else fireGoal();
|
|
407
|
-
};
|
|
408
|
-
node.addEventListener("submit", onSubmit);
|
|
409
|
-
cleanups.push(() => node.removeEventListener("submit", onSubmit));
|
|
410
|
-
return;
|
|
411
|
-
}
|
|
412
|
-
if (sub.type === "scroll_depth") {
|
|
413
|
-
const threshold = Math.max(0, Math.min(1, sub.threshold));
|
|
414
|
-
const io = new IntersectionObserver(
|
|
415
|
-
(entries) => {
|
|
416
|
-
for (const entry of entries) {
|
|
417
|
-
if (entry.intersectionRatio >= threshold) {
|
|
418
|
-
if (goal.type === "composite") checkComposite(idx);
|
|
419
|
-
else fireGoal();
|
|
420
|
-
io.disconnect();
|
|
421
|
-
break;
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
},
|
|
425
|
-
{ threshold: [threshold] }
|
|
426
|
-
);
|
|
427
|
-
io.observe(node);
|
|
428
|
-
cleanups.push(() => io.disconnect());
|
|
429
|
-
return;
|
|
430
|
-
}
|
|
431
|
-
});
|
|
432
|
-
return () => {
|
|
433
|
-
for (const c of cleanups) c();
|
|
434
|
-
};
|
|
435
|
-
}, [client, variantId, apiKey, props.id, goal, goalLabel]);
|
|
436
|
-
if (props.clientOnly && (!mounted || !client)) return null;
|
|
437
|
-
if (!variantId) return null;
|
|
438
|
-
const jsxContent = (_a = props.variants[variantId]) != null ? _a : null;
|
|
439
|
-
const managedContent = jsxContent === null ? content : null;
|
|
440
|
-
if (((_b = process == null ? void 0 : process.env) == null ? void 0 : _b.NODE_ENV) !== "production" && jsxContent === null && managedContent === null) {
|
|
441
|
-
console.warn(
|
|
442
|
-
`[sentient] <Adaptive id="${props.id}"> was assigned variant "${variantId}" but no matching key exists in props.variants. If this is a dashboard-managed text variant, use <AdaptiveText id="${props.id}"> instead.`
|
|
443
|
-
);
|
|
444
|
-
}
|
|
445
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { ref: containerRef, "data-sentient-id": props.id, "data-sentient-variant": variantId, children: jsxContent != null ? jsxContent : managedContent });
|
|
446
|
-
}
|
|
447
|
-
var Adaptive = (0, import_react3.memo)(AdaptiveImpl, (prev, next) => {
|
|
448
|
-
if (prev.id !== next.id) return false;
|
|
449
|
-
if (prev.goal !== next.goal) return false;
|
|
450
|
-
if (prev.variants === next.variants) return true;
|
|
451
|
-
const prevKeys = Object.keys(prev.variants);
|
|
452
|
-
const nextKeys = Object.keys(next.variants);
|
|
453
|
-
if (prevKeys.length !== nextKeys.length) return false;
|
|
454
|
-
return prevKeys.every((k) => k in next.variants);
|
|
455
|
-
});
|
|
456
|
-
|
|
457
|
-
// src/adaptive-text.tsx
|
|
458
|
-
var import_react4 = require("react");
|
|
459
|
-
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
460
|
-
function AdaptiveText({
|
|
461
|
-
id,
|
|
462
|
-
default: defaultText,
|
|
463
|
-
component: Tag = "span",
|
|
464
|
-
className
|
|
465
|
-
}) {
|
|
466
|
-
const client = useSentient();
|
|
467
|
-
const apiKey = useAdaptiveApiKey();
|
|
468
|
-
const onAssignment = useOnAssignment();
|
|
469
|
-
const [text, setText] = (0, import_react4.useState)(null);
|
|
470
|
-
const [variantId, setVariantId] = (0, import_react4.useState)(null);
|
|
471
|
-
const trackedRef = (0, import_react4.useRef)(null);
|
|
472
|
-
(0, import_react4.useEffect)(() => {
|
|
473
|
-
if (!client) return;
|
|
474
|
-
let cancelled = false;
|
|
475
|
-
void client.assign(id).then((result) => {
|
|
476
|
-
if (cancelled || !result) return;
|
|
477
|
-
setVariantId(result.variantId);
|
|
478
|
-
if (result.content) setText(result.content);
|
|
479
|
-
});
|
|
480
|
-
return () => {
|
|
481
|
-
cancelled = true;
|
|
482
|
-
};
|
|
483
|
-
}, [client, id]);
|
|
484
|
-
(0, import_react4.useEffect)(() => {
|
|
485
|
-
if (!client || !variantId || !apiKey) return;
|
|
486
|
-
if (trackedRef.current === variantId) return;
|
|
487
|
-
trackedRef.current = variantId;
|
|
488
|
-
client.track({
|
|
489
|
-
projectId: apiKey,
|
|
490
|
-
componentId: id,
|
|
491
|
-
variantId,
|
|
492
|
-
eventType: "variant_assigned",
|
|
493
|
-
payload: {}
|
|
494
|
-
});
|
|
495
|
-
onAssignment == null ? void 0 : onAssignment(id, variantId);
|
|
496
|
-
}, [client, variantId, apiKey, id, onAssignment]);
|
|
497
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Tag, { className, children: text != null ? text : defaultText });
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
// src/segment.ts
|
|
501
|
-
var import_core2 = require("@sentientui/core");
|
|
502
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
503
|
-
0 && (module.exports = {
|
|
504
|
-
Adaptive,
|
|
505
|
-
AdaptiveProvider,
|
|
506
|
-
AdaptiveText,
|
|
507
|
-
detectSegment,
|
|
508
|
-
getWeights,
|
|
509
|
-
subscribeWeights,
|
|
510
|
-
updateWeights,
|
|
511
|
-
useAssignment,
|
|
512
|
-
useInitialAssignments,
|
|
513
|
-
useLayoutOrder,
|
|
514
|
-
useSentient
|
|
515
|
-
});
|
|
2
|
+
"use strict";var K=Object.defineProperty;var me=Object.getOwnPropertyDescriptor;var ve=Object.getOwnPropertyNames,U=Object.getOwnPropertySymbols;var Q=Object.prototype.hasOwnProperty,pe=Object.prototype.propertyIsEnumerable;var q=(e,t,n)=>t in e?K(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,Y=(e,t)=>{for(var n in t||(t={}))Q.call(t,n)&&q(e,n,t[n]);if(U)for(var n of U(t))pe.call(t,n)&&q(e,n,t[n]);return e};var ye=(e,t)=>{for(var n in t)K(e,n,{get:t[n],enumerable:!0})},he=(e,t,n,l)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of ve(t))!Q.call(e,i)&&i!==n&&K(e,i,{get:()=>t[i],enumerable:!(l=me(t,i))||l.enumerable});return e};var Ae=e=>he(K({},"__esModule",{value:!0}),e);var Le={};ye(Le,{Adaptive:()=>ce,AdaptiveProvider:()=>Z,AdaptiveText:()=>de,detectSegment:()=>J.deriveSessionSegment,getWeights:()=>B,subscribeWeights:()=>$,updateWeights:()=>se,useAssignment:()=>X,useInitialAssignments:()=>N,useLayoutOrder:()=>ne,useSentient:()=>I});module.exports=Ae(Le);var m=require("react"),G=require("@sentientui/core"),ie=require("react/jsx-runtime");function Se(){var e,t;if(typeof window=="undefined")return"desktop:direct";try{let n=(0,G.detectDeviceClass)((e=navigator.userAgent)!=null?e:""),l=(0,G.detectTrafficSource)((t=document.referrer)!=null?t:"",window.location.origin);return`${n}:${l}`}catch(n){return"desktop:direct"}}var L=(0,m.createContext)({client:null,apiKey:"",initialAssignments:{},sessionSegment:"desktop:direct",ssrFallback:"first",onAssignment:void 0,initialLayoutOrder:null});function Z(e){var o;let[t,n]=(0,m.useState)(null),[l]=(0,m.useState)(()=>{var c;return(c=e.sessionSegment)!=null?c:Se()});(0,m.useEffect)(()=>{if(e.consent===!1&&!e.preConsentBehavior){n(y=>(y==null||y.destroy(),null));return}let c=(0,G.init)({apiKey:e.apiKey,context:e.context,debug:e.debug,initialAssignments:e.initialAssignments,sessionSegment:l,consent:e.consent,preConsentBehavior:e.preConsentBehavior});return n(c),()=>{c.destroy()}},[e.consent]);let i=(o=e.ssrFallback)!=null?o:"first",p=(0,m.useMemo)(()=>{var c,y;return{client:t,apiKey:e.apiKey,initialAssignments:(c=e.initialAssignments)!=null?c:{},sessionSegment:l,ssrFallback:i,onAssignment:e.onAssignment,initialLayoutOrder:(y=e.initialLayoutOrder)!=null?y:null}},[t,e.apiKey,e.initialAssignments,l,i,e.onAssignment,e.initialLayoutOrder]);return(0,ie.jsx)(L.Provider,{value:p,children:e.children})}function I(){return(0,m.useContext)(L).client}function V(){return(0,m.useContext)(L).apiKey}function N(){return(0,m.useContext)(L).initialAssignments}function ee(){return(0,m.useContext)(L).sessionSegment}function te(){return(0,m.useContext)(L).ssrFallback}function j(){return(0,m.useContext)(L).onAssignment}function ne(){return(0,m.useContext)(L).initialLayoutOrder}var g=require("react"),le=require("@sentientui/core");var R=require("react");var re=new Map,H=new Map;function $(e,t){let n=H.get(e);return n||(n=new Set,H.set(e,n)),n.add(t),()=>{n.delete(t),n.size===0&&H.delete(e)}}function se(e,t){re.set(e,t);let n=H.get(e);if(n)for(let l of n)try{l(t)}catch(i){}}function B(e){var t;return(t=re.get(e))!=null?t:null}function be(e){var n;if(typeof window=="undefined")return null;let t=(n=window.__sentient_overrides)==null?void 0:n[e];if(t)return t;try{let l=new URLSearchParams(window.location.search);for(let i of l.getAll("sentient_variant")){let p=i.indexOf(":");if(p!==-1&&i.slice(0,p)===e)return i.slice(p+1)}}catch(l){}return null}function oe(e,t){var l;let n=null;for(let i of e.variants)t.includes(i.variantId)&&(!n||i.avgReward>n.avgReward)&&(n={variantId:i.variantId,avgReward:i.avgReward});return(l=n==null?void 0:n.variantId)!=null?l:null}function X(e,t,n,l){let i=N(),p=te(),o=I(),c=ee(),y=j(),h=(0,R.useRef)(null),x=be(e),a=x&&t.includes(x)?x:null;a&&console.info(`[sentient] override active: ${e} -> ${a}`);let T=(()=>{var s,d;if(a)return{variantId:a,content:null,isLoading:!1};if(!o){let f=i[e];return f&&t.includes(f)?{variantId:f,content:null,isLoading:!1}:p==="first"&&t.length>0?{variantId:t[0],content:null,isLoading:!1}:{variantId:null,content:null,isLoading:!0}}let u=o.getAssignment(e,c);if(u&&(t.includes(u.variantId)||u.content))return{variantId:u.variantId,content:(s=u.content)!=null?s:null,isLoading:!1};let r=B(e);if(r){let f=oe(r,t);if(f)return{variantId:f,content:null,isLoading:!1}}return{variantId:(d=t[0])!=null?d:null,content:null,isLoading:!1}})(),[A,w]=(0,R.useState)(T),P=u=>{y&&h.current!==u&&(h.current=u,y(e,u))};return(0,R.useEffect)(()=>{var r;if(a||!o)return;let u=o.getAssignment(e,c);if(u&&(t.includes(u.variantId)||u.content)){w({variantId:u.variantId,content:(r=u.content)!=null?r:null,isLoading:!1}),P(u.variantId);return}w(s=>{var d;return s.variantId?s:{variantId:(d=t[0])!=null?d:null,content:null,isLoading:!1}})},[a,o,e,c]),(0,R.useEffect)(()=>{if(a||!o)return;let u=o.getAssignment(e,c);if(u&&t.includes(u.variantId))return;let r=!1;return o.assign(e,t,n,l).then(s=>{var d;r||s&&(!t.includes(s.variantId)&&!s.content||(w({variantId:s.variantId,content:(d=s.content)!=null?d:null,isLoading:!1}),P(s.variantId)))}),()=>{r=!0}},[a,o,e,c]),(0,R.useEffect)(()=>{if(!a&&o)return $(e,u=>{var d;let r=o.getAssignment(e,c);if(r&&(t.includes(r.variantId)||r.content)){w({variantId:r.variantId,content:(d=r.content)!=null?d:null,isLoading:!1});return}let s=oe(u,t);s&&w({variantId:s,content:null,isLoading:!1})})},[a,o,e,c]),a?{variantId:a,content:null,isLoading:!1}:A}var ue=require("react/jsx-runtime");function ke(e){return typeof e=="string"?{type:"click"}:e}var xe=3e4;function we(e,t){try{let n=`_snt_pv_${e}_${t}`;return sessionStorage.getItem(n)?!1:(sessionStorage.setItem(n,"1"),!0)}catch(n){return!0}}function Ce(e){if(!(e instanceof Element))return!1;let t=e.tagName.toLowerCase();return t==="a"||t==="button"?!0:e.getAttribute("role")==="button"}function ae(e,t){let n=e;for(;n&&n!==t;){if(Ce(n))return!0;n=n.parentElement}return!1}function Ee(e){var P,u;let t=I(),n=V(),l=(0,g.useMemo)(()=>Object.keys(e.variants),[e.variants]),{variantId:i,content:p}=X(e.id,l,e.agentData,e.agentDataByVariant),o=(0,g.useRef)(null),[c,y]=(0,g.useState)(!1);(0,g.useEffect)(()=>{y(!0)},[]);let h=(0,g.useRef)(!1),x=(0,g.useRef)(null),a=(0,g.useMemo)(()=>ke(e.goal),[e.goal]),T=typeof e.goal=="string"?e.goal:a.type;if((0,g.useEffect)(()=>{var d,f;if(!t||!i||!n||x.current===i)return;let r=(f=(d=o.current)==null?void 0:d.innerHTML)!=null?f:"";if(!r&&!c)return;x.current=i;let s=r!==""&&we(e.id,i);t.track({projectId:n,componentId:e.id,variantId:i,eventType:"variant_assigned",payload:s?{previewHtml:r.slice(0,xe)}:{}})},[t,i,n,e.id,c]),(0,g.useEffect)(()=>{h.current=!1},[i]),(0,g.useEffect)(()=>{if(!t||!i)return;let r=o.current;if(!r)return;let s=null,d=0,f=()=>{d=Date.now(),s=setTimeout(()=>{t.track({projectId:n,componentId:e.id,variantId:i,eventType:"cursor_signal",payload:{hoverDuration:Date.now()-d}}),s=null},800)},W=()=>{s!==null&&(clearTimeout(s),s=null)};return r.addEventListener("mouseenter",f),r.addEventListener("mouseleave",W),()=>{r.removeEventListener("mouseenter",f),r.removeEventListener("mouseleave",W),s!==null&&clearTimeout(s)}},[t,i,n,e.id]),(0,g.useEffect)(()=>{if(!t||!i)return;let r=o.current;if(!r)return;let s=Date.now();return(0,le.attachMicroSignalDetectors)((d,f={})=>{t.track({projectId:n,componentId:e.id,variantId:i,eventType:"micro_signal",payload:Y({signalType:d},f)})},r,s)},[t,i,n,e.id]),(0,g.useEffect)(()=>{if(!t||!i)return;let r=o.current;if(!r)return;if(a.type==="weighted_composite"){let S=new Set,k=[];return a.steps.forEach(({goal:v,name:b,weight:O},F)=>{let z=()=>{S.has(F)||(S.add(F),t.track({projectId:n,componentId:e.id,variantId:i,eventType:"goal_achieved",goalType:b,payload:{reward:O}}),t.goal(b,{},O,F))};if(v.type==="click"){let C=E=>{let M=E.target;M instanceof Element&&ae(M,r)&&z()};r.addEventListener("click",C),k.push(()=>r.removeEventListener("click",C));return}if(v.type==="form_submit"){let C=E=>{E.target instanceof HTMLFormElement&&r.contains(E.target)&&z()};r.addEventListener("submit",C),k.push(()=>r.removeEventListener("submit",C));return}if(v.type==="scroll_depth"){let C=Math.max(0,Math.min(1,v.threshold)),E=new IntersectionObserver(M=>{for(let fe of M)if(fe.intersectionRatio>=C){z(),E.disconnect();break}},{threshold:[C]});E.observe(r),k.push(()=>E.disconnect())}}),()=>{for(let v of k)v()}}let s=()=>{h.current||(h.current=!0,t.track({projectId:n,componentId:e.id,variantId:i,eventType:"goal_achieved",goalType:T,payload:{reward:1}}))},d=a.type==="composite"?a.all:[a],f=new Set(d.map((S,k)=>k)),W=S=>{f.delete(S),f.size===0&&s()},D=[];return d.forEach((S,k)=>{if(S.type==="click"){let v=b=>{let O=b.target;O instanceof Element&&ae(O,r)&&(a.type==="composite"?W(k):s())};r.addEventListener("click",v),D.push(()=>r.removeEventListener("click",v));return}if(S.type==="form_submit"){let v=b=>{b.target instanceof HTMLFormElement&&r.contains(b.target)&&(a.type==="composite"?W(k):s())};r.addEventListener("submit",v),D.push(()=>r.removeEventListener("submit",v));return}if(S.type==="scroll_depth"){let v=Math.max(0,Math.min(1,S.threshold)),b=new IntersectionObserver(O=>{for(let F of O)if(F.intersectionRatio>=v){a.type==="composite"?W(k):s(),b.disconnect();break}},{threshold:[v]});b.observe(r),D.push(()=>b.disconnect());return}}),()=>{for(let S of D)S()}},[t,i,n,e.id,a,T]),e.clientOnly&&(!c||!t)||!i)return null;let A=(P=e.variants[i])!=null?P:null,w=A===null?p:null;return((u=process==null?void 0:process.env)==null?void 0:u.NODE_ENV)!=="production"&&A===null&&w===null&&console.warn(`[sentient] <Adaptive id="${e.id}"> was assigned variant "${i}" but no matching key exists in props.variants. If this is a dashboard-managed text variant, use <AdaptiveText id="${e.id}"> instead.`),(0,ue.jsx)("div",{ref:o,"data-sentient-id":e.id,"data-sentient-variant":i,children:A!=null?A:w})}var ce=(0,g.memo)(Ee,(e,t)=>{if(e.id!==t.id||e.goal!==t.goal)return!1;if(e.variants===t.variants)return!0;let n=Object.keys(e.variants),l=Object.keys(t.variants);return n.length!==l.length?!1:n.every(i=>i in t.variants)});var _=require("react");var ge=require("react/jsx-runtime");function de({id:e,default:t,component:n="span",className:l}){let i=I(),p=V(),o=j(),[c,y]=(0,_.useState)(null),[h,x]=(0,_.useState)(null),a=(0,_.useRef)(null);return(0,_.useEffect)(()=>{if(!i)return;let T=!1;return i.assign(e).then(A=>{T||!A||(x(A.variantId),A.content&&y(A.content))}),()=>{T=!0}},[i,e]),(0,_.useEffect)(()=>{!i||!h||!p||a.current!==h&&(a.current=h,i.track({projectId:p,componentId:e,variantId:h,eventType:"variant_assigned",payload:{}}),o==null||o(e,h))},[i,h,p,e,o]),(0,ge.jsx)(n,{className:l,children:c!=null?c:t})}var J=require("@sentientui/core");0&&(module.exports={Adaptive,AdaptiveProvider,AdaptiveText,detectSegment,getWeights,subscribeWeights,updateWeights,useAssignment,useInitialAssignments,useLayoutOrder,useSentient});
|
|
516
3
|
//# sourceMappingURL=index.js.map
|