@sentientui/react 0.14.3 → 0.15.1

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 CHANGED
@@ -7,16 +7,22 @@ learns which one converts best for each visitor type. Visit 1 learns; Visit 2 co
7
7
  ## Installation
8
8
 
9
9
  ```bash
10
- npm install @sentientui/react # or: npx @sentientui/cli init (scaffolds everything)
10
+ npm install @sentientui/react # or: npx @sentientui/cli init (installs + writes .env.local + an example component)
11
11
  ```
12
12
 
13
- ## 60-second start (no account)
13
+ ## 2-minute start (no account)
14
14
 
15
15
  ```bash
16
- npx @sentientui/cli init && npm run dev
17
- # open http://localhost:3000?sentient_persona=buyer watch the page adapt
16
+ npx @sentientui/cli init
17
+ # then follow its printed instructions: wrap your app with <AdaptiveRoot> (or
18
+ # <AdaptiveProvider>) and mount the generated components/adaptive-example.tsx
19
+ npm run dev
20
+ # open http://localhost:3000?sentient_persona=buyer — the example adapts
18
21
  ```
19
22
 
23
+ The CLI installs the package, writes `.env.local`, and generates an example component — it does
24
+ **not** edit your layout, so nothing adapts until you do the wrap-and-mount step it prints.
25
+
20
26
  With no API key the SDK runs in keyless **local mode**: deterministic simulated decisions, zero
21
27
  network. Add a `pk_…` key from [sentient-ui.com](https://sentient-ui.com) to learn from real
22
28
  traffic:
@@ -54,7 +60,13 @@ export default function RootLayout({ children }: { children: React.ReactNode })
54
60
  `suppressHydrationWarning` on `<html>` is required: an inline script (rendered by
55
61
  `AdaptiveRoot` as its first child) sets the persona attributes before first paint, exactly like
56
62
  the `next-themes` pattern. Other React apps (Vite, CRA, Remix, Pages Router) use
57
- `<AdaptiveProvider>` from `@sentientui/react` with the same props.
63
+ `<AdaptiveProvider>` from `@sentientui/react` with the same props — **plus
64
+ `<SentientPersonaScript />` rendered in your document `<head>`** if you want the Rung-1a persona
65
+ attributes: only that inline script (which `AdaptiveRoot` includes for you) writes
66
+ `data-sentient-persona` / `data-sentient-confidence` in hosted mode. Without it, an
67
+ `AdaptiveProvider` app gets everything except the persona CSS hooks. (Keyless local mode writes
68
+ the attributes itself, so the demo works either way — don't let that mask a missing script when
69
+ you add a real key.)
58
70
 
59
71
  ## The adaptive ladder
60
72
 
@@ -85,7 +97,8 @@ html[data-sentient-confidence='low'] .discount-banner,
85
97
  html[data-sentient-confidence='low'] .newsletter-nudge { display: none; }
86
98
  ```
87
99
 
88
- Force any persona in dev: `?sentient_persona=deal_seeker`.
100
+ In keyless local mode (development without a `pk_` key), force any persona with
101
+ `?sentient_persona=deal_seeker`. The override is not read in hosted (`pk_`) mode.
89
102
 
90
103
  **1b. Adaptive tokens — learned.** Declare a bounded design space; the optimizer picks per
91
104
  visitor type; values arrive as element-scoped `data-*` props (SSR-serialized — zero flicker):
@@ -99,7 +112,7 @@ function Hero() {
99
112
  motion: ['none', 'pulse'],
100
113
  });
101
114
  return <section {...t.props} className="hero">…</section>;
102
- // t.props → { 'data-tone': 'urgent', 'data-motion': 'pulse' }
115
+ // t.props → { 'data-sentient-slot': 'hero', 'data-tone': 'urgent', 'data-motion': 'pulse' }
103
116
  // t.tokens → { tone: 'urgent', motion: 'pulse' }
104
117
  }
105
118
  ```
@@ -169,7 +182,7 @@ Imported from `@sentientui/react/next`.
169
182
 
170
183
  | Prop | Type | Description |
171
184
  |------|------|-------------|
172
- | `components` | `Array<{ id: string; variantIds: string[] }>` | Components to preload server-side. `id` must match `<Adaptive id="…">`. |
185
+ | `components` | `Array<{ id: string; variantIds: string[] }>` *(optional, default `[]`)* | Components to preload server-side. `id` must match `<Adaptive id="…">`. Omit when the tree uses only slots/sections or assigns client-side. |
173
186
  | `sections` | `string[]` *(optional)* | Page section IDs in default order. When provided, a single `POST /v1/decide` returns both layout order and assignments; `useLayoutOrder()` becomes available. |
174
187
  | `apiKey` | `string` | `pk_…` key — used by both the browser SDK and server-side SSR requests. |
175
188
  | `appOrigin` | `string` *(default `http://localhost:3001`)* | Your app origin (e.g. `https://yourapp.com`). Must be on the project's allowed-origins list. Always set in production. |
@@ -186,7 +199,8 @@ Accepts the same `apiKey`, `context`, `consent`, `ssrFallback`, `debug` props as
186
199
 
187
200
  | Prop | Type | Description |
188
201
  |------|------|-------------|
189
- | `initialAssignments` | `Record<string, string>` | SSR-preloaded assignments (e.g. from `loadAdaptiveAssignments` in `getServerSideProps`). |
202
+ | `initialAssignments` | `Record<string, string>` | SSR-preloaded assignments the `assignments` field of `loadAdaptiveAssignments`' return value (not the whole object). |
203
+ | `ssrSessionId` | `string` | The `sessionId` field of `loadAdaptiveAssignments`' return value. Required with `initialAssignments` so the browser adopts the same session the server assigned — otherwise exposures and goals attach to a different session than the SSR assignment. |
190
204
  | `sessionSegment` | `string` | Segment from SSR (`device:source`). Must match the value used in `loadAdaptiveAssignments`. |
191
205
  | `initialLayoutOrder` | `string[] \| null` | Preloaded section order from `loadAdaptiveDecision` (for Pages-Router-style SSR with sections). |
192
206
 
@@ -242,7 +256,7 @@ Each goal fires at most once per variant mount. `WeightedCompositeGoal` fires ea
242
256
 
243
257
  ### `<AdaptiveText>`
244
258
 
245
- Lightweight text-only variant (no wrapper element, no automatic goal wiring). Useful when you publish text variants from the dashboard WYSIWYG.
259
+ Lightweight text-only variant (renders an inline `<span>` wrapper by default — change it via the `component` prop; no automatic goal wiring). Useful when you publish text variants from the dashboard WYSIWYG.
246
260
 
247
261
  ```tsx
248
262
  import { AdaptiveText } from '@sentientui/react';
@@ -259,7 +273,9 @@ function useAdaptiveTokens(
259
273
  id: string,
260
274
  dims: Record<string, readonly string[]>, // 1–4 dims × 2–6 values; first value = baseline
261
275
  opts?: { goal?: string | GoalConfig },
262
- ): { tokens: Record<string, string>; props: Record<string, string> }; // props keys: `data-${dim}`
276
+ ): { tokens: Record<string, string>; props: Record<string, string> };
277
+ // props keys: `data-${dim}` per dim, plus `data-sentient-slot`: id — goal
278
+ // wiring locates the slot's element through it, so always spread all of `props`.
263
279
  ```
264
280
 
265
281
  Spread `props` onto the element you style. Values serialize through SSR markup — no flicker, no
@@ -299,9 +315,10 @@ Reorders via React keys (DOM moves, state preserved). Declared orders only.
299
315
  `AdaptiveRoot` renders an inline script (its first child) that sets `data-sentient-persona` and
300
316
  `data-sentient-confidence` on `<html>` before first paint; the client SDK adopts those values
301
317
  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>`.
318
+ `AdaptiveRoot` at the top of the tree. In keyless local mode (development without a `pk_` key),
319
+ override with `?sentient_persona=<persona>` — the parameter is ignored in hosted (`pk_`) mode.
303
320
 
304
- ### `useAssignment(componentId, variantIds?)`
321
+ ### `useAssignment(componentId, variantIds)`
305
322
 
306
323
  > **Deprecated** — use [`useAdaptive`](#useadaptiveid-config) instead; it carries the goal and exposure wiring `useAssignment` leaves to you. Kept for backward compatibility.
307
324
 
@@ -363,7 +380,7 @@ function Page() {
363
380
  import { loadAdaptiveAssignments } from '@sentientui/react/server';
364
381
 
365
382
  export async function getServerSideProps({ req }) {
366
- const assignments = await loadAdaptiveAssignments(
383
+ const { assignments, sessionId } = await loadAdaptiveAssignments(
367
384
  [{ id: 'hero_cta', variantIds: ['control', 'variant_a'] }],
368
385
  {
369
386
  cookies: req.cookies,
@@ -372,11 +389,11 @@ export async function getServerSideProps({ req }) {
372
389
  origin: process.env.NEXT_PUBLIC_APP_URL,
373
390
  },
374
391
  );
375
- return { props: { initialAssignments: assignments } };
392
+ return { props: { initialAssignments: assignments, ssrSessionId: sessionId } };
376
393
  }
377
394
  ```
378
395
 
379
- Pass the result as `initialAssignments` on `<AdaptiveProvider>` in `_app.tsx`. For pages with a `sections` layout, use `loadAdaptiveDecision` (same options shape, plus `sections: string[]`) — its return value carries both `assignments` and `layoutOrder`.
396
+ Pass `assignments` as `initialAssignments` **and `sessionId` as `ssrSessionId`** on `<AdaptiveProvider>` in `_app.tsx` — the return value is `{ assignments, sessionId }`, and without `ssrSessionId` the browser starts a different session than the one the server assigned, so the preload silently does nothing and events attach to the wrong session. For pages with a `sections` layout, use `loadAdaptiveDecision` (same options shape, plus `sections: string[]`) — its return value carries both `assignments` and `layoutOrder`.
380
397
 
381
398
  ### Forwarding to your own analytics
382
399
 
@@ -423,7 +440,7 @@ Direct `@sentientui/core` users enable the same thing by importing `init` from `
423
440
 
424
441
  ## Consent management (GDPR)
425
442
 
426
- SentientUI does not track visitors until consent is granted. To serve the best-performing variant while the cookie banner is pending (rather than freezing the UI), use `preConsentBehavior: 'statistical_winner'`:
443
+ By default the SDK initialises with `consent: true`, so tracking starts on first paint — opt-in consent is something you wire up, not the default. For GDPR-style opt-in, pass `consent={false}` until the visitor accepts your banner; no events are sent and no cookie is written while consent is false. To serve the best-performing variant while the cookie banner is pending (rather than freezing the UI), add `preConsentBehavior: 'statistical_winner'`:
427
444
 
428
445
  ```tsx
429
446
  <AdaptiveProvider
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/devtools/index.tsx","../src/devtools-registry.ts","../src/devtools-overrides.ts","../src/devtools-slot-overrides.ts","../src/preview-mode.ts","../src/override-events.ts","../src/devtools-config.ts"],"sourcesContent":["'use client';\nimport { useEffect, useReducer, useState, useSyncExternalStore, 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 getRegistryVersion,\n type RegisteredSlot,\n} from '../devtools-registry.js';\nimport { setVariantOverride, clearVariantOverride, getOverrides } from '../devtools-overrides.js';\nimport { setSlotOverride, clearSlotOverride, getSlotOverrides } from '../devtools-slot-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\n/** Sentient \"S.\" wordmark, monoline — white strokes for the black launcher button. */\nfunction SentientMark({ size = 26 }: { size?: number } = {}): JSX.Element {\n return (\n <svg width={size} height={size} viewBox=\"0 0 32 32\" fill=\"none\" aria-hidden=\"true\">\n <path\n d=\"M21 9H14C11.2 9 9.8 10.8 9.8 13C9.8 15.4 11.6 16.6 14.5 16.6H18.5C20.5 16.6 20.7 18.2 20.4 19.6\"\n stroke=\"#fff\"\n strokeWidth=\"2.2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n <path d=\"M10 22H17\" stroke=\"#fff\" strokeWidth=\"2.2\" strokeLinecap=\"round\" />\n <circle cx=\"20.6\" cy=\"22\" r=\"2.1\" fill=\"#fff\" />\n </svg>\n );\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 [mounted, setMounted] = useState(false);\n const [, force] = useReducer((n: number) => n + 1, 0);\n // Re-render when the component/slot registry changes. useSyncExternalStore\n // (not a manual useEffect subscription) so the subscription can't go stale\n // across Next Fast Refresh — a stale listener was why the panel needed a hard\n // refresh to pick up a renamed id.\n useSyncExternalStore(subscribeRegistry, getRegistryVersion, () => 0);\n // Client-mount gate: render nothing during SSR / the first paint so the widget\n // never reads `window` on the server. Lets consumers drop `<AdaptiveDevtools/>`\n // straight into a tree without a `dynamic(..., { ssr: false })` wrapper.\n useEffect(() => setMounted(true), []);\n\n // Never render in production, even if imported by mistake. (Hooks run first\n // so the rules of hooks hold regardless of these early returns.)\n if (IS_PROD) return null;\n if (!mounted) 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 slots = getRegisteredSlots();\n const overrides = getOverrides();\n const slotOverrides = getSlotOverrides();\n\n // Preview mode stays on while ANY override (variant or slot) is active.\n function maybeExitPreview(): void {\n if (Object.keys(getOverrides()).length === 0 && Object.keys(getSlotOverrides()).length === 0) {\n setPreviewMode(false);\n }\n }\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 maybeExitPreview();\n notifyOverridesChanged();\n force();\n }\n function chooseSlotArm(id: string, arm: string): void {\n setSlotOverride(id, arm);\n setPreviewMode(true);\n notifyOverridesChanged();\n force();\n }\n function chooseSlotDim(id: string, dim: string, value: string, dims: RegisteredSlot['dims']): void {\n const current = getSlotOverrides()[id];\n // Merge onto the existing forced object, or seed from each dim's baseline\n // (first value) so unset dims stay at baseline instead of vanishing.\n const next: Record<string, string> =\n current && typeof current === 'object'\n ? { ...current }\n : Object.fromEntries(Object.entries(dims ?? {}).map(([d, values]) => [d, values[0]!]));\n next[dim] = value;\n setSlotOverride(id, next);\n setPreviewMode(true);\n notifyOverridesChanged();\n force();\n }\n function resetSlot(id: string): void {\n clearSlotOverride(id);\n maybeExitPreview();\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 {/* Panel stays mounted and animates in/out from the button's corner, so the\n launcher below never shifts. transform-origin is the button (bottom-right). */}\n <div\n aria-hidden={!open}\n style={{\n position: 'absolute',\n bottom: 52,\n right: 0,\n transformOrigin: 'bottom right',\n transition: 'opacity .18s ease, transform .2s cubic-bezier(.16,1,.3,1)',\n opacity: open ? 1 : 0,\n transform: open ? 'translateY(0) scale(1)' : 'translateY(6px) scale(.96)',\n pointerEvents: open ? 'auto' : 'none',\n }}\n >\n <div style={{ width: 300, maxHeight: 460, overflowY: 'auto', background: '#111', color: '#eee',\n borderRadius: 8, padding: 12, 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 || Object.keys(slotOverrides).length > 0) && (\n <button onClick={resetAll} style={{ ...btn(false), color: '#aaa' }}>\n Reset\n </button>\n )}\n </div>\n </div>\n {components.length === 0 && slots.length === 0 && (\n <div style={{ opacity: .6 }}>No components or slots on this page yet.</div>\n )}\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 {slots.length > 0 && (\n <div style={{ borderTop: '1px solid #333', paddingTop: 8, marginTop: 4 }}>\n <div style={{ opacity: .7, marginBottom: 4 }}>Slots</div>\n {slots.map((s) => {\n const ov = slotOverrides[s.id];\n return (\n <div key={s.id} style={{ padding: '6px 0' }}>\n <div style={{ fontWeight: 600 }}>{s.id}</div>\n {s.arms && (\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4, marginTop: 4 }}>\n {s.arms.map((arm) => (\n <button key={arm} onClick={() => chooseSlotArm(s.id, arm)} style={btn(ov === arm)}>\n {arm}\n </button>\n ))}\n </div>\n )}\n {s.dims && Object.entries(s.dims).map(([dim, values]) => (\n <div key={dim} style={{ marginTop: 4 }}>\n <div style={{ opacity: .6, fontSize: 11 }}>{dim}</div>\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4 }}>\n {values.map((v) => (\n <button\n key={v}\n onClick={() => chooseSlotDim(s.id, dim, v, s.dims)}\n style={btn(!!ov && typeof ov === 'object' && ov[dim] === v)}\n >\n {v}\n </button>\n ))}\n </div>\n </div>\n ))}\n {ov !== undefined && (\n <button onClick={() => resetSlot(s.id)} style={{ ...btn(false), color: '#aaa', marginTop: 4 }}>\n reset\n </button>\n )}\n </div>\n );\n })}\n </div>\n )}\n </div>\n </div>\n <button\n aria-label=\"Sentient DevTools\"\n aria-expanded={open}\n onClick={() => setOpen((o) => !o)}\n style={{ width: 44, height: 44, borderRadius: 12, border: '1px solid #2a2a2a', background: '#000',\n display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 0,\n cursor: 'pointer', boxShadow: '0 6px 18px rgba(0,0,0,.4)' }}\n >\n <SentientMark />\n </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 /** Bumped on every mutation so `useSyncExternalStore` can read a stable, comparable snapshot. */\n version: number;\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 version: 0,\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 version: 0,\n };\n }\n return w.__sentient_registry;\n}\n\nfunction emit(): void {\n // Bump BEFORE notifying so a useSyncExternalStore consumer re-reading its\n // snapshot inside the notification sees the new value and re-renders.\n state().version += 1;\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\n/** Monotonic snapshot for `useSyncExternalStore` — changes on every registry mutation. */\nexport function getRegistryVersion(): number {\n return state().version;\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 { SlotResult } from '@sentientui/core';\n\n/**\n * Slot-override store — the channel `useSlotResult` reads (`useAdaptiveTokens` /\n * `AdaptiveGroup`). Separate from `__sentient_overrides` (variant components):\n * a slot result is a token object (`{ tone: 'urgent' }`) or an arm string\n * (`'social_first'`), never a bare variant id. Window-backed so the /devtools\n * bundle and the main bundle share one store.\n */\nfunction store(): Record<string, SlotResult> {\n const w = window as unknown as { __sentient_slot_overrides?: Record<string, SlotResult> };\n if (!w.__sentient_slot_overrides) w.__sentient_slot_overrides = {};\n return w.__sentient_slot_overrides;\n}\n\n/** Force `slotId` to resolve to `result` — read by `useSlotResult`. */\nexport function setSlotOverride(slotId: string, result: SlotResult): void {\n store()[slotId] = result;\n}\n\nexport function clearSlotOverride(slotId: string): void {\n delete store()[slotId];\n}\n\nexport function getSlotOverrides(): Record<string, SlotResult> {\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","getRegistryVersion","store","w","setVariantOverride","componentId","variantId","clearVariantOverride","getOverrides","__spreadValues","store","w","setSlotOverride","slotId","result","clearSlotOverride","getSlotOverrides","__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","SentientMark","size","AdaptiveDevtools","open","setOpen","activePersona","setActivePersona","mounted","setMounted","force","subscribeRegistry","getRegistryVersion","config","readDevtoolsConfig","useLocalEngine","components","slots","overrides","slotOverrides","getSlotOverrides","maybeExitPreview","choose","resetVariant","chooseSlotArm","arm","setSlotOverride","chooseSlotDim","dim","value","dims","current","next","d","values","resetSlot","clearSlotOverride","choosePersona","getPreviewMode","p","v","ov","o"]}
1
+ {"version":3,"sources":["../src/devtools/index.tsx","../src/devtools-registry.ts","../src/devtools-overrides.ts","../src/devtools-slot-overrides.ts","../src/preview-mode.ts","../src/override-events.ts","../src/devtools-config.ts"],"sourcesContent":["'use client';\nimport { useEffect, useReducer, useState, useSyncExternalStore, 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 getRegistryVersion,\n type RegisteredSlot,\n} from '../devtools-registry.js';\nimport { setVariantOverride, clearVariantOverride, getOverrides } from '../devtools-overrides.js';\nimport { setSlotOverride, clearSlotOverride, getSlotOverrides } from '../devtools-slot-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\n/** Sentient \"S.\" wordmark, monoline — white strokes for the black launcher button. */\nfunction SentientMark({ size = 26 }: { size?: number } = {}): JSX.Element {\n return (\n <svg width={size} height={size} viewBox=\"0 0 32 32\" fill=\"none\" aria-hidden=\"true\">\n <path\n d=\"M21 9H14C11.2 9 9.8 10.8 9.8 13C9.8 15.4 11.6 16.6 14.5 16.6H18.5C20.5 16.6 20.7 18.2 20.4 19.6\"\n stroke=\"#fff\"\n strokeWidth=\"2.2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n <path d=\"M10 22H17\" stroke=\"#fff\" strokeWidth=\"2.2\" strokeLinecap=\"round\" />\n <circle cx=\"20.6\" cy=\"22\" r=\"2.1\" fill=\"#fff\" />\n </svg>\n );\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 [mounted, setMounted] = useState(false);\n const [, force] = useReducer((n: number) => n + 1, 0);\n // Re-render when the component/slot registry changes. useSyncExternalStore\n // (not a manual useEffect subscription) so the subscription can't go stale\n // across Next Fast Refresh — a stale listener was why the panel needed a hard\n // refresh to pick up a renamed id.\n useSyncExternalStore(subscribeRegistry, getRegistryVersion, () => 0);\n // Client-mount gate: render nothing during SSR / the first paint so the widget\n // never reads `window` on the server. Lets consumers drop `<AdaptiveDevtools/>`\n // straight into a tree without a `dynamic(..., { ssr: false })` wrapper.\n useEffect(() => setMounted(true), []);\n\n // Never render in production, even if imported by mistake. (Hooks run first\n // so the rules of hooks hold regardless of these early returns.)\n if (IS_PROD) return null;\n if (!mounted) 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 slots = getRegisteredSlots();\n const overrides = getOverrides();\n const slotOverrides = getSlotOverrides();\n\n // Preview mode stays on while ANY override (variant or slot) is active.\n function maybeExitPreview(): void {\n if (Object.keys(getOverrides()).length === 0 && Object.keys(getSlotOverrides()).length === 0) {\n setPreviewMode(false);\n }\n }\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 maybeExitPreview();\n notifyOverridesChanged();\n force();\n }\n function chooseSlotArm(id: string, arm: string): void {\n setSlotOverride(id, arm);\n setPreviewMode(true);\n notifyOverridesChanged();\n force();\n }\n function chooseSlotDim(id: string, dim: string, value: string, dims: RegisteredSlot['dims']): void {\n const current = getSlotOverrides()[id];\n // Merge onto the existing forced object, or seed from each dim's baseline\n // (first value) so unset dims stay at baseline instead of vanishing.\n const next: Record<string, string> =\n current && typeof current === 'object'\n ? { ...current }\n : Object.fromEntries(Object.entries(dims ?? {}).map(([d, values]) => [d, values[0]!]));\n next[dim] = value;\n setSlotOverride(id, next);\n setPreviewMode(true);\n notifyOverridesChanged();\n force();\n }\n function resetSlot(id: string): void {\n clearSlotOverride(id);\n maybeExitPreview();\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 {/* Panel stays mounted and animates in/out from the button's corner, so the\n launcher below never shifts. transform-origin is the button (bottom-right). */}\n <div\n aria-hidden={!open}\n style={{\n position: 'absolute',\n bottom: 52,\n right: 0,\n transformOrigin: 'bottom right',\n transition: 'opacity .18s ease, transform .2s cubic-bezier(.16,1,.3,1)',\n opacity: open ? 1 : 0,\n transform: open ? 'translateY(0) scale(1)' : 'translateY(6px) scale(.96)',\n pointerEvents: open ? 'auto' : 'none',\n }}\n >\n <div style={{ width: 300, maxHeight: 460, overflowY: 'auto', background: '#111', color: '#eee',\n borderRadius: 8, padding: 12, 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 || Object.keys(slotOverrides).length > 0) && (\n <button onClick={resetAll} style={{ ...btn(false), color: '#aaa' }}>\n Reset\n </button>\n )}\n </div>\n </div>\n {components.length === 0 && slots.length === 0 && (\n <div style={{ opacity: .6 }}>No components or slots on this page yet.</div>\n )}\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 {slots.length > 0 && (\n <div style={{ borderTop: '1px solid #333', paddingTop: 8, marginTop: 4 }}>\n <div style={{ opacity: .7, marginBottom: 4 }}>Slots</div>\n {slots.map((s) => {\n const ov = slotOverrides[s.id];\n return (\n <div key={s.id} style={{ padding: '6px 0' }}>\n <div style={{ fontWeight: 600 }}>{s.id}</div>\n {s.arms && (\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4, marginTop: 4 }}>\n {s.arms.map((arm) => (\n <button key={arm} onClick={() => chooseSlotArm(s.id, arm)} style={btn(ov === arm)}>\n {arm}\n </button>\n ))}\n </div>\n )}\n {s.dims && Object.entries(s.dims).map(([dim, values]) => (\n <div key={dim} style={{ marginTop: 4 }}>\n <div style={{ opacity: .6, fontSize: 11 }}>{dim}</div>\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4 }}>\n {values.map((v) => (\n <button\n key={v}\n onClick={() => chooseSlotDim(s.id, dim, v, s.dims)}\n style={btn(!!ov && typeof ov === 'object' && ov[dim] === v)}\n >\n {v}\n </button>\n ))}\n </div>\n </div>\n ))}\n {ov !== undefined && (\n <button onClick={() => resetSlot(s.id)} style={{ ...btn(false), color: '#aaa', marginTop: 4 }}>\n reset\n </button>\n )}\n </div>\n );\n })}\n </div>\n )}\n </div>\n </div>\n <button\n aria-label=\"Sentient DevTools\"\n aria-expanded={open}\n onClick={() => setOpen((o) => !o)}\n style={{ width: 44, height: 44, borderRadius: 12, border: '1px solid #2a2a2a', background: '#000',\n display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 0,\n cursor: 'pointer', boxShadow: '0 6px 18px rgba(0,0,0,.4)' }}\n >\n <SentientMark />\n </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 /** Bumped on every mutation so `useSyncExternalStore` can read a stable, comparable snapshot. */\n version: number;\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 version: 0,\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 version: 0,\n };\n }\n return w.__sentient_registry;\n}\n\nfunction emit(): void {\n // Bump BEFORE notifying so a useSyncExternalStore consumer re-reading its\n // snapshot inside the notification sees the new value and re-renders.\n state().version += 1;\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\n/** Monotonic snapshot for `useSyncExternalStore` — changes on every registry mutation. */\nexport function getRegistryVersion(): number {\n return state().version;\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 { SlotResult } from '@sentientui/core';\n\n/**\n * Slot-override store — the channel `useSlotResult` reads (`useAdaptiveTokens` /\n * `AdaptiveGroup`). Separate from `__sentient_overrides` (variant components):\n * a slot result is a token object (`{ tone: 'urgent' }`) or an arm string\n * (`'social_first'`), never a bare variant id. Window-backed so the /devtools\n * bundle and the main bundle share one store.\n */\nfunction store(): Record<string, SlotResult> {\n const w = window as unknown as { __sentient_slot_overrides?: Record<string, SlotResult> };\n if (!w.__sentient_slot_overrides) w.__sentient_slot_overrides = {};\n return w.__sentient_slot_overrides;\n}\n\n/** Force `slotId` to resolve to `result` — read by `useSlotResult`. */\nexport function setSlotOverride(slotId: string, result: SlotResult): void {\n store()[slotId] = result;\n}\n\nexport function clearSlotOverride(slotId: string): void {\n delete store()[slotId];\n}\n\nexport function getSlotOverrides(): Record<string, SlotResult> {\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 dispose: () => inner.dispose(),\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","getRegistryVersion","store","w","setVariantOverride","componentId","variantId","clearVariantOverride","getOverrides","__spreadValues","store","w","setSlotOverride","slotId","result","clearSlotOverride","getSlotOverrides","__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","SentientMark","size","AdaptiveDevtools","open","setOpen","activePersona","setActivePersona","mounted","setMounted","force","subscribeRegistry","getRegistryVersion","config","readDevtoolsConfig","useLocalEngine","components","slots","overrides","slotOverrides","getSlotOverrides","maybeExitPreview","choose","resetVariant","chooseSlotArm","arm","setSlotOverride","chooseSlotDim","dim","value","dims","current","next","d","values","resetSlot","clearSlotOverride","choosePersona","getPreviewMode","p","v","ov","o"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/devtools/index.tsx","../src/devtools-registry.ts","../src/devtools-overrides.ts","../src/devtools-slot-overrides.ts","../src/preview-mode.ts","../src/override-events.ts","../src/devtools-config.ts"],"sourcesContent":["'use client';\nimport { useEffect, useReducer, useState, useSyncExternalStore, 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 getRegistryVersion,\n type RegisteredSlot,\n} from '../devtools-registry.js';\nimport { setVariantOverride, clearVariantOverride, getOverrides } from '../devtools-overrides.js';\nimport { setSlotOverride, clearSlotOverride, getSlotOverrides } from '../devtools-slot-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\n/** Sentient \"S.\" wordmark, monoline — white strokes for the black launcher button. */\nfunction SentientMark({ size = 26 }: { size?: number } = {}): JSX.Element {\n return (\n <svg width={size} height={size} viewBox=\"0 0 32 32\" fill=\"none\" aria-hidden=\"true\">\n <path\n d=\"M21 9H14C11.2 9 9.8 10.8 9.8 13C9.8 15.4 11.6 16.6 14.5 16.6H18.5C20.5 16.6 20.7 18.2 20.4 19.6\"\n stroke=\"#fff\"\n strokeWidth=\"2.2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n <path d=\"M10 22H17\" stroke=\"#fff\" strokeWidth=\"2.2\" strokeLinecap=\"round\" />\n <circle cx=\"20.6\" cy=\"22\" r=\"2.1\" fill=\"#fff\" />\n </svg>\n );\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 [mounted, setMounted] = useState(false);\n const [, force] = useReducer((n: number) => n + 1, 0);\n // Re-render when the component/slot registry changes. useSyncExternalStore\n // (not a manual useEffect subscription) so the subscription can't go stale\n // across Next Fast Refresh — a stale listener was why the panel needed a hard\n // refresh to pick up a renamed id.\n useSyncExternalStore(subscribeRegistry, getRegistryVersion, () => 0);\n // Client-mount gate: render nothing during SSR / the first paint so the widget\n // never reads `window` on the server. Lets consumers drop `<AdaptiveDevtools/>`\n // straight into a tree without a `dynamic(..., { ssr: false })` wrapper.\n useEffect(() => setMounted(true), []);\n\n // Never render in production, even if imported by mistake. (Hooks run first\n // so the rules of hooks hold regardless of these early returns.)\n if (IS_PROD) return null;\n if (!mounted) 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 slots = getRegisteredSlots();\n const overrides = getOverrides();\n const slotOverrides = getSlotOverrides();\n\n // Preview mode stays on while ANY override (variant or slot) is active.\n function maybeExitPreview(): void {\n if (Object.keys(getOverrides()).length === 0 && Object.keys(getSlotOverrides()).length === 0) {\n setPreviewMode(false);\n }\n }\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 maybeExitPreview();\n notifyOverridesChanged();\n force();\n }\n function chooseSlotArm(id: string, arm: string): void {\n setSlotOverride(id, arm);\n setPreviewMode(true);\n notifyOverridesChanged();\n force();\n }\n function chooseSlotDim(id: string, dim: string, value: string, dims: RegisteredSlot['dims']): void {\n const current = getSlotOverrides()[id];\n // Merge onto the existing forced object, or seed from each dim's baseline\n // (first value) so unset dims stay at baseline instead of vanishing.\n const next: Record<string, string> =\n current && typeof current === 'object'\n ? { ...current }\n : Object.fromEntries(Object.entries(dims ?? {}).map(([d, values]) => [d, values[0]!]));\n next[dim] = value;\n setSlotOverride(id, next);\n setPreviewMode(true);\n notifyOverridesChanged();\n force();\n }\n function resetSlot(id: string): void {\n clearSlotOverride(id);\n maybeExitPreview();\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 {/* Panel stays mounted and animates in/out from the button's corner, so the\n launcher below never shifts. transform-origin is the button (bottom-right). */}\n <div\n aria-hidden={!open}\n style={{\n position: 'absolute',\n bottom: 52,\n right: 0,\n transformOrigin: 'bottom right',\n transition: 'opacity .18s ease, transform .2s cubic-bezier(.16,1,.3,1)',\n opacity: open ? 1 : 0,\n transform: open ? 'translateY(0) scale(1)' : 'translateY(6px) scale(.96)',\n pointerEvents: open ? 'auto' : 'none',\n }}\n >\n <div style={{ width: 300, maxHeight: 460, overflowY: 'auto', background: '#111', color: '#eee',\n borderRadius: 8, padding: 12, 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 || Object.keys(slotOverrides).length > 0) && (\n <button onClick={resetAll} style={{ ...btn(false), color: '#aaa' }}>\n Reset\n </button>\n )}\n </div>\n </div>\n {components.length === 0 && slots.length === 0 && (\n <div style={{ opacity: .6 }}>No components or slots on this page yet.</div>\n )}\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 {slots.length > 0 && (\n <div style={{ borderTop: '1px solid #333', paddingTop: 8, marginTop: 4 }}>\n <div style={{ opacity: .7, marginBottom: 4 }}>Slots</div>\n {slots.map((s) => {\n const ov = slotOverrides[s.id];\n return (\n <div key={s.id} style={{ padding: '6px 0' }}>\n <div style={{ fontWeight: 600 }}>{s.id}</div>\n {s.arms && (\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4, marginTop: 4 }}>\n {s.arms.map((arm) => (\n <button key={arm} onClick={() => chooseSlotArm(s.id, arm)} style={btn(ov === arm)}>\n {arm}\n </button>\n ))}\n </div>\n )}\n {s.dims && Object.entries(s.dims).map(([dim, values]) => (\n <div key={dim} style={{ marginTop: 4 }}>\n <div style={{ opacity: .6, fontSize: 11 }}>{dim}</div>\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4 }}>\n {values.map((v) => (\n <button\n key={v}\n onClick={() => chooseSlotDim(s.id, dim, v, s.dims)}\n style={btn(!!ov && typeof ov === 'object' && ov[dim] === v)}\n >\n {v}\n </button>\n ))}\n </div>\n </div>\n ))}\n {ov !== undefined && (\n <button onClick={() => resetSlot(s.id)} style={{ ...btn(false), color: '#aaa', marginTop: 4 }}>\n reset\n </button>\n )}\n </div>\n );\n })}\n </div>\n )}\n </div>\n </div>\n <button\n aria-label=\"Sentient DevTools\"\n aria-expanded={open}\n onClick={() => setOpen((o) => !o)}\n style={{ width: 44, height: 44, borderRadius: 12, border: '1px solid #2a2a2a', background: '#000',\n display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 0,\n cursor: 'pointer', boxShadow: '0 6px 18px rgba(0,0,0,.4)' }}\n >\n <SentientMark />\n </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 /** Bumped on every mutation so `useSyncExternalStore` can read a stable, comparable snapshot. */\n version: number;\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 version: 0,\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 version: 0,\n };\n }\n return w.__sentient_registry;\n}\n\nfunction emit(): void {\n // Bump BEFORE notifying so a useSyncExternalStore consumer re-reading its\n // snapshot inside the notification sees the new value and re-renders.\n state().version += 1;\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\n/** Monotonic snapshot for `useSyncExternalStore` — changes on every registry mutation. */\nexport function getRegistryVersion(): number {\n return state().version;\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 { SlotResult } from '@sentientui/core';\n\n/**\n * Slot-override store — the channel `useSlotResult` reads (`useAdaptiveTokens` /\n * `AdaptiveGroup`). Separate from `__sentient_overrides` (variant components):\n * a slot result is a token object (`{ tone: 'urgent' }`) or an arm string\n * (`'social_first'`), never a bare variant id. Window-backed so the /devtools\n * bundle and the main bundle share one store.\n */\nfunction store(): Record<string, SlotResult> {\n const w = window as unknown as { __sentient_slot_overrides?: Record<string, SlotResult> };\n if (!w.__sentient_slot_overrides) w.__sentient_slot_overrides = {};\n return w.__sentient_slot_overrides;\n}\n\n/** Force `slotId` to resolve to `result` — read by `useSlotResult`. */\nexport function setSlotOverride(slotId: string, result: SlotResult): void {\n store()[slotId] = result;\n}\n\nexport function clearSlotOverride(slotId: string): void {\n delete store()[slotId];\n}\n\nexport function getSlotOverrides(): Record<string, SlotResult> {\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":";ocACA,OAAS,aAAAA,GAAW,cAAAC,GAAY,YAAAC,EAAU,wBAAAC,OAAgD,QAC1F,OAAS,YAAAC,GAAU,mBAAAC,GAAiB,kBAAAC,OAAsB,qBAC1D,OAAS,+BAAAC,OAAmC,mBCgB5C,IAAMC,GAA6B,CACjC,WAAY,IAAI,IAChB,MAAO,IAAI,IACX,SAAU,CAAC,EACX,UAAW,IAAI,IACf,QAAS,CACX,EAEA,SAASC,GAAuB,CAC9B,GAAI,OAAO,QAAW,YAAa,OAAOD,GAC1C,IAAME,EAAI,OACV,OAAKA,EAAE,sBACLA,EAAE,oBAAsB,CACtB,WAAY,IAAI,IAChB,MAAO,IAAI,IACX,SAAU,CAAC,EACX,UAAW,IAAI,IACf,QAAS,CACX,GAEKA,EAAE,mBACX,CAmCO,SAASC,GAAuC,CACrD,MAAO,CAAC,GAAGC,EAAM,EAAE,WAAW,OAAO,CAAC,CACxC,CAEO,SAASC,GAAuC,CACrD,MAAO,CAAC,GAAGD,EAAM,EAAE,MAAM,OAAO,CAAC,CACnC,CAEO,SAASE,GAAkC,CAChD,MAAO,CAAC,GAAGF,EAAM,EAAE,QAAQ,CAC7B,CAEO,SAASG,EAAkBC,EAA4B,CAC5D,IAAMC,EAAYL,EAAM,EAAE,UAC1B,OAAAK,EAAU,IAAID,CAAE,EACT,IAAM,CACXC,EAAU,OAAOD,CAAE,CACrB,CACF,CAGO,SAASE,GAA6B,CAC3C,OAAON,EAAM,EAAE,OACjB,CClGA,SAASO,GAAgC,CACvC,IAAMC,EAAI,OACV,OAAKA,EAAE,uBAAsBA,EAAE,qBAAuB,CAAC,GAChDA,EAAE,oBACX,CAGO,SAASC,EAAmBC,EAAqBC,EAAyB,CAC/EJ,EAAM,EAAEG,CAAW,EAAIC,CACzB,CAEO,SAASC,EAAqBF,EAA2B,CAC9D,OAAOH,EAAM,EAAEG,CAAW,CAC5B,CAEO,SAASG,GAAuC,CACrD,OAAOC,EAAA,GAAKP,EAAM,EACpB,CCRA,SAASQ,GAAoC,CAC3C,IAAMC,EAAI,OACV,OAAKA,EAAE,4BAA2BA,EAAE,0BAA4B,CAAC,GAC1DA,EAAE,yBACX,CAGO,SAASC,EAAgBC,EAAgBC,EAA0B,CACxEJ,EAAM,EAAEG,CAAM,EAAIC,CACpB,CAEO,SAASC,EAAkBF,EAAsB,CACtD,OAAOH,EAAM,EAAEG,CAAM,CACvB,CAEO,SAASG,GAA+C,CAC7D,OAAOC,EAAA,GAAKP,EAAM,EACpB,CCvBA,IAAMQ,GAA4B,CAAE,GAAI,GAAO,UAAW,IAAI,GAAM,EAIpE,SAASC,GAAsB,CAC7B,GAAI,OAAO,QAAW,YAAa,OAAOD,GAC1C,IAAME,EAAI,OACV,OAAKA,EAAE,qBAAoBA,EAAE,mBAAqB,CAAE,GAAI,GAAO,UAAW,IAAI,GAAM,GAC7EA,EAAE,kBACX,CAEO,SAASC,EAAeC,EAAmB,CAChD,IAAMC,EAAIJ,EAAM,EAChB,GAAII,EAAE,KAAOD,EACb,CAAAC,EAAE,GAAKD,EACP,QAAWE,KAAMD,EAAE,UAAWC,EAAG,EACnC,CAEO,SAASC,GAA0B,CACxC,OAAON,EAAM,EAAE,EACjB,CClBA,IAAMO,GAAQ,6BASP,SAASC,GAA+B,CAd/C,IAAAC,EAeE,GAAI,OAAO,QAAW,YAAa,OACnC,IAAMC,EAAI,OACVA,EAAE,+BAAgCD,EAAAC,EAAE,+BAAF,KAAAD,EAAkC,GAAK,EACzE,OAAO,cAAc,IAAI,MAAME,EAAK,CAAC,CACvC,CCJO,SAASC,GAA4C,CAf5D,IAAAC,EAgBE,OAAI,OAAO,QAAW,YAAoB,MAClCA,EAAA,OAAwB,6BAAxB,KAAAA,EAAsD,IAChE,CN0II,OACE,OAAAC,EADF,QAAAC,MAAA,oBA5JJ,IAAAC,EAmBMC,GAAU,OAAO,SAAY,eAAeD,EAAA,QAAQ,MAAR,YAAAA,EAAa,YAAa,aACtEE,GAAuB,iCAEhBC,GACX,kFAeF,SAASC,EAAaC,EAA8B,CAtCpD,IAAAL,EAAAM,EAuCE,OAAW,CAACC,EAAIC,CAAS,IAAK,OAAO,SAAQR,EAAAK,EAAO,cAAP,KAAAL,EAAsB,CAAC,CAAC,EACnES,EAAmBF,EAAIC,CAAS,EAElC,IAAME,EAAI,OACNL,EAAO,aAAeA,EAAO,YAAY,OAAS,IACpDK,EAAE,2BAA6BL,EAAO,aAEpCA,EAAO,QACTK,EAAE,0BAA4BC,IAAA,IAAML,EAAAI,EAAE,4BAAF,KAAAJ,EAA+B,CAAC,GAAOD,EAAO,QAEhFA,EAAO,oBACT,SAAS,gBAAgB,QAAQ,gBAAkBA,EAAO,kBAAkB,QAC5E,SAAS,gBAAgB,QAAQ,mBAAqBA,EAAO,kBAAkB,YAEjFO,EAAe,EAAI,EACnBC,EAAuB,CACzB,CAEA,SAASC,IAAwB,CAC/B,GAAI,CACF,IAAMC,EAAQ,SAAS,OAAO,MAAM,0BAA0B,EAC9D,GAAIA,EAAO,OAAO,mBAAmBA,EAAM,CAAC,CAAC,CAC/C,OAAQ,GAER,CACA,MAAO,kBACT,CAEA,SAASC,GAAmF,CAC1F,OAAOC,EAAmB,EAAE,IAAKC,GAAOP,IAAA,CACtC,GAAIO,EAAE,IACFA,EAAE,KAAO,CAAE,KAAMA,EAAE,IAAK,EAAI,CAAC,GAC7BA,EAAE,KAAO,CAAE,KAAMA,EAAE,IAAK,EAAI,CAAC,EACjC,CACJ,CAGA,eAAeC,GAAkBC,EAAgBC,EAAoBC,EAAgC,CA5ErG,IAAAtB,EA6EE,IAAMuB,EAAM,MAAM,MAAM,GAAGF,CAAU,WAAY,CAC/C,OAAQ,OACR,QAAS,CAAE,eAAgB,mBAAoB,cAAe,UAAUD,CAAM,EAAG,EACjF,KAAM,KAAK,UAAU,CACnB,QAAAE,EACA,SAAUE,EAAsB,EAAE,IAAKjB,IAAQ,CAAE,GAAAA,CAAG,EAAE,EACtD,WAAYkB,EAAc,EAAE,IAAKC,IAAO,CAAE,GAAIA,EAAE,GAAI,WAAYA,EAAE,UAAW,EAAE,EAC/E,MAAOV,EAAU,CACnB,CAAC,CACH,CAAC,EACD,GAAI,CAACO,EAAI,GAAI,OACb,IAAMI,EAAQ,MAAMJ,EAAI,KAAK,EAC7BnB,EAAawB,EAAAjB,EAAA,GACRgB,GADQ,CAEX,mBAAmB3B,EAAA2B,EAAK,oBAAL,KAAA3B,EAA0B,CAAE,QAAAsB,EAAS,WAAY,MAAO,CAC7E,EAAC,CACH,CAGA,eAAeO,GAAkBP,EAAgC,CAC/D,IAAMQ,EAAM,KAAM,QAAO,wBAAwB,EACjD,GAAI,CAACA,EAAI,uBAAwB,OACjC,IAAMC,EAAUD,EACb,kBAAkB,CAAE,UAAWhB,GAAc,EAAG,cAAeQ,CAAQ,CAAC,EACxE,OAAO,CACN,SAAUE,EAAsB,EAChC,WAAYC,EAAc,EAAE,IAAKC,IAAO,CAAE,GAAIA,EAAE,GAAI,WAAYA,EAAE,UAAW,EAAE,EAC/E,MAAOV,EAAU,CACnB,CAAC,EACHZ,EAAa,CACX,YAAa2B,EAAQ,YACrB,YAAaA,EAAQ,YACrB,MAAOA,EAAQ,MACf,kBAAmB,CACjB,QAASA,EAAQ,QACjB,WAAYC,GAAeD,EAAQ,UAAU,CAC/C,CACF,CAAC,CACH,CAGA,SAASE,IAAiB,CACxB,QAAW1B,KAAM,OAAO,KAAK2B,EAAa,CAAC,EAAGC,EAAqB5B,CAAE,EACrE,IAAMG,EAAI,OACV,OAAOA,EAAE,2BACT,OAAOA,EAAE,0BACT,OAAO,SAAS,gBAAgB,QAAQ,gBACxC,OAAO,SAAS,gBAAgB,QAAQ,mBACxC,GAAI,CACF,IAAM0B,EAAkB,CAAC,EACzB,QAASC,EAAI,EAAGA,EAAI,aAAa,OAAQA,IAAK,CAC5C,IAAMC,EAAM,aAAa,IAAID,CAAC,EAC1BC,GAAOA,EAAI,WAAWC,EAA2B,GAAGH,EAAM,KAAKE,CAAG,CACxE,CACA,QAAWA,KAAOF,EAAO,aAAa,WAAWE,CAAG,CACtD,OAAQE,EAAA,CAER,CACA5B,EAAe,EAAK,EACpBC,EAAuB,EACvB,GAAI,CACF,OAAO,SAAS,OAAO,CACzB,OAAQ2B,EAAA,CAER,CACF,CAEA,IAAMC,EAAOC,IAAoC,CAC/C,QAAS,UACT,aAAc,EACd,OAAQ,iBACR,WAAYA,EAAS,UAAY,OACjC,MAAO,OACP,OAAQ,SACV,GAGA,SAASC,GAAa,CAAE,KAAAC,EAAO,EAAG,EAAuB,CAAC,EAAgB,CACxE,OACE7C,EAAC,OAAI,MAAO6C,EAAM,OAAQA,EAAM,QAAQ,YAAY,KAAK,OAAO,cAAY,OAC1E,UAAA9C,EAAC,QACC,EAAE,kGACF,OAAO,OACP,YAAY,MACZ,cAAc,QACd,eAAe,QACjB,EACAA,EAAC,QAAK,EAAE,YAAY,OAAO,OAAO,YAAY,MAAM,cAAc,QAAQ,EAC1EA,EAAC,UAAO,GAAG,OAAO,GAAG,KAAK,EAAE,MAAM,KAAK,OAAO,GAChD,CAEJ,CAEO,SAAS+C,GAAiB,CAAE,OAAAzB,CAAO,EAAyB,CAAC,EAAuB,CA1K3F,IAAApB,EA2KE,GAAM,CAAC8C,EAAMC,CAAO,EAAIC,EAAS,EAAK,EAChC,CAACC,EAAeC,CAAgB,EAAIF,EAAwB,IAAI,EAChE,CAACG,EAASC,CAAU,EAAIJ,EAAS,EAAK,EACtC,CAAC,CAAEK,CAAK,EAAIC,GAAY,GAAc,EAAI,EAAG,CAAC,EAcpD,GATAC,GAAqBC,EAAmBC,EAAoB,IAAM,CAAC,EAInEC,GAAU,IAAMN,EAAW,EAAI,EAAG,CAAC,CAAC,EAIhCnD,IACA,CAACkD,EAAS,OAAO,KAErB,IAAMQ,GAAyB3D,EAAA4D,EAAmB,IAAnB,KAAA5D,EAAwB,CACrD,OAAQoB,GAAA,KAAAA,EAAU,GAClB,WAAYlB,GACZ,QAAS,EACX,EACM2D,EAAiBF,EAAO,SAAW,CAACA,EAAO,OAC3CG,EAAarC,EAAc,EAC3BsC,EAAQ9C,EAAmB,EAC3B+C,EAAY9B,EAAa,EACzB+B,EAAgBC,EAAiB,EAGvC,SAASC,GAAyB,CAC5B,OAAO,KAAKjC,EAAa,CAAC,EAAE,SAAW,GAAK,OAAO,KAAKgC,EAAiB,CAAC,EAAE,SAAW,GACzFtD,EAAe,EAAK,CAExB,CAEA,SAASwD,EAAO7D,EAAYC,EAAyB,CACnDC,EAAmBF,EAAIC,CAAS,EAChCI,EAAe,EAAI,EACnBC,EAAuB,EACvBwC,EAAM,CACR,CACA,SAASgB,EAAa9D,EAAkB,CACtC4B,EAAqB5B,CAAE,EACvB4D,EAAiB,EACjBtD,EAAuB,EACvBwC,EAAM,CACR,CACA,SAASiB,GAAc/D,EAAYgE,EAAmB,CACpDC,EAAgBjE,EAAIgE,CAAG,EACvB3D,EAAe,EAAI,EACnBC,EAAuB,EACvBwC,EAAM,CACR,CACA,SAASoB,GAAclE,EAAYmE,EAAaC,EAAeC,EAAoC,CACjG,IAAMC,EAAUX,EAAiB,EAAE3D,CAAE,EAG/BuE,EACJD,GAAW,OAAOA,GAAY,SAC1BlE,EAAA,GAAKkE,GACL,OAAO,YAAY,OAAO,QAAQD,GAAA,KAAAA,EAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,CAACG,GAAGC,EAAM,IAAM,CAACD,GAAGC,GAAO,CAAC,CAAE,CAAC,CAAC,EACzFF,EAAKJ,CAAG,EAAIC,EACZH,EAAgBjE,EAAIuE,CAAI,EACxBlE,EAAe,EAAI,EACnBC,EAAuB,EACvBwC,EAAM,CACR,CACA,SAAS4B,GAAU1E,EAAkB,CACnC2E,EAAkB3E,CAAE,EACpB4D,EAAiB,EACjBtD,EAAuB,EACvBwC,EAAM,CACR,CACA,SAAS8B,GAAc7D,EAAuB,CAC5C4B,EAAiB5B,CAAO,GACVuC,EACVhC,GAAkBP,CAAO,EACzBH,GAAkBwC,EAAO,OAAQA,EAAO,WAAYrC,CAAO,GACpD,KAAK+B,CAAK,CACvB,CAEA,OACEtD,EAAC,OAAI,MAAO,CAAE,SAAU,QAAS,OAAQ,GAAI,MAAO,GAAI,OAAQ,WAAY,WAAY,WAAY,EAGlG,UAAAD,EAAC,OACC,cAAa,CAACgD,EACd,MAAO,CACL,SAAU,WACV,OAAQ,GACR,MAAO,EACP,gBAAiB,eACjB,WAAY,4DACZ,QAASA,EAAO,EAAI,EACpB,UAAWA,EAAO,yBAA2B,6BAC7C,cAAeA,EAAO,OAAS,MACjC,EAEA,SAAA/C,EAAC,OAAI,MAAO,CAAE,MAAO,IAAK,UAAW,IAAK,UAAW,OAAQ,WAAY,OAAQ,MAAO,OAC1E,aAAc,EAAG,QAAS,GAAI,UAAW,4BAA6B,SAAU,EAAG,EAC9F,UAAA4D,EAAO,SACN7D,EAAC,OAAI,MAAO,CAAE,WAAY,UAAW,OAAQ,oBAAqB,aAAc,EAClE,QAAS,UAAW,aAAc,CAAE,EAC/C,SAAAK,GACH,EAEFJ,EAAC,OAAI,MAAO,CAAE,QAAS,GAAI,aAAc,CAAE,EACxC,UAAA+D,EAAW,OAAO,aAAWA,EAAW,SAAW,EAAI,GAAK,IAAI,SAAIsB,EAAe,EAAI,iCAA8B,QACxH,EACArF,EAAC,OAAI,MAAO,CAAE,aAAc,iBAAkB,cAAe,EAAG,aAAc,CAAE,EAC9E,UAAAD,EAAC,OAAI,MAAO,CAAE,QAAS,GAAI,aAAc,CAAE,EAAG,2BAAe,EAC7DC,EAAC,OAAI,MAAO,CAAE,QAAS,OAAQ,SAAU,OAAQ,IAAK,CAAE,EACrD,UAAAsF,GAAS,IAAKC,GACbxF,EAAC,UAAe,QAAS,IAAMqF,GAAcG,CAAC,EAAG,MAAO7C,EAAIQ,IAAkBqC,CAAC,EAC5E,SAAAC,GAAgBD,CAAC,GADPA,CAEb,CACD,GACCrC,IAAkB,MAAQ,OAAO,KAAKe,CAAS,EAAE,OAAS,GAAK,OAAO,KAAKC,CAAa,EAAE,OAAS,IACnGnE,EAAC,UAAO,QAASmC,GAAU,MAAOL,EAAAjB,EAAA,GAAK8B,EAAI,EAAK,GAAd,CAAiB,MAAO,MAAO,GAAG,iBAEpE,GAEJ,GACF,EACCqB,EAAW,SAAW,GAAKC,EAAM,SAAW,GAC3CjE,EAAC,OAAI,MAAO,CAAE,QAAS,EAAG,EAAG,oDAAwC,EAEtEgE,EAAW,IAAKpC,GACf3B,EAAC,OAAe,MAAO,CAAE,UAAW,iBAAkB,QAAS,OAAQ,EACrE,UAAAA,EAAC,OAAI,MAAO,CAAE,WAAY,GAAI,EAAI,UAAA2B,EAAE,GAAIA,EAAE,KAAO,eAAYA,EAAE,IAAI,GAAK,IAAG,EAC3E3B,EAAC,OAAI,MAAO,CAAE,QAAS,OAAQ,SAAU,OAAQ,IAAK,EAAG,UAAW,CAAE,EACnE,UAAA2B,EAAE,WAAW,IAAK8D,GACjB1F,EAAC,UAAe,QAAS,IAAMsE,EAAO1C,EAAE,GAAI8D,CAAC,EAAG,MAAO/C,EAAIuB,EAAUtC,EAAE,EAAE,IAAM8D,CAAC,EAC7E,SAAAA,GADUA,CAEb,CACD,EACAxB,EAAUtC,EAAE,EAAE,GACb5B,EAAC,UAAO,QAAS,IAAMuE,EAAa3C,EAAE,EAAE,EAAG,MAAOE,EAAAjB,EAAA,GAAK8B,EAAI,EAAK,GAAd,CAAiB,MAAO,MAAO,GAAG,iBAEpF,GAEJ,IAbQf,EAAE,EAcZ,CACD,EACAqC,EAAM,OAAS,GACdhE,EAAC,OAAI,MAAO,CAAE,UAAW,iBAAkB,WAAY,EAAG,UAAW,CAAE,EACrE,UAAAD,EAAC,OAAI,MAAO,CAAE,QAAS,GAAI,aAAc,CAAE,EAAG,iBAAK,EAClDiE,EAAM,IAAK7C,GAAM,CAChB,IAAMuE,EAAKxB,EAAc/C,EAAE,EAAE,EAC7B,OACEnB,EAAC,OAAe,MAAO,CAAE,QAAS,OAAQ,EACxC,UAAAD,EAAC,OAAI,MAAO,CAAE,WAAY,GAAI,EAAI,SAAAoB,EAAE,GAAG,EACtCA,EAAE,MACDpB,EAAC,OAAI,MAAO,CAAE,QAAS,OAAQ,SAAU,OAAQ,IAAK,EAAG,UAAW,CAAE,EACnE,SAAAoB,EAAE,KAAK,IAAKqD,GACXzE,EAAC,UAAiB,QAAS,IAAMwE,GAAcpD,EAAE,GAAIqD,CAAG,EAAG,MAAO9B,EAAIgD,IAAOlB,CAAG,EAC7E,SAAAA,GADUA,CAEb,CACD,EACH,EAEDrD,EAAE,MAAQ,OAAO,QAAQA,EAAE,IAAI,EAAE,IAAI,CAAC,CAACwD,EAAKM,CAAM,IACjDjF,EAAC,OAAc,MAAO,CAAE,UAAW,CAAE,EACnC,UAAAD,EAAC,OAAI,MAAO,CAAE,QAAS,GAAI,SAAU,EAAG,EAAI,SAAA4E,EAAI,EAChD5E,EAAC,OAAI,MAAO,CAAE,QAAS,OAAQ,SAAU,OAAQ,IAAK,CAAE,EACrD,SAAAkF,EAAO,IAAKQ,GACX1F,EAAC,UAEC,QAAS,IAAM2E,GAAcvD,EAAE,GAAIwD,EAAKc,EAAGtE,EAAE,IAAI,EACjD,MAAOuB,EAAI,CAAC,CAACgD,GAAM,OAAOA,GAAO,UAAYA,EAAGf,CAAG,IAAMc,CAAC,EAEzD,SAAAA,GAJIA,CAKP,CACD,EACH,IAZQd,CAaV,CACD,EACAe,IAAO,QACN3F,EAAC,UAAO,QAAS,IAAMmF,GAAU/D,EAAE,EAAE,EAAG,MAAOU,EAAAjB,EAAA,GAAK8B,EAAI,EAAK,GAAd,CAAiB,MAAO,OAAQ,UAAW,CAAE,GAAG,iBAE/F,IA9BMvB,EAAE,EAgCZ,CAEJ,CAAC,GACH,GAEJ,EACF,EACApB,EAAC,UACC,aAAW,oBACX,gBAAegD,EACf,QAAS,IAAMC,EAAS2C,GAAM,CAACA,CAAC,EAChC,MAAO,CAAE,MAAO,GAAI,OAAQ,GAAI,aAAc,GAAI,OAAQ,oBAAqB,WAAY,OAClF,QAAS,OAAQ,WAAY,SAAU,eAAgB,SAAU,QAAS,EAC1E,OAAQ,UAAW,UAAW,2BAA4B,EAEnE,SAAA5F,EAAC6C,GAAA,EAAa,EAChB,GACF,CAEJ","names":["useEffect","useReducer","useState","useSyncExternalStore","PERSONAS","PERSONA_DISPLAY","confidenceBand","SNAPSHOT_STORAGE_KEY_PREFIX","ssrFallback","state","w","getRegistered","state","getRegisteredSlots","getRegisteredSections","subscribeRegistry","fn","listeners","getRegistryVersion","store","w","setVariantOverride","componentId","variantId","clearVariantOverride","getOverrides","__spreadValues","store","w","setSlotOverride","slotId","result","clearSlotOverride","getSlotOverrides","__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","SentientMark","size","AdaptiveDevtools","open","setOpen","useState","activePersona","setActivePersona","mounted","setMounted","force","useReducer","useSyncExternalStore","subscribeRegistry","getRegistryVersion","useEffect","config","readDevtoolsConfig","useLocalEngine","components","slots","overrides","slotOverrides","getSlotOverrides","maybeExitPreview","choose","resetVariant","chooseSlotArm","arm","setSlotOverride","chooseSlotDim","dim","value","dims","current","next","d","values","resetSlot","clearSlotOverride","choosePersona","getPreviewMode","PERSONAS","p","PERSONA_DISPLAY","v","ov","o"]}
1
+ {"version":3,"sources":["../src/devtools/index.tsx","../src/devtools-registry.ts","../src/devtools-overrides.ts","../src/devtools-slot-overrides.ts","../src/preview-mode.ts","../src/override-events.ts","../src/devtools-config.ts"],"sourcesContent":["'use client';\nimport { useEffect, useReducer, useState, useSyncExternalStore, 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 getRegistryVersion,\n type RegisteredSlot,\n} from '../devtools-registry.js';\nimport { setVariantOverride, clearVariantOverride, getOverrides } from '../devtools-overrides.js';\nimport { setSlotOverride, clearSlotOverride, getSlotOverrides } from '../devtools-slot-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\n/** Sentient \"S.\" wordmark, monoline — white strokes for the black launcher button. */\nfunction SentientMark({ size = 26 }: { size?: number } = {}): JSX.Element {\n return (\n <svg width={size} height={size} viewBox=\"0 0 32 32\" fill=\"none\" aria-hidden=\"true\">\n <path\n d=\"M21 9H14C11.2 9 9.8 10.8 9.8 13C9.8 15.4 11.6 16.6 14.5 16.6H18.5C20.5 16.6 20.7 18.2 20.4 19.6\"\n stroke=\"#fff\"\n strokeWidth=\"2.2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n <path d=\"M10 22H17\" stroke=\"#fff\" strokeWidth=\"2.2\" strokeLinecap=\"round\" />\n <circle cx=\"20.6\" cy=\"22\" r=\"2.1\" fill=\"#fff\" />\n </svg>\n );\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 [mounted, setMounted] = useState(false);\n const [, force] = useReducer((n: number) => n + 1, 0);\n // Re-render when the component/slot registry changes. useSyncExternalStore\n // (not a manual useEffect subscription) so the subscription can't go stale\n // across Next Fast Refresh — a stale listener was why the panel needed a hard\n // refresh to pick up a renamed id.\n useSyncExternalStore(subscribeRegistry, getRegistryVersion, () => 0);\n // Client-mount gate: render nothing during SSR / the first paint so the widget\n // never reads `window` on the server. Lets consumers drop `<AdaptiveDevtools/>`\n // straight into a tree without a `dynamic(..., { ssr: false })` wrapper.\n useEffect(() => setMounted(true), []);\n\n // Never render in production, even if imported by mistake. (Hooks run first\n // so the rules of hooks hold regardless of these early returns.)\n if (IS_PROD) return null;\n if (!mounted) 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 slots = getRegisteredSlots();\n const overrides = getOverrides();\n const slotOverrides = getSlotOverrides();\n\n // Preview mode stays on while ANY override (variant or slot) is active.\n function maybeExitPreview(): void {\n if (Object.keys(getOverrides()).length === 0 && Object.keys(getSlotOverrides()).length === 0) {\n setPreviewMode(false);\n }\n }\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 maybeExitPreview();\n notifyOverridesChanged();\n force();\n }\n function chooseSlotArm(id: string, arm: string): void {\n setSlotOverride(id, arm);\n setPreviewMode(true);\n notifyOverridesChanged();\n force();\n }\n function chooseSlotDim(id: string, dim: string, value: string, dims: RegisteredSlot['dims']): void {\n const current = getSlotOverrides()[id];\n // Merge onto the existing forced object, or seed from each dim's baseline\n // (first value) so unset dims stay at baseline instead of vanishing.\n const next: Record<string, string> =\n current && typeof current === 'object'\n ? { ...current }\n : Object.fromEntries(Object.entries(dims ?? {}).map(([d, values]) => [d, values[0]!]));\n next[dim] = value;\n setSlotOverride(id, next);\n setPreviewMode(true);\n notifyOverridesChanged();\n force();\n }\n function resetSlot(id: string): void {\n clearSlotOverride(id);\n maybeExitPreview();\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 {/* Panel stays mounted and animates in/out from the button's corner, so the\n launcher below never shifts. transform-origin is the button (bottom-right). */}\n <div\n aria-hidden={!open}\n style={{\n position: 'absolute',\n bottom: 52,\n right: 0,\n transformOrigin: 'bottom right',\n transition: 'opacity .18s ease, transform .2s cubic-bezier(.16,1,.3,1)',\n opacity: open ? 1 : 0,\n transform: open ? 'translateY(0) scale(1)' : 'translateY(6px) scale(.96)',\n pointerEvents: open ? 'auto' : 'none',\n }}\n >\n <div style={{ width: 300, maxHeight: 460, overflowY: 'auto', background: '#111', color: '#eee',\n borderRadius: 8, padding: 12, 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 || Object.keys(slotOverrides).length > 0) && (\n <button onClick={resetAll} style={{ ...btn(false), color: '#aaa' }}>\n Reset\n </button>\n )}\n </div>\n </div>\n {components.length === 0 && slots.length === 0 && (\n <div style={{ opacity: .6 }}>No components or slots on this page yet.</div>\n )}\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 {slots.length > 0 && (\n <div style={{ borderTop: '1px solid #333', paddingTop: 8, marginTop: 4 }}>\n <div style={{ opacity: .7, marginBottom: 4 }}>Slots</div>\n {slots.map((s) => {\n const ov = slotOverrides[s.id];\n return (\n <div key={s.id} style={{ padding: '6px 0' }}>\n <div style={{ fontWeight: 600 }}>{s.id}</div>\n {s.arms && (\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4, marginTop: 4 }}>\n {s.arms.map((arm) => (\n <button key={arm} onClick={() => chooseSlotArm(s.id, arm)} style={btn(ov === arm)}>\n {arm}\n </button>\n ))}\n </div>\n )}\n {s.dims && Object.entries(s.dims).map(([dim, values]) => (\n <div key={dim} style={{ marginTop: 4 }}>\n <div style={{ opacity: .6, fontSize: 11 }}>{dim}</div>\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4 }}>\n {values.map((v) => (\n <button\n key={v}\n onClick={() => chooseSlotDim(s.id, dim, v, s.dims)}\n style={btn(!!ov && typeof ov === 'object' && ov[dim] === v)}\n >\n {v}\n </button>\n ))}\n </div>\n </div>\n ))}\n {ov !== undefined && (\n <button onClick={() => resetSlot(s.id)} style={{ ...btn(false), color: '#aaa', marginTop: 4 }}>\n reset\n </button>\n )}\n </div>\n );\n })}\n </div>\n )}\n </div>\n </div>\n <button\n aria-label=\"Sentient DevTools\"\n aria-expanded={open}\n onClick={() => setOpen((o) => !o)}\n style={{ width: 44, height: 44, borderRadius: 12, border: '1px solid #2a2a2a', background: '#000',\n display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 0,\n cursor: 'pointer', boxShadow: '0 6px 18px rgba(0,0,0,.4)' }}\n >\n <SentientMark />\n </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 /** Bumped on every mutation so `useSyncExternalStore` can read a stable, comparable snapshot. */\n version: number;\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 version: 0,\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 version: 0,\n };\n }\n return w.__sentient_registry;\n}\n\nfunction emit(): void {\n // Bump BEFORE notifying so a useSyncExternalStore consumer re-reading its\n // snapshot inside the notification sees the new value and re-renders.\n state().version += 1;\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\n/** Monotonic snapshot for `useSyncExternalStore` — changes on every registry mutation. */\nexport function getRegistryVersion(): number {\n return state().version;\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 { SlotResult } from '@sentientui/core';\n\n/**\n * Slot-override store — the channel `useSlotResult` reads (`useAdaptiveTokens` /\n * `AdaptiveGroup`). Separate from `__sentient_overrides` (variant components):\n * a slot result is a token object (`{ tone: 'urgent' }`) or an arm string\n * (`'social_first'`), never a bare variant id. Window-backed so the /devtools\n * bundle and the main bundle share one store.\n */\nfunction store(): Record<string, SlotResult> {\n const w = window as unknown as { __sentient_slot_overrides?: Record<string, SlotResult> };\n if (!w.__sentient_slot_overrides) w.__sentient_slot_overrides = {};\n return w.__sentient_slot_overrides;\n}\n\n/** Force `slotId` to resolve to `result` — read by `useSlotResult`. */\nexport function setSlotOverride(slotId: string, result: SlotResult): void {\n store()[slotId] = result;\n}\n\nexport function clearSlotOverride(slotId: string): void {\n delete store()[slotId];\n}\n\nexport function getSlotOverrides(): Record<string, SlotResult> {\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 dispose: () => inner.dispose(),\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","useSyncExternalStore","PERSONAS","PERSONA_DISPLAY","confidenceBand","SNAPSHOT_STORAGE_KEY_PREFIX","ssrFallback","state","w","getRegistered","state","getRegisteredSlots","getRegisteredSections","subscribeRegistry","fn","listeners","getRegistryVersion","store","w","setVariantOverride","componentId","variantId","clearVariantOverride","getOverrides","__spreadValues","store","w","setSlotOverride","slotId","result","clearSlotOverride","getSlotOverrides","__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","SentientMark","size","AdaptiveDevtools","open","setOpen","useState","activePersona","setActivePersona","mounted","setMounted","force","useReducer","useSyncExternalStore","subscribeRegistry","getRegistryVersion","useEffect","config","readDevtoolsConfig","useLocalEngine","components","slots","overrides","slotOverrides","getSlotOverrides","maybeExitPreview","choose","resetVariant","chooseSlotArm","arm","setSlotOverride","chooseSlotDim","dim","value","dims","current","next","d","values","resetSlot","clearSlotOverride","choosePersona","getPreviewMode","PERSONAS","p","PERSONA_DISPLAY","v","ov","o"]}
package/dist/index.d.cts CHANGED
@@ -215,6 +215,12 @@ type AssignmentState = {
215
215
  /** Populated when the assigned variant is a dashboard-managed text variant. */
216
216
  content: string | null;
217
217
  isLoading: boolean;
218
+ /**
219
+ * True while a dev override (?sentient_variant= / window.__sentient_overrides)
220
+ * is forcing this variant. Consumers must suppress ALL tracking while set —
221
+ * the override contract is "no events recorded, weights unchanged".
222
+ */
223
+ isOverride?: boolean;
218
224
  };
219
225
  /**
220
226
  * Returns a sticky variant assignment for a component.
package/dist/index.d.ts CHANGED
@@ -215,6 +215,12 @@ type AssignmentState = {
215
215
  /** Populated when the assigned variant is a dashboard-managed text variant. */
216
216
  content: string | null;
217
217
  isLoading: boolean;
218
+ /**
219
+ * True while a dev override (?sentient_variant= / window.__sentient_overrides)
220
+ * is forcing this variant. Consumers must suppress ALL tracking while set —
221
+ * the override contract is "no events recorded, weights unchanged".
222
+ */
223
+ isOverride?: boolean;
218
224
  };
219
225
  /**
220
226
  * Returns a sticky variant assignment for a component.