@sentientui/react 0.12.1 → 0.14.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 +164 -45
- package/dist/devtools.d.cts +2 -1
- package/dist/devtools.d.ts +2 -1
- package/dist/devtools.js +1 -1
- package/dist/devtools.js.map +1 -1
- package/dist/devtools.mjs +1 -1
- package/dist/devtools.mjs.map +1 -1
- package/dist/index.d.cts +142 -2
- package/dist/index.d.ts +142 -2
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/dist/next/adaptive-root.d.ts +36 -2
- package/dist/next/adaptive-root.js +2 -2
- package/dist/next/adaptive-root.js.map +1 -1
- package/dist/server.d.cts +17 -5
- package/dist/server.d.ts +17 -5
- package/dist/server.js +1 -1
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +1 -1
- package/dist/server.mjs.map +1 -1
- package/dist/testing/node.d.cts +5 -0
- package/dist/testing/node.d.ts +5 -0
- package/dist/testing/node.js +1 -1
- package/dist/testing/node.js.map +1 -1
- package/dist/testing/node.mjs +1 -1
- package/dist/testing/node.mjs.map +1 -1
- package/dist/testing.d.cts +15 -5
- package/dist/testing.d.ts +15 -5
- package/dist/testing.js +1 -1
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +1 -1
- package/dist/testing.mjs.map +1 -1
- package/llms.txt +45 -0
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -1,33 +1,34 @@
|
|
|
1
1
|
# @sentientui/react
|
|
2
2
|
|
|
3
|
-
React SDK for [SentientUI](https://sentient-ui.com) —
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
React SDK for [SentientUI](https://sentient-ui.com) — the adaptive ladder. Declare bounded
|
|
4
|
+
variations (styles, content, arrangement order); a persona-keyed optimizer on the hosted API
|
|
5
|
+
learns which one converts best for each visitor type. Visit 1 learns; Visit 2 converts.
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
npm install @sentientui/react
|
|
11
|
-
# or
|
|
12
|
-
pnpm add @sentientui/react
|
|
10
|
+
npm install @sentientui/react # or: npx sentientui init (scaffolds everything)
|
|
13
11
|
```
|
|
14
12
|
|
|
15
|
-
|
|
13
|
+
## 60-second start (no account)
|
|
16
14
|
|
|
17
|
-
|
|
15
|
+
```bash
|
|
16
|
+
npx sentientui init && npm run dev
|
|
17
|
+
# open http://localhost:3000?sentient_persona=buyer — watch the page adapt
|
|
18
|
+
```
|
|
18
19
|
|
|
19
|
-
|
|
20
|
+
With no API key the SDK runs in keyless **local mode**: deterministic simulated decisions, zero
|
|
21
|
+
network. Add a `pk_…` key from [sentient-ui.com](https://sentient-ui.com) to learn from real
|
|
22
|
+
traffic:
|
|
20
23
|
|
|
21
24
|
```bash
|
|
22
25
|
# .env.local
|
|
23
26
|
NEXT_PUBLIC_SENTIENT_API_KEY=pk_your_key
|
|
24
27
|
```
|
|
25
28
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
## Quick start — Next.js App Router (recommended)
|
|
29
|
+
## Setup
|
|
29
30
|
|
|
30
|
-
Wrap your root layout
|
|
31
|
+
Wrap your root layout (Next App Router — server component):
|
|
31
32
|
|
|
32
33
|
```tsx
|
|
33
34
|
// app/layout.tsx
|
|
@@ -35,14 +36,9 @@ import { AdaptiveRoot } from '@sentientui/react/next';
|
|
|
35
36
|
|
|
36
37
|
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
37
38
|
return (
|
|
38
|
-
<html lang="en">
|
|
39
|
+
<html lang="en" suppressHydrationWarning>
|
|
39
40
|
<body>
|
|
40
41
|
<AdaptiveRoot
|
|
41
|
-
components={[
|
|
42
|
-
{ id: 'hero_cta', variantIds: ['control', 'variant_a'] },
|
|
43
|
-
{ id: 'pricing', variantIds: ['monthly', 'annual_first'] },
|
|
44
|
-
]}
|
|
45
|
-
sections={['hero', 'pricing', 'features', 'social_proof']}
|
|
46
42
|
apiKey={process.env.NEXT_PUBLIC_SENTIENT_API_KEY!}
|
|
47
43
|
appOrigin={process.env.NEXT_PUBLIC_APP_URL!}
|
|
48
44
|
context="saas"
|
|
@@ -55,44 +51,116 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
|
|
55
51
|
}
|
|
56
52
|
```
|
|
57
53
|
|
|
58
|
-
|
|
54
|
+
`suppressHydrationWarning` on `<html>` is required: an inline script (rendered by
|
|
55
|
+
`AdaptiveRoot` as its first child) sets the persona attributes before first paint, exactly like
|
|
56
|
+
the `next-themes` pattern. Other React apps (Vite, CRA, Remix, Pages Router) use
|
|
57
|
+
`<AdaptiveProvider>` from `@sentientui/react` with the same props.
|
|
58
|
+
|
|
59
|
+
## The adaptive ladder
|
|
60
|
+
|
|
61
|
+
### Rung 0 — Observe
|
|
62
|
+
|
|
63
|
+
Install is the integration. The dashboard immediately shows who is arriving: persona mix, device
|
|
64
|
+
and traffic-source segments, engagement signals. The "Suggested next step" card tells you which
|
|
65
|
+
rung to climb next, with a copy-pasteable snippet.
|
|
66
|
+
|
|
67
|
+
### Rung 1 — Style (CSS only)
|
|
68
|
+
|
|
69
|
+
**1a. Persona attributes — zero declaration.** The SDK sets on `<html>`:
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
data-sentient-persona = buyer | researcher | deal_seeker | browser | unknown
|
|
73
|
+
data-sentient-confidence = low | medium | high
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Write plain CSS against them (the canonical block — safe defaults for every persona):
|
|
77
|
+
|
|
78
|
+
```css
|
|
79
|
+
/* Show each visitor type what it cares about. Confidence-gate bold treatments. */
|
|
80
|
+
html[data-sentient-persona='buyer'] .cta-primary { font-weight: 700; }
|
|
81
|
+
html[data-sentient-persona='researcher'] .spec-details { display: block; }
|
|
82
|
+
html[data-sentient-persona='deal_seeker'] .discount-banner { display: block; }
|
|
83
|
+
html[data-sentient-persona='browser'] .newsletter-nudge { display: block; }
|
|
84
|
+
html[data-sentient-confidence='low'] .discount-banner,
|
|
85
|
+
html[data-sentient-confidence='low'] .newsletter-nudge { display: none; }
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Force any persona in dev: `?sentient_persona=deal_seeker`.
|
|
89
|
+
|
|
90
|
+
**1b. Adaptive tokens — learned.** Declare a bounded design space; the optimizer picks per
|
|
91
|
+
visitor type; values arrive as element-scoped `data-*` props (SSR-serialized — zero flicker):
|
|
59
92
|
|
|
60
93
|
```tsx
|
|
61
|
-
import {
|
|
94
|
+
import { useAdaptiveTokens } from '@sentientui/react';
|
|
62
95
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
96
|
+
function Hero() {
|
|
97
|
+
const t = useAdaptiveTokens('hero', {
|
|
98
|
+
tone: ['calm', 'urgent'], // first value = baseline (what you show today)
|
|
99
|
+
motion: ['none', 'pulse'],
|
|
100
|
+
});
|
|
101
|
+
return <section {...t.props} className="hero">…</section>;
|
|
102
|
+
// t.props → { 'data-tone': 'urgent', 'data-motion': 'pulse' }
|
|
103
|
+
// t.tokens → { tone: 'urgent', motion: 'pulse' }
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
```css
|
|
108
|
+
.hero[data-tone='urgent'] .cta { font-weight: 700; }
|
|
109
|
+
|
|
110
|
+
/* Animation values are enum values whose CSS you own — always respect reduced motion: */
|
|
111
|
+
.hero[data-motion='pulse'] .cta { animation: pulse 2s infinite; }
|
|
112
|
+
@media (prefers-reduced-motion: reduce) {
|
|
113
|
+
.hero[data-motion='pulse'] .cta { animation: none; }
|
|
74
114
|
}
|
|
75
115
|
```
|
|
76
116
|
|
|
77
|
-
|
|
117
|
+
Constraints: 1–4 dims, 2–6 values each, combinations ≤ 64. Enum values only — no arbitrary CSS.
|
|
118
|
+
Optional `{ goal: 'buy_click' }` third argument credits conversions to this element directly.
|
|
78
119
|
|
|
79
|
-
|
|
120
|
+
### Rung 2 — Swap (alternate content)
|
|
80
121
|
|
|
81
122
|
```tsx
|
|
82
|
-
import {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
<
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
{children}
|
|
91
|
-
</AdaptiveProvider>
|
|
92
|
-
);
|
|
123
|
+
import { useAdaptive } from '@sentientui/react';
|
|
124
|
+
|
|
125
|
+
function BuyBox() {
|
|
126
|
+
const { variant, value, bind, fireGoal } = useAdaptive('buy-box', {
|
|
127
|
+
variants: { calm: <CalmBuyBox />, urgent: <UrgentBuyBox /> }, // first key = baseline
|
|
128
|
+
goal: 'buy_click', // required
|
|
129
|
+
});
|
|
130
|
+
return <div {...bind}>{value}</div>;
|
|
93
131
|
}
|
|
94
132
|
```
|
|
95
133
|
|
|
134
|
+
`bind` (ref + data attributes) wires exposure tracking, goal listeners, and engagement signals —
|
|
135
|
+
attach it or the slot cannot learn (dev mode warns loudly if you don't). `<Adaptive>` is the
|
|
136
|
+
wrapper form of the same rung; `<AdaptiveText>` swaps dashboard-managed text.
|
|
137
|
+
|
|
138
|
+
### Rung 3 — Reorder (structure)
|
|
139
|
+
|
|
140
|
+
Region-scope — declared arrangements of keyed children only, never free permutation:
|
|
141
|
+
|
|
142
|
+
```tsx
|
|
143
|
+
import { AdaptiveGroup } from '@sentientui/react';
|
|
144
|
+
|
|
145
|
+
<AdaptiveGroup
|
|
146
|
+
id="pricing-area"
|
|
147
|
+
arrangements={{
|
|
148
|
+
standard: ['plans', 'faq', 'social'], // first key = baseline
|
|
149
|
+
social_first: ['social', 'plans', 'faq'],
|
|
150
|
+
}}
|
|
151
|
+
goal="plan_selected"
|
|
152
|
+
>
|
|
153
|
+
<PlanGrid key="plans" />
|
|
154
|
+
<Faq key="faq" />
|
|
155
|
+
<Testimonials key="social" />
|
|
156
|
+
</AdaptiveGroup>
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Page-scope — declare `sections` on `AdaptiveRoot` and read the order with `useLayoutOrder()`
|
|
160
|
+
(see the API reference below). Use `AdaptiveGroup` for a region, `sections` for the page.
|
|
161
|
+
|
|
162
|
+
Every decision is locked for the session — visitors never see the page reshuffle under them.
|
|
163
|
+
|
|
96
164
|
## API
|
|
97
165
|
|
|
98
166
|
### `<AdaptiveRoot>` (Next.js App Router — server component)
|
|
@@ -184,8 +252,59 @@ import { AdaptiveText } from '@sentientui/react';
|
|
|
184
252
|
</h1>
|
|
185
253
|
```
|
|
186
254
|
|
|
255
|
+
### `useAdaptiveTokens(id, dims, opts?)`
|
|
256
|
+
|
|
257
|
+
```ts
|
|
258
|
+
function useAdaptiveTokens(
|
|
259
|
+
id: string,
|
|
260
|
+
dims: Record<string, readonly string[]>, // 1–4 dims × 2–6 values; first value = baseline
|
|
261
|
+
opts?: { goal?: string | GoalConfig },
|
|
262
|
+
): { tokens: Record<string, string>; props: Record<string, string> }; // props keys: `data-${dim}`
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Spread `props` onto the element you style. Values serialize through SSR markup — no flicker, no
|
|
266
|
+
hydration mismatch. Renaming a value is a cold start for that value's learning.
|
|
267
|
+
|
|
268
|
+
### `useAdaptive(id, config)`
|
|
269
|
+
|
|
270
|
+
```ts
|
|
271
|
+
function useAdaptive<T>(
|
|
272
|
+
id: string,
|
|
273
|
+
config: { variants: Record<string, T>; goal: string | GoalConfig }, // first key = baseline
|
|
274
|
+
): {
|
|
275
|
+
variant: string;
|
|
276
|
+
value: T;
|
|
277
|
+
bind: { ref: (el: HTMLElement | null) => void; 'data-sentient-id': string; 'data-sentient-variant': string };
|
|
278
|
+
fireGoal: (goalType?: string, opts?: ComponentGoalOptions) => void;
|
|
279
|
+
};
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Headless Swap-rung hook. `goal` is required and `bind` must be attached to a rendered element —
|
|
283
|
+
learning needs both. Supersedes `useAssignment`.
|
|
284
|
+
|
|
285
|
+
### `<AdaptiveGroup>`
|
|
286
|
+
|
|
287
|
+
| Prop | Type | Description |
|
|
288
|
+
|------|------|-------------|
|
|
289
|
+
| `id` | `string` | Unique group identifier. |
|
|
290
|
+
| `arrangements` | `Record<string, string[]>` | Arrangement id → ordered child keys. First entry = baseline. |
|
|
291
|
+
| `baseline` | `string` *(optional)* | Explicit baseline arrangement id (defaults to the first key). |
|
|
292
|
+
| `goal` | `string \| GoalConfig` *(optional)* | Conversion goal credited to this group. |
|
|
293
|
+
| `children` | keyed `ReactNode`s | Every key referenced by an arrangement must exist. |
|
|
294
|
+
|
|
295
|
+
Reorders via React keys (DOM moves, state preserved). Declared orders only.
|
|
296
|
+
|
|
297
|
+
### Persona attributes (Rung 1a)
|
|
298
|
+
|
|
299
|
+
`AdaptiveRoot` renders an inline script (its first child) that sets `data-sentient-persona` and
|
|
300
|
+
`data-sentient-confidence` on `<html>` before first paint; the client SDK adopts those values
|
|
301
|
+
and never rewrites them mid-session. Requirements: `suppressHydrationWarning` on `<html>`,
|
|
302
|
+
`AdaptiveRoot` at the top of the tree. Override in dev with `?sentient_persona=<persona>`.
|
|
303
|
+
|
|
187
304
|
### `useAssignment(componentId, variantIds?)`
|
|
188
305
|
|
|
306
|
+
> **Deprecated** — use [`useAdaptive`](#useadaptiveid-config) instead; it carries the goal and exposure wiring `useAssignment` leaves to you. Kept for backward compatibility.
|
|
307
|
+
|
|
189
308
|
Lower-level hook when you need the variant ID inside your own render logic (e.g. full-page layout tests where `<Adaptive>`'s wrapper `<div>` would break a flex/grid layout).
|
|
190
309
|
|
|
191
310
|
```tsx
|
package/dist/devtools.d.cts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
declare const LOCAL_MODE_DEVTOOLS_BANNER = "Local mode \u2014 decisions are simulated; add a key to learn from real traffic";
|
|
1
2
|
declare function AdaptiveDevtools({ apiKey }?: {
|
|
2
3
|
apiKey?: string;
|
|
3
4
|
}): JSX.Element | null;
|
|
4
5
|
|
|
5
|
-
export { AdaptiveDevtools };
|
|
6
|
+
export { AdaptiveDevtools, LOCAL_MODE_DEVTOOLS_BANNER };
|
package/dist/devtools.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
declare const LOCAL_MODE_DEVTOOLS_BANNER = "Local mode \u2014 decisions are simulated; add a key to learn from real traffic";
|
|
1
2
|
declare function AdaptiveDevtools({ apiKey }?: {
|
|
2
3
|
apiKey?: string;
|
|
3
4
|
}): JSX.Element | null;
|
|
4
5
|
|
|
5
|
-
export { AdaptiveDevtools };
|
|
6
|
+
export { AdaptiveDevtools, LOCAL_MODE_DEVTOOLS_BANNER };
|
package/dist/devtools.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
"use strict";"use client";var
|
|
2
|
+
"use strict";"use client";var Y=Object.create;var u=Object.defineProperty,$=Object.defineProperties,H=Object.getOwnPropertyDescriptor,J=Object.getOwnPropertyDescriptors,X=Object.getOwnPropertyNames,E=Object.getOwnPropertySymbols,q=Object.getPrototypeOf,k=Object.prototype.hasOwnProperty,Q=Object.prototype.propertyIsEnumerable;var A=(e,t,n)=>t in e?u(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,a=(e,t)=>{for(var n in t||(t={}))k.call(t,n)&&A(e,n,t[n]);if(E)for(var n of E(t))Q.call(t,n)&&A(e,n,t[n]);return e},_=(e,t)=>$(e,J(t));var Z=(e,t)=>{for(var n in t)u(e,n,{get:t[n],enumerable:!0})},L=(e,t,n,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of X(t))!k.call(e,r)&&r!==n&&u(e,r,{get:()=>t[r],enumerable:!(i=H(t,r))||i.enumerable});return e};var ee=(e,t,n)=>(n=e!=null?Y(q(e)):{},L(t||!e||!e.__esModule?u(n,"default",{value:e,enumerable:!0}):n,e)),te=e=>L(u({},"__esModule",{value:!0}),e);var pe={};Z(pe,{AdaptiveDevtools:()=>ge,LOCAL_MODE_DEVTOOLS_BANNER:()=>M});module.exports=te(pe);var g=require("react"),p=require("@sentientui/policy"),T=require("@sentientui/core");var ne={components:new Map,slots:new Map,sections:[],listeners:new Set};function w(){if(typeof window=="undefined")return ne;let e=window;return e.__sentient_registry||(e.__sentient_registry={components:new Map,slots:new Map,sections:[],listeners:new Set}),e.__sentient_registry}function y(){return[...w().components.values()]}function D(){return[...w().slots.values()]}function R(){return[...w().sections]}function I(e){let t=w().listeners;return t.add(e),()=>{t.delete(e)}}function x(){let e=window;return e.__sentient_overrides||(e.__sentient_overrides={}),e.__sentient_overrides}function O(e,t){x()[e]=t}function C(e){delete x()[e]}function b(){return a({},x())}var oe={on:!1,listeners:new Set};function W(){if(typeof window=="undefined")return oe;let e=window;return e.__sentient_preview||(e.__sentient_preview={on:!1,listeners:new Set}),e.__sentient_preview}function f(e){let t=W();if(t.on!==e){t.on=e;for(let n of t.listeners)n()}}function B(){return W().on}var ie="sentient:overrides-changed";function v(){var t;if(typeof window=="undefined")return;let e=window;e.__sentient_overrides_version=((t=e.__sentient_overrides_version)!=null?t:0)+1,window.dispatchEvent(new Event(ie))}function N(){var e;return typeof window=="undefined"?null:(e=window.__sentient_devtools_config)!=null?e:null}var s=require("react/jsx-runtime"),V,se=typeof process!="undefined"&&((V=process.env)==null?void 0:V.NODE_ENV)==="production",re="https://api.sentient-ui.com/v1",M="Local mode \u2014 decisions are simulated; add a key to learn from real traffic";function U(e){var n,i;for(let[r,l]of Object.entries((n=e.assignments)!=null?n:{}))O(r,l);let t=window;e.layoutOrder&&e.layoutOrder.length>0&&(t.__sentient_layout_override=e.layoutOrder),e.slots&&(t.__sentient_slot_overrides=a(a({},(i=t.__sentient_slot_overrides)!=null?i:{}),e.slots)),e.personaAttributes&&(document.documentElement.dataset.sentientPersona=e.personaAttributes.persona,document.documentElement.dataset.sentientConfidence=e.personaAttributes.confidence),f(!0),v()}function de(){try{let e=document.cookie.match(/(?:^|; )_snt_uid=([^;]*)/);if(e)return decodeURIComponent(e[1])}catch(e){}return"devtools-preview"}function j(){return D().map(e=>a(a({id:e.id},e.arms?{arms:e.arms}:{}),e.dims?{dims:e.dims}:{}))}async function ae(e,t,n){var l;let i=await fetch(`${t}/explain`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${e}`},body:JSON.stringify({persona:n,sections:R().map(d=>({id:d})),components:y().map(d=>({id:d.id,variantIds:d.variantIds})),slots:j()})});if(!i.ok)return;let r=await i.json();U(_(a({},r),{personaAttributes:(l=r.personaAttributes)!=null?l:{persona:n,confidence:"high"}}))}async function le(e){let t=await import("@sentientui/core/local");if(!t.LOCAL_ENGINE_AVAILABLE)return;let n=t.createLocalEngine({sessionId:de(),forcedPersona:e}).decide({sections:R(),components:y().map(i=>({id:i.id,variantIds:i.variantIds})),slots:j()});U({assignments:n.assignments,layoutOrder:n.layoutOrder,slots:n.slots,personaAttributes:{persona:n.persona,confidence:(0,p.confidenceBand)(n.confidence)}})}function ce(){for(let t of Object.keys(b()))C(t);let e=window;delete e.__sentient_layout_override,delete e.__sentient_slot_overrides,delete document.documentElement.dataset.sentientPersona,delete document.documentElement.dataset.sentientConfidence;try{let t=[];for(let n=0;n<localStorage.length;n++){let i=localStorage.key(n);i&&i.startsWith(T.SNAPSHOT_STORAGE_KEY_PREFIX)&&t.push(i)}for(let n of t)localStorage.removeItem(n)}catch(t){}f(!1),v();try{window.location.reload()}catch(t){}}var S=e=>({padding:"2px 8px",borderRadius:4,border:"1px solid #444",background:e?"#3b82f6":"#222",color:"#eee",cursor:"pointer"});function ge({apiKey:e}={}){var P;let[t,n]=(0,g.useState)(!1),[i,r]=(0,g.useState)(null),[,l]=(0,g.useReducer)(o=>o+1,0);if((0,g.useEffect)(()=>I(l),[]),se)return null;let d=(P=N())!=null?P:{apiKey:e!=null?e:"",apiBaseUrl:re,isLocal:!1},F=d.isLocal||!d.apiKey,m=y(),h=b();function G(o,c){O(o,c),f(!0),v(),l()}function z(o){C(o),Object.keys(b()).length===0&&f(!1),v(),l()}function K(o){r(o),(F?le(o):ae(d.apiKey,d.apiBaseUrl,o)).then(l)}return(0,s.jsxs)("div",{style:{position:"fixed",bottom:16,right:16,zIndex:2147483647,fontFamily:"system-ui"},children:[t&&(0,s.jsxs)("div",{style:{width:300,maxHeight:460,overflowY:"auto",background:"#111",color:"#eee",borderRadius:8,padding:12,marginBottom:8,boxShadow:"0 8px 30px rgba(0,0,0,.4)",fontSize:12},children:[d.isLocal&&(0,s.jsx)("div",{style:{background:"#1e293b",border:"1px solid #334155",borderRadius:4,padding:"6px 8px",marginBottom:8},children:M}),(0,s.jsxs)("div",{style:{opacity:.7,marginBottom:8},children:[m.length," component",m.length===1?"":"s"," \xB7 ",B()?"preview \u2014 writing nothing":"live"]}),(0,s.jsxs)("div",{style:{borderBottom:"1px solid #333",paddingBottom:8,marginBottom:8},children:[(0,s.jsx)("div",{style:{opacity:.7,marginBottom:4},children:"Preview persona"}),(0,s.jsxs)("div",{style:{display:"flex",flexWrap:"wrap",gap:4},children:[p.PERSONAS.map(o=>(0,s.jsx)("button",{onClick:()=>K(o),style:S(i===o),children:p.PERSONA_DISPLAY[o]},o)),(i!==null||Object.keys(h).length>0)&&(0,s.jsx)("button",{onClick:ce,style:_(a({},S(!1)),{color:"#aaa"}),children:"Reset"})]})]}),m.length===0&&(0,s.jsx)("div",{style:{opacity:.6},children:"No components on this page yet."}),m.map(o=>(0,s.jsxs)("div",{style:{borderTop:"1px solid #333",padding:"8px 0"},children:[(0,s.jsxs)("div",{style:{fontWeight:600},children:[o.id,o.goal?` \xB7 goal: ${o.goal}`:""]}),(0,s.jsxs)("div",{style:{display:"flex",flexWrap:"wrap",gap:4,marginTop:4},children:[o.variantIds.map(c=>(0,s.jsx)("button",{onClick:()=>G(o.id,c),style:S(h[o.id]===c),children:c},c)),h[o.id]&&(0,s.jsx)("button",{onClick:()=>z(o.id),style:_(a({},S(!1)),{color:"#aaa"}),children:"reset"})]})]},o.id))]}),(0,s.jsx)("button",{"aria-label":"Sentient DevTools",onClick:()=>n(o=>!o),style:{width:40,height:40,borderRadius:20,border:"none",background:"#3b82f6",color:"#fff",cursor:"pointer",boxShadow:"0 4px 14px rgba(0,0,0,.3)"},children:"\u25E7"})]})}0&&(module.exports={AdaptiveDevtools,LOCAL_MODE_DEVTOOLS_BANNER});
|
|
3
3
|
//# sourceMappingURL=devtools.js.map
|
package/dist/devtools.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/devtools/index.tsx","../src/devtools-registry.ts","../src/devtools-overrides.ts","../src/preview-mode.ts"],"sourcesContent":["'use client';\nimport { useEffect, useReducer, useState } from 'react';\nimport { getRegistered, subscribeRegistry } from '../devtools-registry.js';\nimport { setVariantOverride, clearVariantOverride, getOverrides } from '../devtools-overrides.js';\nimport { setPreviewMode, getPreviewMode } from '../preview-mode.js';\n\ndeclare const process: { env?: { NODE_ENV?: string } } | undefined;\nconst IS_PROD = typeof process !== 'undefined' && process.env?.NODE_ENV === 'production';\n\nconst SEED_PERSONAS = ['browsers', 'buyers', 'researchers', 'deal-seekers'];\n\n/** Simulate a persona via /v1/explain (read-only) and apply the result as overrides. */\nasync function forcePersona(apiKey: string, persona: string): Promise<void> {\n const sections = getRegistered().map((c) => ({ id: c.id }));\n const components = getRegistered().map((c) => ({ id: c.id, variantIds: c.variantIds }));\n const res = await fetch('/v1/explain', {\n method: 'POST',\n headers: { 'content-type': 'application/json', authorization: `Bearer ${apiKey}` },\n body: JSON.stringify({ persona, sections, components }),\n });\n if (!res.ok) return;\n const data = (await res.json()) as { assignments: Record<string, string> };\n for (const [id, variantId] of Object.entries(data.assignments)) setVariantOverride(id, variantId);\n setPreviewMode(true);\n}\n\nexport function AdaptiveDevtools({ apiKey }: { apiKey?: string } = {}): JSX.Element | null {\n const [open, setOpen] = useState(false);\n const [, force] = useReducer((n: number) => n + 1, 0);\n useEffect(() => subscribeRegistry(force), []);\n\n // Never render in production, even if imported by mistake. (Hooks run first\n // so the rules of hooks hold regardless of this compile-time constant.)\n if (IS_PROD) return null;\n\n const components = getRegistered();\n const overrides = getOverrides();\n\n function choose(id: string, variantId: string): void {\n setVariantOverride(id, variantId);\n setPreviewMode(true); // suppress events while previewing\n force();\n }\n function reset(id: string): void {\n clearVariantOverride(id);\n if (Object.keys(getOverrides()).length === 0) setPreviewMode(false);\n force();\n }\n\n return (\n <div style={{ position: 'fixed', bottom: 16, right: 16, zIndex: 2147483647, fontFamily: 'system-ui' }}>\n {open && (\n <div style={{ width: 300, maxHeight: 420, overflowY: 'auto', background: '#111', color: '#eee',\n borderRadius: 8, padding: 12, marginBottom: 8, boxShadow: '0 8px 30px rgba(0,0,0,.4)', fontSize: 12 }}>\n <div style={{ opacity: .7, marginBottom: 8 }}>\n {components.length} component{components.length === 1 ? '' : 's'} · {getPreviewMode() ? 'preview — writing nothing' : 'live'}\n </div>\n {apiKey && (\n <div style={{ borderBottom: '1px solid #333', paddingBottom: 8, marginBottom: 8 }}>\n <div style={{ opacity: .7, marginBottom: 4 }}>Preview persona</div>\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4 }}>\n {SEED_PERSONAS.map((p) => (\n <button key={p} onClick={() => void forcePersona(apiKey, p).then(force)}\n style={{ padding: '2px 8px', borderRadius: 4, border: '1px solid #444', background: '#222', color: '#eee', cursor: 'pointer' }}>\n {p}\n </button>\n ))}\n </div>\n </div>\n )}\n {components.length === 0 && <div style={{ opacity: .6 }}>No components on this page yet.</div>}\n {components.map((c) => (\n <div key={c.id} style={{ borderTop: '1px solid #333', padding: '8px 0' }}>\n <div style={{ fontWeight: 600 }}>{c.id}{c.goal ? ` · goal: ${c.goal}` : ''}</div>\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4, marginTop: 4 }}>\n {c.variantIds.map((v) => (\n <button key={v} onClick={() => choose(c.id, v)}\n style={{ padding: '2px 8px', borderRadius: 4, border: '1px solid #444',\n background: overrides[c.id] === v ? '#3b82f6' : '#222', color: '#eee', cursor: 'pointer' }}>\n {v}\n </button>\n ))}\n {overrides[c.id] && (\n <button onClick={() => reset(c.id)}\n style={{ padding: '2px 8px', borderRadius: 4, border: '1px solid #444', background: '#222', color: '#aaa', cursor: 'pointer' }}>\n reset\n </button>\n )}\n </div>\n </div>\n ))}\n </div>\n )}\n <button aria-label=\"Sentient DevTools\" onClick={() => setOpen((o) => !o)}\n style={{ width: 40, height: 40, borderRadius: 20, border: 'none', background: '#3b82f6', color: '#fff',\n cursor: 'pointer', boxShadow: '0 4px 14px rgba(0,0,0,.3)' }}>◧</button>\n </div>\n );\n}\n","export type RegisteredComponent = { id: string; variantIds: string[]; goal?: string };\n\nconst registry = new Map<string, RegisteredComponent>();\nconst listeners = new Set<() => void>();\n\nfunction emit(): void {\n for (const fn of listeners) fn();\n}\n\n/** Register (or re-register) a component. Returns an unregister function. */\nexport function registerComponent(c: RegisteredComponent): () => void {\n registry.set(c.id, c);\n emit();\n return () => {\n registry.delete(c.id);\n emit();\n };\n}\n\nexport function getRegistered(): RegisteredComponent[] {\n return [...registry.values()];\n}\n\nexport function subscribeRegistry(fn: () => void): () => void {\n listeners.add(fn);\n return () => {\n listeners.delete(fn);\n };\n}\n","function store(): Record<string, string> {\n const w = window as unknown as { __sentient_overrides?: Record<string, string> };\n if (!w.__sentient_overrides) w.__sentient_overrides = {};\n return w.__sentient_overrides;\n}\n\n/** Force `componentId` to render `variantId` — read by `useAssignment`. */\nexport function setVariantOverride(componentId: string, variantId: string): void {\n store()[componentId] = variantId;\n}\n\nexport function clearVariantOverride(componentId: string): void {\n delete store()[componentId];\n}\n\nexport function getOverrides(): Record<string, string> {\n return { ...store() };\n}\n","import type { SentientClient } from '@sentientui/core';\n\nlet previewOn = false;\nconst listeners = new Set<() => void>();\n\nexport function setPreviewMode(on: boolean): void {\n if (previewOn === on) return;\n previewOn = on;\n for (const fn of listeners) fn();\n}\n\nexport function getPreviewMode(): boolean {\n return previewOn;\n}\n\nexport function subscribePreview(fn: () => void): () => void {\n listeners.add(fn);\n return () => {\n listeners.delete(fn);\n };\n}\n\n/**\n * Wraps a client so it writes nothing: reads pass through, all emitters no-op.\n * Used while previewing variants/personas so no `variant_assigned`, goal, or\n * session events are sent.\n */\nexport function createPreviewClient(inner: SentientClient): SentientClient {\n return {\n track: () => undefined,\n goal: () => undefined,\n componentGoal: () => undefined,\n identify: () => undefined,\n fetchWeights: () => Promise.resolve([]),\n getAssignment: (componentId, segment) => inner.getAssignment(componentId, segment),\n assign: (componentId, variantIds, agentData, agentDataByVariant) =>\n inner.assign(componentId, variantIds, agentData, agentDataByVariant),\n getGraph: () => inner.getGraph(),\n destroy: () => inner.destroy(),\n };\n}\n"],"mappings":"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","names":["devtools_exports","__export","AdaptiveDevtools","__toCommonJS","import_react","registry","listeners","getRegistered","registry","subscribeRegistry","fn","listeners","store","w","setVariantOverride","componentId","variantId","clearVariantOverride","getOverrides","__spreadValues","previewOn","listeners","setPreviewMode","on","fn","getPreviewMode","import_jsx_runtime","_a","IS_PROD","SEED_PERSONAS","forcePersona","apiKey","persona","sections","getRegistered","c","components","res","data","id","variantId","setVariantOverride","setPreviewMode","AdaptiveDevtools","open","setOpen","force","n","subscribeRegistry","overrides","getOverrides","choose","reset","clearVariantOverride","getPreviewMode","p","v"]}
|
|
1
|
+
{"version":3,"sources":["../src/devtools/index.tsx","../src/devtools-registry.ts","../src/devtools-overrides.ts","../src/preview-mode.ts","../src/override-events.ts","../src/devtools-config.ts"],"sourcesContent":["'use client';\nimport { useEffect, useReducer, useState, type CSSProperties } from 'react';\nimport { PERSONAS, PERSONA_DISPLAY, confidenceBand } from '@sentientui/policy';\nimport { SNAPSHOT_STORAGE_KEY_PREFIX } from '@sentientui/core';\nimport {\n getRegistered,\n getRegisteredSlots,\n getRegisteredSections,\n subscribeRegistry,\n type RegisteredSlot,\n} from '../devtools-registry.js';\nimport { setVariantOverride, clearVariantOverride, getOverrides } from '../devtools-overrides.js';\nimport { setPreviewMode, getPreviewMode } from '../preview-mode.js';\nimport { notifyOverridesChanged } from '../override-events.js';\nimport { readDevtoolsConfig, type DevtoolsConfig } from '../devtools-config.js';\n\ndeclare const process: { env?: { NODE_ENV?: string } } | undefined;\nconst IS_PROD = typeof process !== 'undefined' && process.env?.NODE_ENV === 'production';\nconst DEFAULT_API_BASE_URL = 'https://api.sentient-ui.com/v1';\n\nexport const LOCAL_MODE_DEVTOOLS_BANNER =\n 'Local mode — decisions are simulated; add a key to learn from real traffic';\n\ntype OutcomeToApply = {\n assignments?: Record<string, string>;\n layoutOrder?: string[] | null;\n slots?: Record<string, string | Record<string, string>>;\n personaAttributes?: { persona: string; confidence: 'low' | 'medium' | 'high' };\n};\n\ntype OverrideWindow = Window & {\n __sentient_layout_override?: string[];\n __sentient_slot_overrides?: Record<string, string | Record<string, string>>;\n};\n\n/** Apply a simulated outcome across every surface: variants, layout, slots/tokens, persona attrs. */\nfunction applyOutcome(result: OutcomeToApply): void {\n for (const [id, variantId] of Object.entries(result.assignments ?? {})) {\n setVariantOverride(id, variantId);\n }\n const w = window as unknown as OverrideWindow;\n if (result.layoutOrder && result.layoutOrder.length > 0) {\n w.__sentient_layout_override = result.layoutOrder;\n }\n if (result.slots) {\n w.__sentient_slot_overrides = { ...(w.__sentient_slot_overrides ?? {}), ...result.slots };\n }\n if (result.personaAttributes) {\n document.documentElement.dataset.sentientPersona = result.personaAttributes.persona;\n document.documentElement.dataset.sentientConfidence = result.personaAttributes.confidence;\n }\n setPreviewMode(true); // suppress events while previewing\n notifyOverridesChanged(); // re-render layout/slot consumers\n}\n\nfunction readSessionId(): string {\n try {\n const match = document.cookie.match(/(?:^|; )_snt_uid=([^;]*)/);\n if (match) return decodeURIComponent(match[1]);\n } catch {\n /* ignore */\n }\n return 'devtools-preview';\n}\n\nfunction slotDecls(): Array<{ id: string; arms?: string[]; dims?: RegisteredSlot['dims'] }> {\n return getRegisteredSlots().map((s) => ({\n id: s.id,\n ...(s.arms ? { arms: s.arms } : {}),\n ...(s.dims ? { dims: s.dims } : {}),\n }));\n}\n\n/** Keyed mode: simulate via /v1/explain (read-only, event-free). */\nasync function forcePersonaKeyed(apiKey: string, apiBaseUrl: string, persona: string): Promise<void> {\n const res = await fetch(`${apiBaseUrl}/explain`, {\n method: 'POST',\n headers: { 'content-type': 'application/json', authorization: `Bearer ${apiKey}` },\n body: JSON.stringify({\n persona,\n sections: getRegisteredSections().map((id) => ({ id })),\n components: getRegistered().map((c) => ({ id: c.id, variantIds: c.variantIds })),\n slots: slotDecls(),\n }),\n });\n if (!res.ok) return;\n const data = (await res.json()) as OutcomeToApply;\n applyOutcome({\n ...data,\n personaAttributes: data.personaAttributes ?? { persona, confidence: 'high' },\n });\n}\n\n/** Local mode: simulate via the deterministic local engine — zero network. */\nasync function forcePersonaLocal(persona: string): Promise<void> {\n const mod = await import('@sentientui/core/local');\n if (!mod.LOCAL_ENGINE_AVAILABLE) return;\n const outcome = mod\n .createLocalEngine({ sessionId: readSessionId(), forcedPersona: persona })\n .decide({\n sections: getRegisteredSections(),\n components: getRegistered().map((c) => ({ id: c.id, variantIds: c.variantIds })),\n slots: slotDecls(),\n });\n applyOutcome({\n assignments: outcome.assignments,\n layoutOrder: outcome.layoutOrder,\n slots: outcome.slots,\n personaAttributes: {\n persona: outcome.persona,\n confidence: confidenceBand(outcome.confidence),\n },\n });\n}\n\n/** Reset: clear the decision snapshot + every override, then reload to re-decide. */\nfunction resetAll(): void {\n for (const id of Object.keys(getOverrides())) clearVariantOverride(id);\n const w = window as unknown as OverrideWindow;\n delete w.__sentient_layout_override;\n delete w.__sentient_slot_overrides;\n delete document.documentElement.dataset.sentientPersona;\n delete document.documentElement.dataset.sentientConfidence;\n try {\n const stale: string[] = [];\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key && key.startsWith(SNAPSHOT_STORAGE_KEY_PREFIX)) stale.push(key);\n }\n for (const key of stale) localStorage.removeItem(key);\n } catch {\n /* ignore */\n }\n setPreviewMode(false);\n notifyOverridesChanged();\n try {\n window.location.reload();\n } catch {\n /* jsdom */\n }\n}\n\nconst btn = (active: boolean): CSSProperties => ({\n padding: '2px 8px',\n borderRadius: 4,\n border: '1px solid #444',\n background: active ? '#3b82f6' : '#222',\n color: '#eee',\n cursor: 'pointer',\n});\n\nexport function AdaptiveDevtools({ apiKey }: { apiKey?: string } = {}): JSX.Element | null {\n const [open, setOpen] = useState(false);\n const [activePersona, setActivePersona] = useState<string | null>(null);\n const [, force] = useReducer((n: number) => n + 1, 0);\n useEffect(() => subscribeRegistry(force), []);\n\n // Never render in production, even if imported by mistake. (Hooks run first\n // so the rules of hooks hold regardless of this compile-time constant.)\n if (IS_PROD) return null;\n\n const config: DevtoolsConfig = readDevtoolsConfig() ?? {\n apiKey: apiKey ?? '',\n apiBaseUrl: DEFAULT_API_BASE_URL,\n isLocal: false,\n };\n const useLocalEngine = config.isLocal || !config.apiKey;\n const components = getRegistered();\n const overrides = getOverrides();\n\n function choose(id: string, variantId: string): void {\n setVariantOverride(id, variantId);\n setPreviewMode(true);\n notifyOverridesChanged();\n force();\n }\n function resetVariant(id: string): void {\n clearVariantOverride(id);\n if (Object.keys(getOverrides()).length === 0) setPreviewMode(false);\n notifyOverridesChanged();\n force();\n }\n function choosePersona(persona: string): void {\n setActivePersona(persona);\n const apply = useLocalEngine\n ? forcePersonaLocal(persona)\n : forcePersonaKeyed(config.apiKey, config.apiBaseUrl, persona);\n void apply.then(force);\n }\n\n return (\n <div style={{ position: 'fixed', bottom: 16, right: 16, zIndex: 2147483647, fontFamily: 'system-ui' }}>\n {open && (\n <div style={{ width: 300, maxHeight: 460, overflowY: 'auto', background: '#111', color: '#eee',\n borderRadius: 8, padding: 12, marginBottom: 8, boxShadow: '0 8px 30px rgba(0,0,0,.4)', fontSize: 12 }}>\n {config.isLocal && (\n <div style={{ background: '#1e293b', border: '1px solid #334155', borderRadius: 4,\n padding: '6px 8px', marginBottom: 8 }}>\n {LOCAL_MODE_DEVTOOLS_BANNER}\n </div>\n )}\n <div style={{ opacity: .7, marginBottom: 8 }}>\n {components.length} component{components.length === 1 ? '' : 's'} · {getPreviewMode() ? 'preview — writing nothing' : 'live'}\n </div>\n <div style={{ borderBottom: '1px solid #333', paddingBottom: 8, marginBottom: 8 }}>\n <div style={{ opacity: .7, marginBottom: 4 }}>Preview persona</div>\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4 }}>\n {PERSONAS.map((p) => (\n <button key={p} onClick={() => choosePersona(p)} style={btn(activePersona === p)}>\n {PERSONA_DISPLAY[p]}\n </button>\n ))}\n {(activePersona !== null || Object.keys(overrides).length > 0) && (\n <button onClick={resetAll} style={{ ...btn(false), color: '#aaa' }}>\n Reset\n </button>\n )}\n </div>\n </div>\n {components.length === 0 && <div style={{ opacity: .6 }}>No components on this page yet.</div>}\n {components.map((c) => (\n <div key={c.id} style={{ borderTop: '1px solid #333', padding: '8px 0' }}>\n <div style={{ fontWeight: 600 }}>{c.id}{c.goal ? ` · goal: ${c.goal}` : ''}</div>\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4, marginTop: 4 }}>\n {c.variantIds.map((v) => (\n <button key={v} onClick={() => choose(c.id, v)} style={btn(overrides[c.id] === v)}>\n {v}\n </button>\n ))}\n {overrides[c.id] && (\n <button onClick={() => resetVariant(c.id)} style={{ ...btn(false), color: '#aaa' }}>\n reset\n </button>\n )}\n </div>\n </div>\n ))}\n </div>\n )}\n <button aria-label=\"Sentient DevTools\" onClick={() => setOpen((o) => !o)}\n style={{ width: 40, height: 40, borderRadius: 20, border: 'none', background: '#3b82f6', color: '#fff',\n cursor: 'pointer', boxShadow: '0 4px 14px rgba(0,0,0,.3)' }}>◧</button>\n </div>\n );\n}\n","export type RegisteredComponent = { id: string; variantIds: string[]; goal?: string };\nexport type RegisteredSlot = {\n id: string;\n arms?: string[];\n dims?: Record<string, readonly string[]>;\n};\n\ntype RegistryState = {\n components: Map<string, RegisteredComponent>;\n slots: Map<string, RegisteredSlot>;\n sections: string[];\n listeners: Set<() => void>;\n};\n\n// Shared through a window global: the main entry and the /devtools entry are\n// separate bundles, each with its own copy of this module — module-local\n// state would give the devtools an always-empty registry in published apps.\nconst ssrFallback: RegistryState = {\n components: new Map(),\n slots: new Map(),\n sections: [],\n listeners: new Set(),\n};\n\nfunction state(): RegistryState {\n if (typeof window === 'undefined') return ssrFallback;\n const w = window as unknown as { __sentient_registry?: RegistryState };\n if (!w.__sentient_registry) {\n w.__sentient_registry = {\n components: new Map(),\n slots: new Map(),\n sections: [],\n listeners: new Set(),\n };\n }\n return w.__sentient_registry;\n}\n\nfunction emit(): void {\n for (const fn of state().listeners) fn();\n}\n\n/** Register (or re-register) a component. Returns an unregister function. */\nexport function registerComponent(c: RegisteredComponent): () => void {\n state().components.set(c.id, c);\n emit();\n return () => {\n state().components.delete(c.id);\n emit();\n };\n}\n\n/** Register (or re-register) a slot declaration. Returns an unregister function. */\nexport function registerSlot(s: RegisteredSlot): () => void {\n state().slots.set(s.id, s);\n emit();\n return () => {\n state().slots.delete(s.id);\n emit();\n };\n}\n\n/** Register the page's declared section ids (from AdaptiveRoot/provider). */\nexport function registerSections(sections: string[]): void {\n state().sections = [...sections];\n emit();\n}\n\nexport function getRegistered(): RegisteredComponent[] {\n return [...state().components.values()];\n}\n\nexport function getRegisteredSlots(): RegisteredSlot[] {\n return [...state().slots.values()];\n}\n\nexport function getRegisteredSections(): string[] {\n return [...state().sections];\n}\n\nexport function subscribeRegistry(fn: () => void): () => void {\n const listeners = state().listeners;\n listeners.add(fn);\n return () => {\n listeners.delete(fn);\n };\n}\n","function store(): Record<string, string> {\n const w = window as unknown as { __sentient_overrides?: Record<string, string> };\n if (!w.__sentient_overrides) w.__sentient_overrides = {};\n return w.__sentient_overrides;\n}\n\n/** Force `componentId` to render `variantId` — read by `useAssignment`. */\nexport function setVariantOverride(componentId: string, variantId: string): void {\n store()[componentId] = variantId;\n}\n\nexport function clearVariantOverride(componentId: string): void {\n delete store()[componentId];\n}\n\nexport function getOverrides(): Record<string, string> {\n return { ...store() };\n}\n","import type { SentientClient } from '@sentientui/core';\n\ntype PreviewState = { on: boolean; listeners: Set<() => void> };\nconst ssrFallback: PreviewState = { on: false, listeners: new Set() };\n\n// Window-backed: the /devtools entry (a separate bundle) toggles preview mode\n// and the provider (main bundle) must observe it.\nfunction state(): PreviewState {\n if (typeof window === 'undefined') return ssrFallback;\n const w = window as unknown as { __sentient_preview?: PreviewState };\n if (!w.__sentient_preview) w.__sentient_preview = { on: false, listeners: new Set() };\n return w.__sentient_preview;\n}\n\nexport function setPreviewMode(on: boolean): void {\n const s = state();\n if (s.on === on) return;\n s.on = on;\n for (const fn of s.listeners) fn();\n}\n\nexport function getPreviewMode(): boolean {\n return state().on;\n}\n\nexport function subscribePreview(fn: () => void): () => void {\n const listeners = state().listeners;\n listeners.add(fn);\n return () => {\n listeners.delete(fn);\n };\n}\n\n/**\n * Wraps a client so it writes nothing: reads pass through, all emitters no-op.\n * Used while previewing variants/personas so no `variant_assigned`, goal, or\n * session events are sent.\n */\nexport function createPreviewClient(inner: SentientClient): SentientClient {\n return {\n isLocal: inner.isLocal,\n track: () => undefined,\n goal: () => undefined,\n componentGoal: () => undefined,\n identify: () => undefined,\n fetchWeights: () => Promise.resolve([]),\n getAssignment: (componentId, segment) => inner.getAssignment(componentId, segment),\n assign: (componentId, variantIds, agentData, agentDataByVariant) =>\n inner.assign(componentId, variantIds, agentData, agentDataByVariant),\n // Reads pass through; decide is a write (slot decisions persist server-side)\n // so preview mode never issues it.\n decide: () => Promise.resolve(null),\n getSlotResult: (slotId) => inner.getSlotResult(slotId),\n getPersona: () => inner.getPersona(),\n getGraph: () => inner.getGraph(),\n destroy: () => inner.destroy(),\n };\n}\n","/**\n * Cross-bundle re-render bus for devtools overrides. The main entry and the\n * /devtools entry are separate bundles, so state and notifications go through\n * window (a version counter + a DOM event) — never module-local state.\n */\nconst EVENT = 'sentient:overrides-changed';\n\ntype VersionWindow = Window & { __sentient_overrides_version?: number };\n\nexport function getOverridesVersion(): number {\n if (typeof window === 'undefined') return 0;\n return (window as VersionWindow).__sentient_overrides_version ?? 0;\n}\n\nexport function notifyOverridesChanged(): void {\n if (typeof window === 'undefined') return;\n const w = window as VersionWindow;\n w.__sentient_overrides_version = (w.__sentient_overrides_version ?? 0) + 1;\n window.dispatchEvent(new Event(EVENT));\n}\n\nexport function subscribeOverridesChanged(fn: () => void): () => void {\n if (typeof window === 'undefined') return () => undefined;\n window.addEventListener(EVENT, fn);\n return () => window.removeEventListener(EVENT, fn);\n}\n","/** Provider → devtools config handoff. Window-backed: the /devtools entry is a\n * separate bundle and cannot share the provider's React context instance. */\nexport type DevtoolsConfig = {\n apiKey: string;\n apiBaseUrl: string;\n isLocal: boolean;\n};\n\ntype ConfigWindow = Window & { __sentient_devtools_config?: DevtoolsConfig };\n\nexport function publishDevtoolsConfig(config: DevtoolsConfig): void {\n if (typeof window === 'undefined') return;\n (window as ConfigWindow).__sentient_devtools_config = config;\n}\n\nexport function readDevtoolsConfig(): DevtoolsConfig | null {\n if (typeof window === 'undefined') return null;\n return (window as ConfigWindow).__sentient_devtools_config ?? null;\n}\n"],"mappings":"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","names":["devtools_exports","__export","AdaptiveDevtools","LOCAL_MODE_DEVTOOLS_BANNER","__toCommonJS","import_react","import_policy","import_core","ssrFallback","state","w","getRegistered","state","getRegisteredSlots","getRegisteredSections","subscribeRegistry","fn","listeners","store","w","setVariantOverride","componentId","variantId","clearVariantOverride","getOverrides","__spreadValues","ssrFallback","state","w","setPreviewMode","on","s","fn","getPreviewMode","EVENT","notifyOverridesChanged","_a","w","EVENT","readDevtoolsConfig","_a","import_jsx_runtime","_a","IS_PROD","DEFAULT_API_BASE_URL","LOCAL_MODE_DEVTOOLS_BANNER","applyOutcome","result","_b","id","variantId","setVariantOverride","w","__spreadValues","setPreviewMode","notifyOverridesChanged","readSessionId","match","slotDecls","getRegisteredSlots","s","forcePersonaKeyed","apiKey","apiBaseUrl","persona","res","getRegisteredSections","getRegistered","c","data","__spreadProps","forcePersonaLocal","mod","outcome","resetAll","getOverrides","clearVariantOverride","stale","i","key","e","btn","active","AdaptiveDevtools","open","setOpen","activePersona","setActivePersona","force","n","subscribeRegistry","config","readDevtoolsConfig","useLocalEngine","components","overrides","choose","resetVariant","choosePersona","getPreviewMode","p","v"]}
|
package/dist/devtools.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
"use client";var
|
|
2
|
+
"use client";var j=Object.defineProperty,F=Object.defineProperties;var G=Object.getOwnPropertyDescriptors;var C=Object.getOwnPropertySymbols;var z=Object.prototype.hasOwnProperty,K=Object.prototype.propertyIsEnumerable;var P=(e,t,n)=>t in e?j(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,r=(e,t)=>{for(var n in t||(t={}))z.call(t,n)&&P(e,n,t[n]);if(C)for(var n of C(t))K.call(t,n)&&P(e,n,t[n]);return e},v=(e,t)=>F(e,G(t));import{useEffect as J,useReducer as X,useState as I}from"react";import{PERSONAS as q,PERSONA_DISPLAY as Q,confidenceBand as Z}from"@sentientui/policy";import{SNAPSHOT_STORAGE_KEY_PREFIX as ee}from"@sentientui/core";var Y={components:new Map,slots:new Map,sections:[],listeners:new Set};function m(){if(typeof window=="undefined")return Y;let e=window;return e.__sentient_registry||(e.__sentient_registry={components:new Map,slots:new Map,sections:[],listeners:new Set}),e.__sentient_registry}function _(){return[...m().components.values()]}function E(){return[...m().slots.values()]}function S(){return[...m().sections]}function A(e){let t=m().listeners;return t.add(e),()=>{t.delete(e)}}function h(){let e=window;return e.__sentient_overrides||(e.__sentient_overrides={}),e.__sentient_overrides}function R(e,t){h()[e]=t}function x(e){delete h()[e]}function w(){return r({},h())}var $={on:!1,listeners:new Set};function k(){if(typeof window=="undefined")return $;let e=window;return e.__sentient_preview||(e.__sentient_preview={on:!1,listeners:new Set}),e.__sentient_preview}function p(e){let t=k();if(t.on!==e){t.on=e;for(let n of t.listeners)n()}}function L(){return k().on}var H="sentient:overrides-changed";function u(){var t;if(typeof window=="undefined")return;let e=window;e.__sentient_overrides_version=((t=e.__sentient_overrides_version)!=null?t:0)+1,window.dispatchEvent(new Event(H))}function D(){var e;return typeof window=="undefined"?null:(e=window.__sentient_devtools_config)!=null?e:null}import{jsx as a,jsxs as l}from"react/jsx-runtime";var W,te=typeof process!="undefined"&&((W=process.env)==null?void 0:W.NODE_ENV)==="production",ne="https://api.sentient-ui.com/v1",oe="Local mode \u2014 decisions are simulated; add a key to learn from real traffic";function B(e){var n,i;for(let[g,d]of Object.entries((n=e.assignments)!=null?n:{}))R(g,d);let t=window;e.layoutOrder&&e.layoutOrder.length>0&&(t.__sentient_layout_override=e.layoutOrder),e.slots&&(t.__sentient_slot_overrides=r(r({},(i=t.__sentient_slot_overrides)!=null?i:{}),e.slots)),e.personaAttributes&&(document.documentElement.dataset.sentientPersona=e.personaAttributes.persona,document.documentElement.dataset.sentientConfidence=e.personaAttributes.confidence),p(!0),u()}function ie(){try{let e=document.cookie.match(/(?:^|; )_snt_uid=([^;]*)/);if(e)return decodeURIComponent(e[1])}catch(e){}return"devtools-preview"}function N(){return E().map(e=>r(r({id:e.id},e.arms?{arms:e.arms}:{}),e.dims?{dims:e.dims}:{}))}async function se(e,t,n){var d;let i=await fetch(`${t}/explain`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${e}`},body:JSON.stringify({persona:n,sections:S().map(s=>({id:s})),components:_().map(s=>({id:s.id,variantIds:s.variantIds})),slots:N()})});if(!i.ok)return;let g=await i.json();B(v(r({},g),{personaAttributes:(d=g.personaAttributes)!=null?d:{persona:n,confidence:"high"}}))}async function re(e){let t=await import("@sentientui/core/local");if(!t.LOCAL_ENGINE_AVAILABLE)return;let n=t.createLocalEngine({sessionId:ie(),forcedPersona:e}).decide({sections:S(),components:_().map(i=>({id:i.id,variantIds:i.variantIds})),slots:N()});B({assignments:n.assignments,layoutOrder:n.layoutOrder,slots:n.slots,personaAttributes:{persona:n.persona,confidence:Z(n.confidence)}})}function de(){for(let t of Object.keys(w()))x(t);let e=window;delete e.__sentient_layout_override,delete e.__sentient_slot_overrides,delete document.documentElement.dataset.sentientPersona,delete document.documentElement.dataset.sentientConfidence;try{let t=[];for(let n=0;n<localStorage.length;n++){let i=localStorage.key(n);i&&i.startsWith(ee)&&t.push(i)}for(let n of t)localStorage.removeItem(n)}catch(t){}p(!1),u();try{window.location.reload()}catch(t){}}var y=e=>({padding:"2px 8px",borderRadius:4,border:"1px solid #444",background:e?"#3b82f6":"#222",color:"#eee",cursor:"pointer"});function Re({apiKey:e}={}){var O;let[t,n]=I(!1),[i,g]=I(null),[,d]=X(o=>o+1,0);if(J(()=>A(d),[]),te)return null;let s=(O=D())!=null?O:{apiKey:e!=null?e:"",apiBaseUrl:ne,isLocal:!1},V=s.isLocal||!s.apiKey,f=_(),b=w();function T(o,c){R(o,c),p(!0),u(),d()}function M(o){x(o),Object.keys(w()).length===0&&p(!1),u(),d()}function U(o){g(o),(V?re(o):se(s.apiKey,s.apiBaseUrl,o)).then(d)}return l("div",{style:{position:"fixed",bottom:16,right:16,zIndex:2147483647,fontFamily:"system-ui"},children:[t&&l("div",{style:{width:300,maxHeight:460,overflowY:"auto",background:"#111",color:"#eee",borderRadius:8,padding:12,marginBottom:8,boxShadow:"0 8px 30px rgba(0,0,0,.4)",fontSize:12},children:[s.isLocal&&a("div",{style:{background:"#1e293b",border:"1px solid #334155",borderRadius:4,padding:"6px 8px",marginBottom:8},children:oe}),l("div",{style:{opacity:.7,marginBottom:8},children:[f.length," component",f.length===1?"":"s"," \xB7 ",L()?"preview \u2014 writing nothing":"live"]}),l("div",{style:{borderBottom:"1px solid #333",paddingBottom:8,marginBottom:8},children:[a("div",{style:{opacity:.7,marginBottom:4},children:"Preview persona"}),l("div",{style:{display:"flex",flexWrap:"wrap",gap:4},children:[q.map(o=>a("button",{onClick:()=>U(o),style:y(i===o),children:Q[o]},o)),(i!==null||Object.keys(b).length>0)&&a("button",{onClick:de,style:v(r({},y(!1)),{color:"#aaa"}),children:"Reset"})]})]}),f.length===0&&a("div",{style:{opacity:.6},children:"No components on this page yet."}),f.map(o=>l("div",{style:{borderTop:"1px solid #333",padding:"8px 0"},children:[l("div",{style:{fontWeight:600},children:[o.id,o.goal?` \xB7 goal: ${o.goal}`:""]}),l("div",{style:{display:"flex",flexWrap:"wrap",gap:4,marginTop:4},children:[o.variantIds.map(c=>a("button",{onClick:()=>T(o.id,c),style:y(b[o.id]===c),children:c},c)),b[o.id]&&a("button",{onClick:()=>M(o.id),style:v(r({},y(!1)),{color:"#aaa"}),children:"reset"})]})]},o.id))]}),a("button",{"aria-label":"Sentient DevTools",onClick:()=>n(o=>!o),style:{width:40,height:40,borderRadius:20,border:"none",background:"#3b82f6",color:"#fff",cursor:"pointer",boxShadow:"0 4px 14px rgba(0,0,0,.3)"},children:"\u25E7"})]})}export{Re as AdaptiveDevtools,oe as LOCAL_MODE_DEVTOOLS_BANNER};
|
|
3
3
|
//# sourceMappingURL=devtools.mjs.map
|
package/dist/devtools.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/devtools/index.tsx","../src/devtools-registry.ts","../src/devtools-overrides.ts","../src/preview-mode.ts"],"sourcesContent":["'use client';\nimport { useEffect, useReducer, useState } from 'react';\nimport { getRegistered, subscribeRegistry } from '../devtools-registry.js';\nimport { setVariantOverride, clearVariantOverride, getOverrides } from '../devtools-overrides.js';\nimport { setPreviewMode, getPreviewMode } from '../preview-mode.js';\n\ndeclare const process: { env?: { NODE_ENV?: string } } | undefined;\nconst IS_PROD = typeof process !== 'undefined' && process.env?.NODE_ENV === 'production';\n\nconst SEED_PERSONAS = ['browsers', 'buyers', 'researchers', 'deal-seekers'];\n\n/** Simulate a persona via /v1/explain (read-only) and apply the result as overrides. */\nasync function forcePersona(apiKey: string, persona: string): Promise<void> {\n const sections = getRegistered().map((c) => ({ id: c.id }));\n const components = getRegistered().map((c) => ({ id: c.id, variantIds: c.variantIds }));\n const res = await fetch('/v1/explain', {\n method: 'POST',\n headers: { 'content-type': 'application/json', authorization: `Bearer ${apiKey}` },\n body: JSON.stringify({ persona, sections, components }),\n });\n if (!res.ok) return;\n const data = (await res.json()) as { assignments: Record<string, string> };\n for (const [id, variantId] of Object.entries(data.assignments)) setVariantOverride(id, variantId);\n setPreviewMode(true);\n}\n\nexport function AdaptiveDevtools({ apiKey }: { apiKey?: string } = {}): JSX.Element | null {\n const [open, setOpen] = useState(false);\n const [, force] = useReducer((n: number) => n + 1, 0);\n useEffect(() => subscribeRegistry(force), []);\n\n // Never render in production, even if imported by mistake. (Hooks run first\n // so the rules of hooks hold regardless of this compile-time constant.)\n if (IS_PROD) return null;\n\n const components = getRegistered();\n const overrides = getOverrides();\n\n function choose(id: string, variantId: string): void {\n setVariantOverride(id, variantId);\n setPreviewMode(true); // suppress events while previewing\n force();\n }\n function reset(id: string): void {\n clearVariantOverride(id);\n if (Object.keys(getOverrides()).length === 0) setPreviewMode(false);\n force();\n }\n\n return (\n <div style={{ position: 'fixed', bottom: 16, right: 16, zIndex: 2147483647, fontFamily: 'system-ui' }}>\n {open && (\n <div style={{ width: 300, maxHeight: 420, overflowY: 'auto', background: '#111', color: '#eee',\n borderRadius: 8, padding: 12, marginBottom: 8, boxShadow: '0 8px 30px rgba(0,0,0,.4)', fontSize: 12 }}>\n <div style={{ opacity: .7, marginBottom: 8 }}>\n {components.length} component{components.length === 1 ? '' : 's'} · {getPreviewMode() ? 'preview — writing nothing' : 'live'}\n </div>\n {apiKey && (\n <div style={{ borderBottom: '1px solid #333', paddingBottom: 8, marginBottom: 8 }}>\n <div style={{ opacity: .7, marginBottom: 4 }}>Preview persona</div>\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4 }}>\n {SEED_PERSONAS.map((p) => (\n <button key={p} onClick={() => void forcePersona(apiKey, p).then(force)}\n style={{ padding: '2px 8px', borderRadius: 4, border: '1px solid #444', background: '#222', color: '#eee', cursor: 'pointer' }}>\n {p}\n </button>\n ))}\n </div>\n </div>\n )}\n {components.length === 0 && <div style={{ opacity: .6 }}>No components on this page yet.</div>}\n {components.map((c) => (\n <div key={c.id} style={{ borderTop: '1px solid #333', padding: '8px 0' }}>\n <div style={{ fontWeight: 600 }}>{c.id}{c.goal ? ` · goal: ${c.goal}` : ''}</div>\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4, marginTop: 4 }}>\n {c.variantIds.map((v) => (\n <button key={v} onClick={() => choose(c.id, v)}\n style={{ padding: '2px 8px', borderRadius: 4, border: '1px solid #444',\n background: overrides[c.id] === v ? '#3b82f6' : '#222', color: '#eee', cursor: 'pointer' }}>\n {v}\n </button>\n ))}\n {overrides[c.id] && (\n <button onClick={() => reset(c.id)}\n style={{ padding: '2px 8px', borderRadius: 4, border: '1px solid #444', background: '#222', color: '#aaa', cursor: 'pointer' }}>\n reset\n </button>\n )}\n </div>\n </div>\n ))}\n </div>\n )}\n <button aria-label=\"Sentient DevTools\" onClick={() => setOpen((o) => !o)}\n style={{ width: 40, height: 40, borderRadius: 20, border: 'none', background: '#3b82f6', color: '#fff',\n cursor: 'pointer', boxShadow: '0 4px 14px rgba(0,0,0,.3)' }}>◧</button>\n </div>\n );\n}\n","export type RegisteredComponent = { id: string; variantIds: string[]; goal?: string };\n\nconst registry = new Map<string, RegisteredComponent>();\nconst listeners = new Set<() => void>();\n\nfunction emit(): void {\n for (const fn of listeners) fn();\n}\n\n/** Register (or re-register) a component. Returns an unregister function. */\nexport function registerComponent(c: RegisteredComponent): () => void {\n registry.set(c.id, c);\n emit();\n return () => {\n registry.delete(c.id);\n emit();\n };\n}\n\nexport function getRegistered(): RegisteredComponent[] {\n return [...registry.values()];\n}\n\nexport function subscribeRegistry(fn: () => void): () => void {\n listeners.add(fn);\n return () => {\n listeners.delete(fn);\n };\n}\n","function store(): Record<string, string> {\n const w = window as unknown as { __sentient_overrides?: Record<string, string> };\n if (!w.__sentient_overrides) w.__sentient_overrides = {};\n return w.__sentient_overrides;\n}\n\n/** Force `componentId` to render `variantId` — read by `useAssignment`. */\nexport function setVariantOverride(componentId: string, variantId: string): void {\n store()[componentId] = variantId;\n}\n\nexport function clearVariantOverride(componentId: string): void {\n delete store()[componentId];\n}\n\nexport function getOverrides(): Record<string, string> {\n return { ...store() };\n}\n","import type { SentientClient } from '@sentientui/core';\n\nlet previewOn = false;\nconst listeners = new Set<() => void>();\n\nexport function setPreviewMode(on: boolean): void {\n if (previewOn === on) return;\n previewOn = on;\n for (const fn of listeners) fn();\n}\n\nexport function getPreviewMode(): boolean {\n return previewOn;\n}\n\nexport function subscribePreview(fn: () => void): () => void {\n listeners.add(fn);\n return () => {\n listeners.delete(fn);\n };\n}\n\n/**\n * Wraps a client so it writes nothing: reads pass through, all emitters no-op.\n * Used while previewing variants/personas so no `variant_assigned`, goal, or\n * session events are sent.\n */\nexport function createPreviewClient(inner: SentientClient): SentientClient {\n return {\n track: () => undefined,\n goal: () => undefined,\n componentGoal: () => undefined,\n identify: () => undefined,\n fetchWeights: () => Promise.resolve([]),\n getAssignment: (componentId, segment) => inner.getAssignment(componentId, segment),\n assign: (componentId, variantIds, agentData, agentDataByVariant) =>\n inner.assign(componentId, variantIds, agentData, agentDataByVariant),\n getGraph: () => inner.getGraph(),\n destroy: () => inner.destroy(),\n };\n}\n"],"mappings":"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","names":["useEffect","useReducer","useState","registry","listeners","getRegistered","registry","subscribeRegistry","fn","listeners","store","w","setVariantOverride","componentId","variantId","clearVariantOverride","getOverrides","__spreadValues","previewOn","listeners","setPreviewMode","on","fn","getPreviewMode","jsx","jsxs","_a","IS_PROD","SEED_PERSONAS","forcePersona","apiKey","persona","sections","getRegistered","c","components","res","data","id","variantId","setVariantOverride","setPreviewMode","AdaptiveDevtools","open","setOpen","useState","force","useReducer","n","useEffect","subscribeRegistry","overrides","getOverrides","choose","reset","clearVariantOverride","getPreviewMode","p","v","o"]}
|
|
1
|
+
{"version":3,"sources":["../src/devtools/index.tsx","../src/devtools-registry.ts","../src/devtools-overrides.ts","../src/preview-mode.ts","../src/override-events.ts","../src/devtools-config.ts"],"sourcesContent":["'use client';\nimport { useEffect, useReducer, useState, type CSSProperties } from 'react';\nimport { PERSONAS, PERSONA_DISPLAY, confidenceBand } from '@sentientui/policy';\nimport { SNAPSHOT_STORAGE_KEY_PREFIX } from '@sentientui/core';\nimport {\n getRegistered,\n getRegisteredSlots,\n getRegisteredSections,\n subscribeRegistry,\n type RegisteredSlot,\n} from '../devtools-registry.js';\nimport { setVariantOverride, clearVariantOverride, getOverrides } from '../devtools-overrides.js';\nimport { setPreviewMode, getPreviewMode } from '../preview-mode.js';\nimport { notifyOverridesChanged } from '../override-events.js';\nimport { readDevtoolsConfig, type DevtoolsConfig } from '../devtools-config.js';\n\ndeclare const process: { env?: { NODE_ENV?: string } } | undefined;\nconst IS_PROD = typeof process !== 'undefined' && process.env?.NODE_ENV === 'production';\nconst DEFAULT_API_BASE_URL = 'https://api.sentient-ui.com/v1';\n\nexport const LOCAL_MODE_DEVTOOLS_BANNER =\n 'Local mode — decisions are simulated; add a key to learn from real traffic';\n\ntype OutcomeToApply = {\n assignments?: Record<string, string>;\n layoutOrder?: string[] | null;\n slots?: Record<string, string | Record<string, string>>;\n personaAttributes?: { persona: string; confidence: 'low' | 'medium' | 'high' };\n};\n\ntype OverrideWindow = Window & {\n __sentient_layout_override?: string[];\n __sentient_slot_overrides?: Record<string, string | Record<string, string>>;\n};\n\n/** Apply a simulated outcome across every surface: variants, layout, slots/tokens, persona attrs. */\nfunction applyOutcome(result: OutcomeToApply): void {\n for (const [id, variantId] of Object.entries(result.assignments ?? {})) {\n setVariantOverride(id, variantId);\n }\n const w = window as unknown as OverrideWindow;\n if (result.layoutOrder && result.layoutOrder.length > 0) {\n w.__sentient_layout_override = result.layoutOrder;\n }\n if (result.slots) {\n w.__sentient_slot_overrides = { ...(w.__sentient_slot_overrides ?? {}), ...result.slots };\n }\n if (result.personaAttributes) {\n document.documentElement.dataset.sentientPersona = result.personaAttributes.persona;\n document.documentElement.dataset.sentientConfidence = result.personaAttributes.confidence;\n }\n setPreviewMode(true); // suppress events while previewing\n notifyOverridesChanged(); // re-render layout/slot consumers\n}\n\nfunction readSessionId(): string {\n try {\n const match = document.cookie.match(/(?:^|; )_snt_uid=([^;]*)/);\n if (match) return decodeURIComponent(match[1]);\n } catch {\n /* ignore */\n }\n return 'devtools-preview';\n}\n\nfunction slotDecls(): Array<{ id: string; arms?: string[]; dims?: RegisteredSlot['dims'] }> {\n return getRegisteredSlots().map((s) => ({\n id: s.id,\n ...(s.arms ? { arms: s.arms } : {}),\n ...(s.dims ? { dims: s.dims } : {}),\n }));\n}\n\n/** Keyed mode: simulate via /v1/explain (read-only, event-free). */\nasync function forcePersonaKeyed(apiKey: string, apiBaseUrl: string, persona: string): Promise<void> {\n const res = await fetch(`${apiBaseUrl}/explain`, {\n method: 'POST',\n headers: { 'content-type': 'application/json', authorization: `Bearer ${apiKey}` },\n body: JSON.stringify({\n persona,\n sections: getRegisteredSections().map((id) => ({ id })),\n components: getRegistered().map((c) => ({ id: c.id, variantIds: c.variantIds })),\n slots: slotDecls(),\n }),\n });\n if (!res.ok) return;\n const data = (await res.json()) as OutcomeToApply;\n applyOutcome({\n ...data,\n personaAttributes: data.personaAttributes ?? { persona, confidence: 'high' },\n });\n}\n\n/** Local mode: simulate via the deterministic local engine — zero network. */\nasync function forcePersonaLocal(persona: string): Promise<void> {\n const mod = await import('@sentientui/core/local');\n if (!mod.LOCAL_ENGINE_AVAILABLE) return;\n const outcome = mod\n .createLocalEngine({ sessionId: readSessionId(), forcedPersona: persona })\n .decide({\n sections: getRegisteredSections(),\n components: getRegistered().map((c) => ({ id: c.id, variantIds: c.variantIds })),\n slots: slotDecls(),\n });\n applyOutcome({\n assignments: outcome.assignments,\n layoutOrder: outcome.layoutOrder,\n slots: outcome.slots,\n personaAttributes: {\n persona: outcome.persona,\n confidence: confidenceBand(outcome.confidence),\n },\n });\n}\n\n/** Reset: clear the decision snapshot + every override, then reload to re-decide. */\nfunction resetAll(): void {\n for (const id of Object.keys(getOverrides())) clearVariantOverride(id);\n const w = window as unknown as OverrideWindow;\n delete w.__sentient_layout_override;\n delete w.__sentient_slot_overrides;\n delete document.documentElement.dataset.sentientPersona;\n delete document.documentElement.dataset.sentientConfidence;\n try {\n const stale: string[] = [];\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key && key.startsWith(SNAPSHOT_STORAGE_KEY_PREFIX)) stale.push(key);\n }\n for (const key of stale) localStorage.removeItem(key);\n } catch {\n /* ignore */\n }\n setPreviewMode(false);\n notifyOverridesChanged();\n try {\n window.location.reload();\n } catch {\n /* jsdom */\n }\n}\n\nconst btn = (active: boolean): CSSProperties => ({\n padding: '2px 8px',\n borderRadius: 4,\n border: '1px solid #444',\n background: active ? '#3b82f6' : '#222',\n color: '#eee',\n cursor: 'pointer',\n});\n\nexport function AdaptiveDevtools({ apiKey }: { apiKey?: string } = {}): JSX.Element | null {\n const [open, setOpen] = useState(false);\n const [activePersona, setActivePersona] = useState<string | null>(null);\n const [, force] = useReducer((n: number) => n + 1, 0);\n useEffect(() => subscribeRegistry(force), []);\n\n // Never render in production, even if imported by mistake. (Hooks run first\n // so the rules of hooks hold regardless of this compile-time constant.)\n if (IS_PROD) return null;\n\n const config: DevtoolsConfig = readDevtoolsConfig() ?? {\n apiKey: apiKey ?? '',\n apiBaseUrl: DEFAULT_API_BASE_URL,\n isLocal: false,\n };\n const useLocalEngine = config.isLocal || !config.apiKey;\n const components = getRegistered();\n const overrides = getOverrides();\n\n function choose(id: string, variantId: string): void {\n setVariantOverride(id, variantId);\n setPreviewMode(true);\n notifyOverridesChanged();\n force();\n }\n function resetVariant(id: string): void {\n clearVariantOverride(id);\n if (Object.keys(getOverrides()).length === 0) setPreviewMode(false);\n notifyOverridesChanged();\n force();\n }\n function choosePersona(persona: string): void {\n setActivePersona(persona);\n const apply = useLocalEngine\n ? forcePersonaLocal(persona)\n : forcePersonaKeyed(config.apiKey, config.apiBaseUrl, persona);\n void apply.then(force);\n }\n\n return (\n <div style={{ position: 'fixed', bottom: 16, right: 16, zIndex: 2147483647, fontFamily: 'system-ui' }}>\n {open && (\n <div style={{ width: 300, maxHeight: 460, overflowY: 'auto', background: '#111', color: '#eee',\n borderRadius: 8, padding: 12, marginBottom: 8, boxShadow: '0 8px 30px rgba(0,0,0,.4)', fontSize: 12 }}>\n {config.isLocal && (\n <div style={{ background: '#1e293b', border: '1px solid #334155', borderRadius: 4,\n padding: '6px 8px', marginBottom: 8 }}>\n {LOCAL_MODE_DEVTOOLS_BANNER}\n </div>\n )}\n <div style={{ opacity: .7, marginBottom: 8 }}>\n {components.length} component{components.length === 1 ? '' : 's'} · {getPreviewMode() ? 'preview — writing nothing' : 'live'}\n </div>\n <div style={{ borderBottom: '1px solid #333', paddingBottom: 8, marginBottom: 8 }}>\n <div style={{ opacity: .7, marginBottom: 4 }}>Preview persona</div>\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4 }}>\n {PERSONAS.map((p) => (\n <button key={p} onClick={() => choosePersona(p)} style={btn(activePersona === p)}>\n {PERSONA_DISPLAY[p]}\n </button>\n ))}\n {(activePersona !== null || Object.keys(overrides).length > 0) && (\n <button onClick={resetAll} style={{ ...btn(false), color: '#aaa' }}>\n Reset\n </button>\n )}\n </div>\n </div>\n {components.length === 0 && <div style={{ opacity: .6 }}>No components on this page yet.</div>}\n {components.map((c) => (\n <div key={c.id} style={{ borderTop: '1px solid #333', padding: '8px 0' }}>\n <div style={{ fontWeight: 600 }}>{c.id}{c.goal ? ` · goal: ${c.goal}` : ''}</div>\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4, marginTop: 4 }}>\n {c.variantIds.map((v) => (\n <button key={v} onClick={() => choose(c.id, v)} style={btn(overrides[c.id] === v)}>\n {v}\n </button>\n ))}\n {overrides[c.id] && (\n <button onClick={() => resetVariant(c.id)} style={{ ...btn(false), color: '#aaa' }}>\n reset\n </button>\n )}\n </div>\n </div>\n ))}\n </div>\n )}\n <button aria-label=\"Sentient DevTools\" onClick={() => setOpen((o) => !o)}\n style={{ width: 40, height: 40, borderRadius: 20, border: 'none', background: '#3b82f6', color: '#fff',\n cursor: 'pointer', boxShadow: '0 4px 14px rgba(0,0,0,.3)' }}>◧</button>\n </div>\n );\n}\n","export type RegisteredComponent = { id: string; variantIds: string[]; goal?: string };\nexport type RegisteredSlot = {\n id: string;\n arms?: string[];\n dims?: Record<string, readonly string[]>;\n};\n\ntype RegistryState = {\n components: Map<string, RegisteredComponent>;\n slots: Map<string, RegisteredSlot>;\n sections: string[];\n listeners: Set<() => void>;\n};\n\n// Shared through a window global: the main entry and the /devtools entry are\n// separate bundles, each with its own copy of this module — module-local\n// state would give the devtools an always-empty registry in published apps.\nconst ssrFallback: RegistryState = {\n components: new Map(),\n slots: new Map(),\n sections: [],\n listeners: new Set(),\n};\n\nfunction state(): RegistryState {\n if (typeof window === 'undefined') return ssrFallback;\n const w = window as unknown as { __sentient_registry?: RegistryState };\n if (!w.__sentient_registry) {\n w.__sentient_registry = {\n components: new Map(),\n slots: new Map(),\n sections: [],\n listeners: new Set(),\n };\n }\n return w.__sentient_registry;\n}\n\nfunction emit(): void {\n for (const fn of state().listeners) fn();\n}\n\n/** Register (or re-register) a component. Returns an unregister function. */\nexport function registerComponent(c: RegisteredComponent): () => void {\n state().components.set(c.id, c);\n emit();\n return () => {\n state().components.delete(c.id);\n emit();\n };\n}\n\n/** Register (or re-register) a slot declaration. Returns an unregister function. */\nexport function registerSlot(s: RegisteredSlot): () => void {\n state().slots.set(s.id, s);\n emit();\n return () => {\n state().slots.delete(s.id);\n emit();\n };\n}\n\n/** Register the page's declared section ids (from AdaptiveRoot/provider). */\nexport function registerSections(sections: string[]): void {\n state().sections = [...sections];\n emit();\n}\n\nexport function getRegistered(): RegisteredComponent[] {\n return [...state().components.values()];\n}\n\nexport function getRegisteredSlots(): RegisteredSlot[] {\n return [...state().slots.values()];\n}\n\nexport function getRegisteredSections(): string[] {\n return [...state().sections];\n}\n\nexport function subscribeRegistry(fn: () => void): () => void {\n const listeners = state().listeners;\n listeners.add(fn);\n return () => {\n listeners.delete(fn);\n };\n}\n","function store(): Record<string, string> {\n const w = window as unknown as { __sentient_overrides?: Record<string, string> };\n if (!w.__sentient_overrides) w.__sentient_overrides = {};\n return w.__sentient_overrides;\n}\n\n/** Force `componentId` to render `variantId` — read by `useAssignment`. */\nexport function setVariantOverride(componentId: string, variantId: string): void {\n store()[componentId] = variantId;\n}\n\nexport function clearVariantOverride(componentId: string): void {\n delete store()[componentId];\n}\n\nexport function getOverrides(): Record<string, string> {\n return { ...store() };\n}\n","import type { SentientClient } from '@sentientui/core';\n\ntype PreviewState = { on: boolean; listeners: Set<() => void> };\nconst ssrFallback: PreviewState = { on: false, listeners: new Set() };\n\n// Window-backed: the /devtools entry (a separate bundle) toggles preview mode\n// and the provider (main bundle) must observe it.\nfunction state(): PreviewState {\n if (typeof window === 'undefined') return ssrFallback;\n const w = window as unknown as { __sentient_preview?: PreviewState };\n if (!w.__sentient_preview) w.__sentient_preview = { on: false, listeners: new Set() };\n return w.__sentient_preview;\n}\n\nexport function setPreviewMode(on: boolean): void {\n const s = state();\n if (s.on === on) return;\n s.on = on;\n for (const fn of s.listeners) fn();\n}\n\nexport function getPreviewMode(): boolean {\n return state().on;\n}\n\nexport function subscribePreview(fn: () => void): () => void {\n const listeners = state().listeners;\n listeners.add(fn);\n return () => {\n listeners.delete(fn);\n };\n}\n\n/**\n * Wraps a client so it writes nothing: reads pass through, all emitters no-op.\n * Used while previewing variants/personas so no `variant_assigned`, goal, or\n * session events are sent.\n */\nexport function createPreviewClient(inner: SentientClient): SentientClient {\n return {\n isLocal: inner.isLocal,\n track: () => undefined,\n goal: () => undefined,\n componentGoal: () => undefined,\n identify: () => undefined,\n fetchWeights: () => Promise.resolve([]),\n getAssignment: (componentId, segment) => inner.getAssignment(componentId, segment),\n assign: (componentId, variantIds, agentData, agentDataByVariant) =>\n inner.assign(componentId, variantIds, agentData, agentDataByVariant),\n // Reads pass through; decide is a write (slot decisions persist server-side)\n // so preview mode never issues it.\n decide: () => Promise.resolve(null),\n getSlotResult: (slotId) => inner.getSlotResult(slotId),\n getPersona: () => inner.getPersona(),\n getGraph: () => inner.getGraph(),\n destroy: () => inner.destroy(),\n };\n}\n","/**\n * Cross-bundle re-render bus for devtools overrides. The main entry and the\n * /devtools entry are separate bundles, so state and notifications go through\n * window (a version counter + a DOM event) — never module-local state.\n */\nconst EVENT = 'sentient:overrides-changed';\n\ntype VersionWindow = Window & { __sentient_overrides_version?: number };\n\nexport function getOverridesVersion(): number {\n if (typeof window === 'undefined') return 0;\n return (window as VersionWindow).__sentient_overrides_version ?? 0;\n}\n\nexport function notifyOverridesChanged(): void {\n if (typeof window === 'undefined') return;\n const w = window as VersionWindow;\n w.__sentient_overrides_version = (w.__sentient_overrides_version ?? 0) + 1;\n window.dispatchEvent(new Event(EVENT));\n}\n\nexport function subscribeOverridesChanged(fn: () => void): () => void {\n if (typeof window === 'undefined') return () => undefined;\n window.addEventListener(EVENT, fn);\n return () => window.removeEventListener(EVENT, fn);\n}\n","/** Provider → devtools config handoff. Window-backed: the /devtools entry is a\n * separate bundle and cannot share the provider's React context instance. */\nexport type DevtoolsConfig = {\n apiKey: string;\n apiBaseUrl: string;\n isLocal: boolean;\n};\n\ntype ConfigWindow = Window & { __sentient_devtools_config?: DevtoolsConfig };\n\nexport function publishDevtoolsConfig(config: DevtoolsConfig): void {\n if (typeof window === 'undefined') return;\n (window as ConfigWindow).__sentient_devtools_config = config;\n}\n\nexport function readDevtoolsConfig(): DevtoolsConfig | null {\n if (typeof window === 'undefined') return null;\n return (window as ConfigWindow).__sentient_devtools_config ?? null;\n}\n"],"mappings":"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","names":["useEffect","useReducer","useState","PERSONAS","PERSONA_DISPLAY","confidenceBand","SNAPSHOT_STORAGE_KEY_PREFIX","ssrFallback","state","w","getRegistered","state","getRegisteredSlots","getRegisteredSections","subscribeRegistry","fn","listeners","store","w","setVariantOverride","componentId","variantId","clearVariantOverride","getOverrides","__spreadValues","ssrFallback","state","w","setPreviewMode","on","s","fn","getPreviewMode","EVENT","notifyOverridesChanged","_a","w","EVENT","readDevtoolsConfig","_a","jsx","jsxs","_a","IS_PROD","DEFAULT_API_BASE_URL","LOCAL_MODE_DEVTOOLS_BANNER","applyOutcome","result","_b","id","variantId","setVariantOverride","w","__spreadValues","setPreviewMode","notifyOverridesChanged","readSessionId","match","slotDecls","getRegisteredSlots","s","forcePersonaKeyed","apiKey","apiBaseUrl","persona","res","getRegisteredSections","getRegistered","c","data","__spreadProps","forcePersonaLocal","mod","outcome","confidenceBand","resetAll","getOverrides","clearVariantOverride","stale","i","key","SNAPSHOT_STORAGE_KEY_PREFIX","e","btn","active","AdaptiveDevtools","open","setOpen","useState","activePersona","setActivePersona","force","useReducer","n","useEffect","subscribeRegistry","config","readDevtoolsConfig","useLocalEngine","components","overrides","choose","resetVariant","choosePersona","getPreviewMode","PERSONAS","p","PERSONA_DISPLAY","v"]}
|