@perspective-ai/sdk-react 1.0.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,529 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var sdk = require('@perspective-ai/sdk');
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+
7
+ var useIsomorphicLayoutEffect = typeof window !== "undefined" ? react.useLayoutEffect : react.useEffect;
8
+ function useStableCallback(callback) {
9
+ const callbackRef = react.useRef(callback);
10
+ useIsomorphicLayoutEffect(() => {
11
+ callbackRef.current = callback;
12
+ });
13
+ return react.useCallback(
14
+ ((...args) => callbackRef.current?.(...args)),
15
+ []
16
+ );
17
+ }
18
+ function Widget({
19
+ researchId,
20
+ params,
21
+ brand,
22
+ theme,
23
+ host,
24
+ onReady,
25
+ onSubmit,
26
+ onNavigate,
27
+ onClose,
28
+ onError,
29
+ embedRef,
30
+ className,
31
+ style,
32
+ ...divProps
33
+ }) {
34
+ const containerRef = react.useRef(null);
35
+ const handleRef = react.useRef(null);
36
+ const stableOnReady = useStableCallback(onReady);
37
+ const stableOnSubmit = useStableCallback(onSubmit);
38
+ const stableOnNavigate = useStableCallback(onNavigate);
39
+ const stableOnClose = useStableCallback(onClose);
40
+ const stableOnError = useStableCallback(onError);
41
+ react.useEffect(() => {
42
+ const container = containerRef.current;
43
+ if (!container) return;
44
+ const handle = sdk.createWidget(container, {
45
+ researchId,
46
+ params,
47
+ brand,
48
+ theme,
49
+ host,
50
+ onReady: stableOnReady,
51
+ onSubmit: stableOnSubmit,
52
+ onNavigate: stableOnNavigate,
53
+ onClose: stableOnClose,
54
+ onError: stableOnError
55
+ });
56
+ handleRef.current = handle;
57
+ if (embedRef) {
58
+ embedRef.current = handle;
59
+ }
60
+ return () => {
61
+ handle.unmount();
62
+ handleRef.current = null;
63
+ if (embedRef) {
64
+ embedRef.current = null;
65
+ }
66
+ };
67
+ }, [
68
+ researchId,
69
+ params,
70
+ brand,
71
+ theme,
72
+ host,
73
+ stableOnReady,
74
+ stableOnSubmit,
75
+ stableOnNavigate,
76
+ stableOnClose,
77
+ stableOnError,
78
+ embedRef
79
+ ]);
80
+ return /* @__PURE__ */ jsxRuntime.jsx(
81
+ "div",
82
+ {
83
+ ref: containerRef,
84
+ className,
85
+ style: { minHeight: 500, ...style },
86
+ "data-testid": "perspective-widget",
87
+ ...divProps
88
+ }
89
+ );
90
+ }
91
+ function PopupButton({
92
+ researchId,
93
+ params,
94
+ brand,
95
+ theme,
96
+ host,
97
+ onReady,
98
+ onSubmit,
99
+ onNavigate,
100
+ onClose,
101
+ onError,
102
+ children,
103
+ open,
104
+ onOpenChange,
105
+ embedRef,
106
+ onClick,
107
+ ...buttonProps
108
+ }) {
109
+ const handleRef = react.useRef(null);
110
+ const [internalOpen, setInternalOpen] = react.useState(false);
111
+ const isControlled = open !== void 0;
112
+ const isOpen = isControlled ? open : internalOpen;
113
+ const stableOnReady = useStableCallback(onReady);
114
+ const stableOnSubmit = useStableCallback(onSubmit);
115
+ const stableOnNavigate = useStableCallback(onNavigate);
116
+ const stableOnError = useStableCallback(onError);
117
+ const setOpen = react.useCallback(
118
+ (value) => {
119
+ if (isControlled) {
120
+ onOpenChange?.(value);
121
+ } else {
122
+ setInternalOpen(value);
123
+ }
124
+ },
125
+ [isControlled, onOpenChange]
126
+ );
127
+ const handleClose = react.useCallback(() => {
128
+ handleRef.current = null;
129
+ setOpen(false);
130
+ onClose?.();
131
+ }, [setOpen, onClose]);
132
+ const stableOnClose = useStableCallback(handleClose);
133
+ const createPopup = react.useCallback(() => {
134
+ if (handleRef.current) return handleRef.current;
135
+ const handle = sdk.openPopup({
136
+ researchId,
137
+ params,
138
+ brand,
139
+ theme,
140
+ host,
141
+ onReady: stableOnReady,
142
+ onSubmit: stableOnSubmit,
143
+ onNavigate: stableOnNavigate,
144
+ onClose: stableOnClose,
145
+ onError: stableOnError
146
+ });
147
+ handleRef.current = handle;
148
+ return handle;
149
+ }, [
150
+ researchId,
151
+ params,
152
+ brand,
153
+ theme,
154
+ host,
155
+ stableOnReady,
156
+ stableOnSubmit,
157
+ stableOnNavigate,
158
+ stableOnClose,
159
+ stableOnError
160
+ ]);
161
+ const proxyHandle = react.useMemo(
162
+ () => ({
163
+ open: () => {
164
+ createPopup();
165
+ setOpen(true);
166
+ },
167
+ close: () => {
168
+ handleRef.current?.destroy();
169
+ handleRef.current = null;
170
+ setOpen(false);
171
+ },
172
+ toggle: () => {
173
+ if (handleRef.current) {
174
+ handleRef.current.destroy();
175
+ handleRef.current = null;
176
+ setOpen(false);
177
+ } else {
178
+ createPopup();
179
+ setOpen(true);
180
+ }
181
+ },
182
+ unmount: () => {
183
+ handleRef.current?.unmount();
184
+ handleRef.current = null;
185
+ setOpen(false);
186
+ },
187
+ get isOpen() {
188
+ return isOpen;
189
+ },
190
+ researchId
191
+ }),
192
+ [createPopup, setOpen, researchId, isOpen]
193
+ );
194
+ react.useEffect(() => {
195
+ if (embedRef) {
196
+ embedRef.current = proxyHandle;
197
+ }
198
+ return () => {
199
+ if (embedRef) {
200
+ embedRef.current = null;
201
+ }
202
+ };
203
+ }, [embedRef, proxyHandle]);
204
+ react.useEffect(() => {
205
+ if (!isControlled) return;
206
+ if (open && !handleRef.current) {
207
+ createPopup();
208
+ } else if (!open && handleRef.current) {
209
+ handleRef.current.destroy();
210
+ handleRef.current = null;
211
+ }
212
+ }, [open, isControlled, createPopup]);
213
+ const handleClick = react.useCallback(
214
+ (e) => {
215
+ onClick?.(e);
216
+ if (e.defaultPrevented) return;
217
+ if (isOpen && handleRef.current) {
218
+ handleRef.current.destroy();
219
+ handleRef.current = null;
220
+ setOpen(false);
221
+ } else {
222
+ createPopup();
223
+ setOpen(true);
224
+ }
225
+ },
226
+ [onClick, isOpen, createPopup, setOpen]
227
+ );
228
+ return /* @__PURE__ */ jsxRuntime.jsx(
229
+ "button",
230
+ {
231
+ type: "button",
232
+ onClick: handleClick,
233
+ "data-testid": "perspective-popup-button",
234
+ ...buttonProps,
235
+ children
236
+ }
237
+ );
238
+ }
239
+ function SliderButton({
240
+ researchId,
241
+ params,
242
+ brand,
243
+ theme,
244
+ host,
245
+ onReady,
246
+ onSubmit,
247
+ onNavigate,
248
+ onClose,
249
+ onError,
250
+ children,
251
+ open,
252
+ onOpenChange,
253
+ embedRef,
254
+ onClick,
255
+ ...buttonProps
256
+ }) {
257
+ const handleRef = react.useRef(null);
258
+ const [internalOpen, setInternalOpen] = react.useState(false);
259
+ const isControlled = open !== void 0;
260
+ const isOpen = isControlled ? open : internalOpen;
261
+ const stableOnReady = useStableCallback(onReady);
262
+ const stableOnSubmit = useStableCallback(onSubmit);
263
+ const stableOnNavigate = useStableCallback(onNavigate);
264
+ const stableOnError = useStableCallback(onError);
265
+ const setOpen = react.useCallback(
266
+ (value) => {
267
+ if (isControlled) {
268
+ onOpenChange?.(value);
269
+ } else {
270
+ setInternalOpen(value);
271
+ }
272
+ },
273
+ [isControlled, onOpenChange]
274
+ );
275
+ const handleClose = react.useCallback(() => {
276
+ handleRef.current = null;
277
+ setOpen(false);
278
+ onClose?.();
279
+ }, [setOpen, onClose]);
280
+ const stableOnClose = useStableCallback(handleClose);
281
+ const createSlider = react.useCallback(() => {
282
+ if (handleRef.current) return handleRef.current;
283
+ const handle = sdk.openSlider({
284
+ researchId,
285
+ params,
286
+ brand,
287
+ theme,
288
+ host,
289
+ onReady: stableOnReady,
290
+ onSubmit: stableOnSubmit,
291
+ onNavigate: stableOnNavigate,
292
+ onClose: stableOnClose,
293
+ onError: stableOnError
294
+ });
295
+ handleRef.current = handle;
296
+ return handle;
297
+ }, [
298
+ researchId,
299
+ params,
300
+ brand,
301
+ theme,
302
+ host,
303
+ stableOnReady,
304
+ stableOnSubmit,
305
+ stableOnNavigate,
306
+ stableOnClose,
307
+ stableOnError
308
+ ]);
309
+ const proxyHandle = react.useMemo(
310
+ () => ({
311
+ open: () => {
312
+ createSlider();
313
+ setOpen(true);
314
+ },
315
+ close: () => {
316
+ handleRef.current?.destroy();
317
+ handleRef.current = null;
318
+ setOpen(false);
319
+ },
320
+ toggle: () => {
321
+ if (handleRef.current) {
322
+ handleRef.current.destroy();
323
+ handleRef.current = null;
324
+ setOpen(false);
325
+ } else {
326
+ createSlider();
327
+ setOpen(true);
328
+ }
329
+ },
330
+ unmount: () => {
331
+ handleRef.current?.unmount();
332
+ handleRef.current = null;
333
+ setOpen(false);
334
+ },
335
+ get isOpen() {
336
+ return isOpen;
337
+ },
338
+ researchId
339
+ }),
340
+ [createSlider, setOpen, researchId, isOpen]
341
+ );
342
+ react.useEffect(() => {
343
+ if (embedRef) {
344
+ embedRef.current = proxyHandle;
345
+ }
346
+ return () => {
347
+ if (embedRef) {
348
+ embedRef.current = null;
349
+ }
350
+ };
351
+ }, [embedRef, proxyHandle]);
352
+ react.useEffect(() => {
353
+ if (!isControlled) return;
354
+ if (open && !handleRef.current) {
355
+ createSlider();
356
+ } else if (!open && handleRef.current) {
357
+ handleRef.current.destroy();
358
+ handleRef.current = null;
359
+ }
360
+ }, [open, isControlled, createSlider]);
361
+ const handleClick = react.useCallback(
362
+ (e) => {
363
+ onClick?.(e);
364
+ if (e.defaultPrevented) return;
365
+ if (isOpen && handleRef.current) {
366
+ handleRef.current.destroy();
367
+ handleRef.current = null;
368
+ setOpen(false);
369
+ } else {
370
+ createSlider();
371
+ setOpen(true);
372
+ }
373
+ },
374
+ [onClick, isOpen, createSlider, setOpen]
375
+ );
376
+ return /* @__PURE__ */ jsxRuntime.jsx(
377
+ "button",
378
+ {
379
+ type: "button",
380
+ onClick: handleClick,
381
+ "data-testid": "perspective-slider-button",
382
+ ...buttonProps,
383
+ children
384
+ }
385
+ );
386
+ }
387
+ function FloatBubble({
388
+ researchId,
389
+ params,
390
+ brand,
391
+ theme,
392
+ host,
393
+ onReady,
394
+ onSubmit,
395
+ onNavigate,
396
+ onClose,
397
+ onError,
398
+ embedRef
399
+ }) {
400
+ const handleRef = react.useRef(null);
401
+ const stableOnReady = useStableCallback(onReady);
402
+ const stableOnSubmit = useStableCallback(onSubmit);
403
+ const stableOnNavigate = useStableCallback(onNavigate);
404
+ const stableOnClose = useStableCallback(onClose);
405
+ const stableOnError = useStableCallback(onError);
406
+ react.useEffect(() => {
407
+ const handle = sdk.createFloatBubble({
408
+ researchId,
409
+ params,
410
+ brand,
411
+ theme,
412
+ host,
413
+ onReady: stableOnReady,
414
+ onSubmit: stableOnSubmit,
415
+ onNavigate: stableOnNavigate,
416
+ onClose: stableOnClose,
417
+ onError: stableOnError
418
+ });
419
+ handleRef.current = handle;
420
+ if (embedRef) {
421
+ embedRef.current = handle;
422
+ }
423
+ return () => {
424
+ handle.unmount();
425
+ handleRef.current = null;
426
+ if (embedRef) {
427
+ embedRef.current = null;
428
+ }
429
+ };
430
+ }, [
431
+ researchId,
432
+ params,
433
+ brand,
434
+ theme,
435
+ host,
436
+ stableOnReady,
437
+ stableOnSubmit,
438
+ stableOnNavigate,
439
+ stableOnClose,
440
+ stableOnError,
441
+ embedRef
442
+ ]);
443
+ return null;
444
+ }
445
+ function Fullpage({
446
+ researchId,
447
+ params,
448
+ brand,
449
+ theme,
450
+ host,
451
+ onReady,
452
+ onSubmit,
453
+ onNavigate,
454
+ onClose,
455
+ onError,
456
+ embedRef
457
+ }) {
458
+ const handleRef = react.useRef(null);
459
+ const stableOnReady = useStableCallback(onReady);
460
+ const stableOnSubmit = useStableCallback(onSubmit);
461
+ const stableOnNavigate = useStableCallback(onNavigate);
462
+ const stableOnClose = useStableCallback(onClose);
463
+ const stableOnError = useStableCallback(onError);
464
+ react.useEffect(() => {
465
+ const handle = sdk.createFullpage({
466
+ researchId,
467
+ params,
468
+ brand,
469
+ theme,
470
+ host,
471
+ onReady: stableOnReady,
472
+ onSubmit: stableOnSubmit,
473
+ onNavigate: stableOnNavigate,
474
+ onClose: stableOnClose,
475
+ onError: stableOnError
476
+ });
477
+ handleRef.current = handle;
478
+ if (embedRef) {
479
+ embedRef.current = handle;
480
+ }
481
+ return () => {
482
+ handle.unmount();
483
+ handleRef.current = null;
484
+ if (embedRef) {
485
+ embedRef.current = null;
486
+ }
487
+ };
488
+ }, [
489
+ researchId,
490
+ params,
491
+ brand,
492
+ theme,
493
+ host,
494
+ stableOnReady,
495
+ stableOnSubmit,
496
+ stableOnNavigate,
497
+ stableOnClose,
498
+ stableOnError,
499
+ embedRef
500
+ ]);
501
+ return null;
502
+ }
503
+ function useThemeSync(theme = "system") {
504
+ const [resolved, setResolved] = react.useState(
505
+ theme !== "system" ? theme : "light"
506
+ );
507
+ react.useEffect(() => {
508
+ if (theme !== "system") {
509
+ setResolved(theme);
510
+ return;
511
+ }
512
+ const mq = window.matchMedia("(prefers-color-scheme: dark)");
513
+ setResolved(mq.matches ? "dark" : "light");
514
+ const handler = (e) => setResolved(e.matches ? "dark" : "light");
515
+ mq.addEventListener("change", handler);
516
+ return () => mq.removeEventListener("change", handler);
517
+ }, [theme]);
518
+ return resolved;
519
+ }
520
+
521
+ exports.FloatBubble = FloatBubble;
522
+ exports.Fullpage = Fullpage;
523
+ exports.PopupButton = PopupButton;
524
+ exports.SliderButton = SliderButton;
525
+ exports.Widget = Widget;
526
+ exports.useStableCallback = useStableCallback;
527
+ exports.useThemeSync = useThemeSync;
528
+ //# sourceMappingURL=index.cjs.map
529
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hooks/useStableCallback.ts","../src/Widget.tsx","../src/PopupButton.tsx","../src/SliderButton.tsx","../src/FloatBubble.tsx","../src/Fullpage.tsx","../src/hooks/useThemeSync.ts"],"names":["useLayoutEffect","useEffect","useRef","useCallback","createWidget","jsx","useState","openPopup","useMemo","openSlider","createFloatBubble","createFullpage"],"mappings":"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","file":"index.cjs","sourcesContent":["import { useRef, useCallback, useLayoutEffect, useEffect } from \"react\";\n\nconst useIsomorphicLayoutEffect =\n typeof window !== \"undefined\" ? useLayoutEffect : useEffect;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function useStableCallback<T extends (...args: any[]) => any>(\n callback: T | undefined\n): T {\n const callbackRef = useRef(callback);\n\n useIsomorphicLayoutEffect(() => {\n callbackRef.current = callback;\n });\n\n return useCallback(\n ((...args: Parameters<T>) => callbackRef.current?.(...args)) as T,\n []\n );\n}\n","import { useRef, useEffect, type HTMLAttributes, type RefObject } from \"react\";\nimport {\n createWidget,\n type EmbedConfig,\n type EmbedHandle,\n} from \"@perspective-ai/sdk\";\nimport { useStableCallback } from \"./hooks/useStableCallback\";\n\nexport interface WidgetProps\n extends\n Omit<EmbedConfig, \"type\">,\n Omit<HTMLAttributes<HTMLDivElement>, \"onError\" | \"onSubmit\"> {\n /** Ref to access the embed handle for programmatic control */\n embedRef?: RefObject<EmbedHandle | null>;\n}\n\n/**\n * Inline widget embed component.\n * Renders the interview directly in a container.\n */\nexport function Widget({\n researchId,\n params,\n brand,\n theme,\n host,\n onReady,\n onSubmit,\n onNavigate,\n onClose,\n onError,\n embedRef,\n className,\n style,\n ...divProps\n}: WidgetProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const handleRef = useRef<EmbedHandle | null>(null);\n\n // Stable callbacks to avoid re-mounting on callback changes\n const stableOnReady = useStableCallback(onReady);\n const stableOnSubmit = useStableCallback(onSubmit);\n const stableOnNavigate = useStableCallback(onNavigate);\n const stableOnClose = useStableCallback(onClose);\n const stableOnError = useStableCallback(onError);\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n const handle = createWidget(container, {\n researchId,\n params,\n brand,\n theme,\n host,\n onReady: stableOnReady,\n onSubmit: stableOnSubmit,\n onNavigate: stableOnNavigate,\n onClose: stableOnClose,\n onError: stableOnError,\n });\n\n handleRef.current = handle;\n\n if (embedRef) {\n embedRef.current = handle;\n }\n\n return () => {\n handle.unmount();\n handleRef.current = null;\n if (embedRef) {\n embedRef.current = null;\n }\n };\n }, [\n researchId,\n params,\n brand,\n theme,\n host,\n stableOnReady,\n stableOnSubmit,\n stableOnNavigate,\n stableOnClose,\n stableOnError,\n embedRef,\n ]);\n\n return (\n <div\n ref={containerRef}\n className={className}\n style={{ minHeight: 500, ...style }}\n data-testid=\"perspective-widget\"\n {...divProps}\n />\n );\n}\n","import {\n useRef,\n useCallback,\n useState,\n useEffect,\n useMemo,\n type ReactNode,\n type ButtonHTMLAttributes,\n type RefObject,\n} from \"react\";\nimport {\n openPopup,\n type EmbedConfig,\n type EmbedHandle,\n} from \"@perspective-ai/sdk\";\nimport { useStableCallback } from \"./hooks/useStableCallback\";\n\n/** Handle for programmatic control of popup button */\nexport interface PopupButtonHandle {\n open: () => void;\n close: () => void;\n toggle: () => void;\n unmount: () => void;\n readonly isOpen: boolean;\n readonly researchId: string;\n}\n\nexport interface PopupButtonProps\n extends\n Omit<EmbedConfig, \"type\">,\n Omit<ButtonHTMLAttributes<HTMLButtonElement>, \"onError\" | \"onSubmit\"> {\n /** Button content */\n children: ReactNode;\n /** Controlled open state */\n open?: boolean;\n /** Callback when open state changes */\n onOpenChange?: (open: boolean) => void;\n /** Ref to access the handle for programmatic control */\n embedRef?: RefObject<PopupButtonHandle | null>;\n}\n\n/**\n * Button that opens a popup modal when clicked.\n * Supports both controlled and uncontrolled modes.\n */\nexport function PopupButton({\n researchId,\n params,\n brand,\n theme,\n host,\n onReady,\n onSubmit,\n onNavigate,\n onClose,\n onError,\n children,\n open,\n onOpenChange,\n embedRef,\n onClick,\n ...buttonProps\n}: PopupButtonProps) {\n const handleRef = useRef<EmbedHandle | null>(null);\n const [internalOpen, setInternalOpen] = useState(false);\n\n const isControlled = open !== undefined;\n const isOpen = isControlled ? open : internalOpen;\n\n const stableOnReady = useStableCallback(onReady);\n const stableOnSubmit = useStableCallback(onSubmit);\n const stableOnNavigate = useStableCallback(onNavigate);\n const stableOnError = useStableCallback(onError);\n\n const setOpen = useCallback(\n (value: boolean) => {\n if (isControlled) {\n onOpenChange?.(value);\n } else {\n setInternalOpen(value);\n }\n },\n [isControlled, onOpenChange]\n );\n\n const handleClose = useCallback(() => {\n handleRef.current = null;\n setOpen(false);\n onClose?.();\n }, [setOpen, onClose]);\n\n const stableOnClose = useStableCallback(handleClose);\n\n const createPopup = useCallback(() => {\n if (handleRef.current) return handleRef.current;\n\n const handle = openPopup({\n researchId,\n params,\n brand,\n theme,\n host,\n onReady: stableOnReady,\n onSubmit: stableOnSubmit,\n onNavigate: stableOnNavigate,\n onClose: stableOnClose,\n onError: stableOnError,\n });\n\n handleRef.current = handle;\n return handle;\n }, [\n researchId,\n params,\n brand,\n theme,\n host,\n stableOnReady,\n stableOnSubmit,\n stableOnNavigate,\n stableOnClose,\n stableOnError,\n ]);\n\n const proxyHandle = useMemo<PopupButtonHandle>(\n () => ({\n open: () => {\n createPopup();\n setOpen(true);\n },\n close: () => {\n handleRef.current?.destroy();\n handleRef.current = null;\n setOpen(false);\n },\n toggle: () => {\n if (handleRef.current) {\n handleRef.current.destroy();\n handleRef.current = null;\n setOpen(false);\n } else {\n createPopup();\n setOpen(true);\n }\n },\n unmount: () => {\n handleRef.current?.unmount();\n handleRef.current = null;\n setOpen(false);\n },\n get isOpen() {\n return isOpen;\n },\n researchId,\n }),\n [createPopup, setOpen, researchId, isOpen]\n );\n\n useEffect(() => {\n if (embedRef) {\n embedRef.current = proxyHandle;\n }\n return () => {\n if (embedRef) {\n embedRef.current = null;\n }\n };\n }, [embedRef, proxyHandle]);\n\n useEffect(() => {\n if (!isControlled) return;\n\n if (open && !handleRef.current) {\n createPopup();\n } else if (!open && handleRef.current) {\n handleRef.current.destroy();\n handleRef.current = null;\n }\n }, [open, isControlled, createPopup]);\n\n const handleClick = useCallback(\n (e: React.MouseEvent<HTMLButtonElement>) => {\n onClick?.(e);\n if (e.defaultPrevented) return;\n\n if (isOpen && handleRef.current) {\n handleRef.current.destroy();\n handleRef.current = null;\n setOpen(false);\n } else {\n createPopup();\n setOpen(true);\n }\n },\n [onClick, isOpen, createPopup, setOpen]\n );\n\n return (\n <button\n type=\"button\"\n onClick={handleClick}\n data-testid=\"perspective-popup-button\"\n {...buttonProps}\n >\n {children}\n </button>\n );\n}\n","import {\n useRef,\n useCallback,\n useState,\n useEffect,\n useMemo,\n type ReactNode,\n type ButtonHTMLAttributes,\n type RefObject,\n} from \"react\";\nimport {\n openSlider,\n type EmbedConfig,\n type EmbedHandle,\n} from \"@perspective-ai/sdk\";\nimport { useStableCallback } from \"./hooks/useStableCallback\";\n\n/** Handle for programmatic control of slider button */\nexport interface SliderButtonHandle {\n open: () => void;\n close: () => void;\n toggle: () => void;\n unmount: () => void;\n readonly isOpen: boolean;\n readonly researchId: string;\n}\n\nexport interface SliderButtonProps\n extends\n Omit<EmbedConfig, \"type\">,\n Omit<ButtonHTMLAttributes<HTMLButtonElement>, \"onError\" | \"onSubmit\"> {\n /** Button content */\n children: ReactNode;\n /** Controlled open state */\n open?: boolean;\n /** Callback when open state changes */\n onOpenChange?: (open: boolean) => void;\n /** Ref to access the handle for programmatic control */\n embedRef?: RefObject<SliderButtonHandle | null>;\n}\n\n/**\n * Button that opens a slider panel when clicked.\n * Supports both controlled and uncontrolled modes.\n */\nexport function SliderButton({\n researchId,\n params,\n brand,\n theme,\n host,\n onReady,\n onSubmit,\n onNavigate,\n onClose,\n onError,\n children,\n open,\n onOpenChange,\n embedRef,\n onClick,\n ...buttonProps\n}: SliderButtonProps) {\n const handleRef = useRef<EmbedHandle | null>(null);\n const [internalOpen, setInternalOpen] = useState(false);\n\n const isControlled = open !== undefined;\n const isOpen = isControlled ? open : internalOpen;\n\n const stableOnReady = useStableCallback(onReady);\n const stableOnSubmit = useStableCallback(onSubmit);\n const stableOnNavigate = useStableCallback(onNavigate);\n const stableOnError = useStableCallback(onError);\n\n const setOpen = useCallback(\n (value: boolean) => {\n if (isControlled) {\n onOpenChange?.(value);\n } else {\n setInternalOpen(value);\n }\n },\n [isControlled, onOpenChange]\n );\n\n const handleClose = useCallback(() => {\n handleRef.current = null;\n setOpen(false);\n onClose?.();\n }, [setOpen, onClose]);\n\n const stableOnClose = useStableCallback(handleClose);\n\n const createSlider = useCallback(() => {\n if (handleRef.current) return handleRef.current;\n\n const handle = openSlider({\n researchId,\n params,\n brand,\n theme,\n host,\n onReady: stableOnReady,\n onSubmit: stableOnSubmit,\n onNavigate: stableOnNavigate,\n onClose: stableOnClose,\n onError: stableOnError,\n });\n\n handleRef.current = handle;\n return handle;\n }, [\n researchId,\n params,\n brand,\n theme,\n host,\n stableOnReady,\n stableOnSubmit,\n stableOnNavigate,\n stableOnClose,\n stableOnError,\n ]);\n\n const proxyHandle = useMemo<SliderButtonHandle>(\n () => ({\n open: () => {\n createSlider();\n setOpen(true);\n },\n close: () => {\n handleRef.current?.destroy();\n handleRef.current = null;\n setOpen(false);\n },\n toggle: () => {\n if (handleRef.current) {\n handleRef.current.destroy();\n handleRef.current = null;\n setOpen(false);\n } else {\n createSlider();\n setOpen(true);\n }\n },\n unmount: () => {\n handleRef.current?.unmount();\n handleRef.current = null;\n setOpen(false);\n },\n get isOpen() {\n return isOpen;\n },\n researchId,\n }),\n [createSlider, setOpen, researchId, isOpen]\n );\n\n useEffect(() => {\n if (embedRef) {\n embedRef.current = proxyHandle;\n }\n return () => {\n if (embedRef) {\n embedRef.current = null;\n }\n };\n }, [embedRef, proxyHandle]);\n\n useEffect(() => {\n if (!isControlled) return;\n\n if (open && !handleRef.current) {\n createSlider();\n } else if (!open && handleRef.current) {\n handleRef.current.destroy();\n handleRef.current = null;\n }\n }, [open, isControlled, createSlider]);\n\n const handleClick = useCallback(\n (e: React.MouseEvent<HTMLButtonElement>) => {\n onClick?.(e);\n if (e.defaultPrevented) return;\n\n if (isOpen && handleRef.current) {\n handleRef.current.destroy();\n handleRef.current = null;\n setOpen(false);\n } else {\n createSlider();\n setOpen(true);\n }\n },\n [onClick, isOpen, createSlider, setOpen]\n );\n\n return (\n <button\n type=\"button\"\n onClick={handleClick}\n data-testid=\"perspective-slider-button\"\n {...buttonProps}\n >\n {children}\n </button>\n );\n}\n","import { useRef, useEffect, type RefObject } from \"react\";\nimport {\n createFloatBubble,\n type EmbedConfig,\n type FloatHandle,\n} from \"@perspective-ai/sdk\";\nimport { useStableCallback } from \"./hooks/useStableCallback\";\n\nexport interface FloatBubbleProps extends Omit<EmbedConfig, \"type\"> {\n /** Ref to access the handle for programmatic control */\n embedRef?: RefObject<FloatHandle | null>;\n}\n\n/**\n * Floating bubble widget that expands into a chat window.\n * Renders a floating button in the corner of the screen.\n */\nexport function FloatBubble({\n researchId,\n params,\n brand,\n theme,\n host,\n onReady,\n onSubmit,\n onNavigate,\n onClose,\n onError,\n embedRef,\n}: FloatBubbleProps) {\n const handleRef = useRef<FloatHandle | null>(null);\n\n // Stable callbacks\n const stableOnReady = useStableCallback(onReady);\n const stableOnSubmit = useStableCallback(onSubmit);\n const stableOnNavigate = useStableCallback(onNavigate);\n const stableOnClose = useStableCallback(onClose);\n const stableOnError = useStableCallback(onError);\n\n useEffect(() => {\n const handle = createFloatBubble({\n researchId,\n params,\n brand,\n theme,\n host,\n onReady: stableOnReady,\n onSubmit: stableOnSubmit,\n onNavigate: stableOnNavigate,\n onClose: stableOnClose,\n onError: stableOnError,\n });\n\n handleRef.current = handle;\n\n if (embedRef) {\n embedRef.current = handle;\n }\n\n return () => {\n handle.unmount();\n handleRef.current = null;\n if (embedRef) {\n embedRef.current = null;\n }\n };\n }, [\n researchId,\n params,\n brand,\n theme,\n host,\n stableOnReady,\n stableOnSubmit,\n stableOnNavigate,\n stableOnClose,\n stableOnError,\n embedRef,\n ]);\n\n // This component doesn't render anything - the bubble is added to document.body\n return null;\n}\n","import { useRef, useEffect, type RefObject } from \"react\";\nimport {\n createFullpage,\n type EmbedConfig,\n type EmbedHandle,\n} from \"@perspective-ai/sdk\";\nimport { useStableCallback } from \"./hooks/useStableCallback\";\n\nexport interface FullpageProps extends Omit<EmbedConfig, \"type\"> {\n /** Ref to access the embed handle for programmatic control */\n embedRef?: RefObject<EmbedHandle | null>;\n}\n\n/**\n * Full viewport embed component.\n * Takes over the entire screen with the interview.\n */\nexport function Fullpage({\n researchId,\n params,\n brand,\n theme,\n host,\n onReady,\n onSubmit,\n onNavigate,\n onClose,\n onError,\n embedRef,\n}: FullpageProps) {\n const handleRef = useRef<EmbedHandle | null>(null);\n\n // Stable callbacks\n const stableOnReady = useStableCallback(onReady);\n const stableOnSubmit = useStableCallback(onSubmit);\n const stableOnNavigate = useStableCallback(onNavigate);\n const stableOnClose = useStableCallback(onClose);\n const stableOnError = useStableCallback(onError);\n\n useEffect(() => {\n const handle = createFullpage({\n researchId,\n params,\n brand,\n theme,\n host,\n onReady: stableOnReady,\n onSubmit: stableOnSubmit,\n onNavigate: stableOnNavigate,\n onClose: stableOnClose,\n onError: stableOnError,\n });\n\n handleRef.current = handle;\n\n if (embedRef) {\n embedRef.current = handle;\n }\n\n return () => {\n handle.unmount();\n handleRef.current = null;\n if (embedRef) {\n embedRef.current = null;\n }\n };\n }, [\n researchId,\n params,\n brand,\n theme,\n host,\n stableOnReady,\n stableOnSubmit,\n stableOnNavigate,\n stableOnClose,\n stableOnError,\n embedRef,\n ]);\n\n // This component doesn't render anything - the fullpage overlay is added to document.body\n return null;\n}\n","import { useState, useEffect } from \"react\";\n\ntype Theme = \"light\" | \"dark\";\ntype ThemeInput = \"light\" | \"dark\" | \"system\";\n\n/**\n * Hook to resolve theme based on override and system preference.\n * Listens for system preference changes when theme is \"system\".\n */\nexport function useThemeSync(theme: ThemeInput = \"system\"): Theme {\n // Always start with a deterministic value for SSR hydration safety.\n // The actual system preference is synced in useEffect.\n const [resolved, setResolved] = useState<Theme>(\n theme !== \"system\" ? theme : \"light\"\n );\n\n useEffect(() => {\n if (theme !== \"system\") {\n setResolved(theme);\n return;\n }\n\n const mq = window.matchMedia(\"(prefers-color-scheme: dark)\");\n\n // Set initial value\n setResolved(mq.matches ? \"dark\" : \"light\");\n\n const handler = (e: MediaQueryListEvent) =>\n setResolved(e.matches ? \"dark\" : \"light\");\n\n mq.addEventListener(\"change\", handler);\n return () => mq.removeEventListener(\"change\", handler);\n }, [theme]);\n\n return resolved;\n}\n"]}
@@ -0,0 +1,96 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { HTMLAttributes, RefObject, ButtonHTMLAttributes, ReactNode } from 'react';
3
+ import { EmbedConfig, EmbedHandle, FloatHandle } from '@perspective-ai/sdk';
4
+ export { BrandColors, EmbedConfig, EmbedError, EmbedHandle, FloatHandle, ThemeValue } from '@perspective-ai/sdk';
5
+
6
+ interface WidgetProps extends Omit<EmbedConfig, "type">, Omit<HTMLAttributes<HTMLDivElement>, "onError" | "onSubmit"> {
7
+ /** Ref to access the embed handle for programmatic control */
8
+ embedRef?: RefObject<EmbedHandle | null>;
9
+ }
10
+ /**
11
+ * Inline widget embed component.
12
+ * Renders the interview directly in a container.
13
+ */
14
+ declare function Widget({ researchId, params, brand, theme, host, onReady, onSubmit, onNavigate, onClose, onError, embedRef, className, style, ...divProps }: WidgetProps): react_jsx_runtime.JSX.Element;
15
+
16
+ /** Handle for programmatic control of popup button */
17
+ interface PopupButtonHandle {
18
+ open: () => void;
19
+ close: () => void;
20
+ toggle: () => void;
21
+ unmount: () => void;
22
+ readonly isOpen: boolean;
23
+ readonly researchId: string;
24
+ }
25
+ interface PopupButtonProps extends Omit<EmbedConfig, "type">, Omit<ButtonHTMLAttributes<HTMLButtonElement>, "onError" | "onSubmit"> {
26
+ /** Button content */
27
+ children: ReactNode;
28
+ /** Controlled open state */
29
+ open?: boolean;
30
+ /** Callback when open state changes */
31
+ onOpenChange?: (open: boolean) => void;
32
+ /** Ref to access the handle for programmatic control */
33
+ embedRef?: RefObject<PopupButtonHandle | null>;
34
+ }
35
+ /**
36
+ * Button that opens a popup modal when clicked.
37
+ * Supports both controlled and uncontrolled modes.
38
+ */
39
+ declare function PopupButton({ researchId, params, brand, theme, host, onReady, onSubmit, onNavigate, onClose, onError, children, open, onOpenChange, embedRef, onClick, ...buttonProps }: PopupButtonProps): react_jsx_runtime.JSX.Element;
40
+
41
+ /** Handle for programmatic control of slider button */
42
+ interface SliderButtonHandle {
43
+ open: () => void;
44
+ close: () => void;
45
+ toggle: () => void;
46
+ unmount: () => void;
47
+ readonly isOpen: boolean;
48
+ readonly researchId: string;
49
+ }
50
+ interface SliderButtonProps extends Omit<EmbedConfig, "type">, Omit<ButtonHTMLAttributes<HTMLButtonElement>, "onError" | "onSubmit"> {
51
+ /** Button content */
52
+ children: ReactNode;
53
+ /** Controlled open state */
54
+ open?: boolean;
55
+ /** Callback when open state changes */
56
+ onOpenChange?: (open: boolean) => void;
57
+ /** Ref to access the handle for programmatic control */
58
+ embedRef?: RefObject<SliderButtonHandle | null>;
59
+ }
60
+ /**
61
+ * Button that opens a slider panel when clicked.
62
+ * Supports both controlled and uncontrolled modes.
63
+ */
64
+ declare function SliderButton({ researchId, params, brand, theme, host, onReady, onSubmit, onNavigate, onClose, onError, children, open, onOpenChange, embedRef, onClick, ...buttonProps }: SliderButtonProps): react_jsx_runtime.JSX.Element;
65
+
66
+ interface FloatBubbleProps extends Omit<EmbedConfig, "type"> {
67
+ /** Ref to access the handle for programmatic control */
68
+ embedRef?: RefObject<FloatHandle | null>;
69
+ }
70
+ /**
71
+ * Floating bubble widget that expands into a chat window.
72
+ * Renders a floating button in the corner of the screen.
73
+ */
74
+ declare function FloatBubble({ researchId, params, brand, theme, host, onReady, onSubmit, onNavigate, onClose, onError, embedRef, }: FloatBubbleProps): null;
75
+
76
+ interface FullpageProps extends Omit<EmbedConfig, "type"> {
77
+ /** Ref to access the embed handle for programmatic control */
78
+ embedRef?: RefObject<EmbedHandle | null>;
79
+ }
80
+ /**
81
+ * Full viewport embed component.
82
+ * Takes over the entire screen with the interview.
83
+ */
84
+ declare function Fullpage({ researchId, params, brand, theme, host, onReady, onSubmit, onNavigate, onClose, onError, embedRef, }: FullpageProps): null;
85
+
86
+ type Theme = "light" | "dark";
87
+ type ThemeInput = "light" | "dark" | "system";
88
+ /**
89
+ * Hook to resolve theme based on override and system preference.
90
+ * Listens for system preference changes when theme is "system".
91
+ */
92
+ declare function useThemeSync(theme?: ThemeInput): Theme;
93
+
94
+ declare function useStableCallback<T extends (...args: any[]) => any>(callback: T | undefined): T;
95
+
96
+ export { FloatBubble, type FloatBubbleProps, Fullpage, type FullpageProps, PopupButton, type PopupButtonHandle, type PopupButtonProps, SliderButton, type SliderButtonHandle, type SliderButtonProps, Widget, type WidgetProps, useStableCallback, useThemeSync };