@sunboard/react 0.2.0 → 0.3.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/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { ReactNode } from "react";
2
2
  import { RuntimeBootstrapRequest, RuntimeBootstrapResponse, RuntimeExperience, RuntimeProgress, RuntimeTrackRequest, RuntimeTrackResponse, SunboardRuntimeApiError, SunboardRuntimeClient, SunboardRuntimeClientOptions } from "@sunboard/core/runtime-api";
3
- import * as react_jsx_runtime1 from "react/jsx-runtime";
3
+ import * as react_jsx_runtime0 from "react/jsx-runtime";
4
4
  import { NormalizedSunboardSpec, SunboardHotspot, SunboardStep, SunboardTourStep, SurveyQuestionType, TrackEventName } from "@sunboard/core";
5
5
 
6
6
  //#region src/adapters/navigation.d.ts
@@ -32,14 +32,6 @@ type TourResult = {
32
32
  ok: false;
33
33
  reason: "not-ready" | "missing-user" | "missing-tour";
34
34
  };
35
- type SunboardRuntimeMode = "production" | "preview";
36
- type SunboardTourError = {
37
- message: string;
38
- route?: string;
39
- stepKey?: string;
40
- target?: string;
41
- tourKey: string;
42
- };
43
35
  type ActiveSunboardTour = {
44
36
  key: string;
45
37
  name: string;
@@ -52,13 +44,11 @@ type SunboardContextValue = {
52
44
  user?: SunboardUser | null;
53
45
  loading: boolean;
54
46
  route?: string;
55
- mode: SunboardRuntimeMode;
56
47
  completedStepKeys: ReadonlySet<string>;
57
48
  completedTourKeys: ReadonlySet<string>;
58
49
  completedCount: number;
59
50
  totalSteps: number;
60
51
  activeTour: ActiveSunboardTour | null;
61
- tourError: SunboardTourError | null;
62
52
  track: (eventName: TrackEventName, properties?: TrackProperties) => Promise<TrackResult>;
63
53
  /**
64
54
  * Submit survey answers for the active survey experience. On success the
@@ -79,7 +69,6 @@ type SunboardContextValue = {
79
69
  advanceTour: () => Promise<TourResult>;
80
70
  navigateToStep: (step: SunboardStep) => Promise<void>;
81
71
  startChecklistStep: (step: SunboardStep) => Promise<TourResult | void>;
82
- reportTourError: (error: SunboardTourError | null) => void;
83
72
  visibleHotspots: Array<{
84
73
  key: string;
85
74
  hotspot: SunboardHotspot;
@@ -91,7 +80,6 @@ type SunboardProviderProps = {
91
80
  spec?: NormalizedSunboardSpec | null;
92
81
  user?: SunboardUser | null;
93
82
  loading?: boolean;
94
- mode?: SunboardRuntimeMode;
95
83
  route?: string;
96
84
  navigate?: NavigateCallback;
97
85
  /**
@@ -101,7 +89,6 @@ type SunboardProviderProps = {
101
89
  */
102
90
  apiUrl?: string;
103
91
  publishableKey?: string;
104
- environment?: string;
105
92
  onTrack?: (eventName: TrackEventName, properties?: TrackProperties) => void | Promise<void>;
106
93
  children: ReactNode;
107
94
  };
@@ -109,25 +96,23 @@ declare function SunboardProvider({
109
96
  spec: specProp,
110
97
  user,
111
98
  loading: loadingProp,
112
- mode: modeProp,
113
99
  route,
114
100
  navigate,
115
101
  apiUrl,
116
102
  publishableKey,
117
- environment,
118
103
  onTrack,
119
104
  children
120
- }: SunboardProviderProps): react_jsx_runtime1.JSX.Element;
105
+ }: SunboardProviderProps): react_jsx_runtime0.JSX.Element;
121
106
  //#endregion
122
107
  //#region src/components/SunboardHotspots.d.ts
123
- declare function SunboardHotspots(): react_jsx_runtime1.JSX.Element;
108
+ declare function SunboardHotspots(): react_jsx_runtime0.JSX.Element;
124
109
  //#endregion
125
110
  //#region src/components/SunboardTour.d.ts
126
- declare function SunboardTour(): react_jsx_runtime1.JSX.Element | null;
111
+ declare function SunboardTour(): react_jsx_runtime0.JSX.Element | null;
127
112
  //#endregion
128
113
  //#region src/components/Sunboard.d.ts
129
- declare function SunboardChecklist(): react_jsx_runtime1.JSX.Element | null;
130
- declare function SunboardSurvey(): react_jsx_runtime1.JSX.Element | null;
114
+ declare function SunboardChecklist(): react_jsx_runtime0.JSX.Element | null;
115
+ declare function SunboardSurvey(): react_jsx_runtime0.JSX.Element | null;
131
116
  declare const Sunboard: typeof SunboardChecklist & {
132
117
  Checklist: typeof SunboardChecklist;
133
118
  Survey: typeof SunboardSurvey;
@@ -135,15 +120,6 @@ declare const Sunboard: typeof SunboardChecklist & {
135
120
  Tour: typeof SunboardTour;
136
121
  };
137
122
  //#endregion
138
- //#region src/hooks/useSunboardPreviewSpec.d.ts
139
- type SunboardPreviewSpecState = {
140
- error: Error | null;
141
- loading: boolean;
142
- source: "none" | "preview";
143
- spec: NormalizedSunboardSpec | null;
144
- };
145
- declare function useSunboardPreviewSpec(): SunboardPreviewSpecState;
146
- //#endregion
147
123
  //#region src/hooks/useSunboard.d.ts
148
124
  declare function useSunboard(): SunboardContextValue;
149
125
  //#endregion
@@ -228,5 +204,5 @@ type UseSurveyResult = {
228
204
  };
229
205
  declare function useSurvey(): UseSurveyResult;
230
206
  //#endregion
231
- export { type ChecklistViewStep, type RuntimeBootstrapRequest, type RuntimeBootstrapResponse, type RuntimeExperience, type RuntimeProgress, type RuntimeTrackRequest, type RuntimeTrackResponse, Sunboard, SunboardChecklist, type SunboardContextValue, SunboardHotspots, type SunboardPreviewSpecState, SunboardProvider, type SunboardProviderProps, SunboardRuntimeApiError, SunboardRuntimeClient, type SunboardRuntimeClientOptions, type SunboardRuntimeMode, SunboardSurvey, SunboardTour, type SunboardTourError, type SunboardUser, type SurveyResult, type SurveyViewOption, type SurveyViewQuestion, type SurveyViewStep, type TourResult, type TrackProperties, type TrackResult, type UseChecklistResult, type UseSurveyResult, useChecklist, useSunboard, useSunboardPreviewSpec, useSurvey };
207
+ export { type ChecklistViewStep, type RuntimeBootstrapRequest, type RuntimeBootstrapResponse, type RuntimeExperience, type RuntimeProgress, type RuntimeTrackRequest, type RuntimeTrackResponse, Sunboard, SunboardChecklist, type SunboardContextValue, SunboardHotspots, SunboardProvider, type SunboardProviderProps, SunboardRuntimeApiError, SunboardRuntimeClient, type SunboardRuntimeClientOptions, SunboardSurvey, SunboardTour, type SunboardUser, type SurveyResult, type SurveyViewOption, type SurveyViewQuestion, type SurveyViewStep, type TourResult, type TrackProperties, type TrackResult, type UseChecklistResult, type UseSurveyResult, useChecklist, useSunboard, useSurvey };
232
208
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/adapters/navigation.ts","../src/hooks/sunboard-context.ts","../src/components/SunboardProvider.tsx","../src/components/SunboardHotspots.tsx","../src/components/SunboardTour.tsx","../src/components/Sunboard.tsx","../src/hooks/useSunboardPreviewSpec.ts","../src/hooks/useSunboard.ts","../src/hooks/useChecklist.ts","../src/hooks/useSurvey.ts"],"sourcesContent":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/adapters/navigation.ts","../src/hooks/sunboard-context.ts","../src/components/SunboardProvider.tsx","../src/components/SunboardHotspots.tsx","../src/components/SunboardTour.tsx","../src/components/Sunboard.tsx","../src/hooks/useSunboard.ts","../src/hooks/useChecklist.ts","../src/hooks/useSurvey.ts"],"sourcesContent":[],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -18,151 +18,20 @@ async function navigateWithFallback(url, navigate) {
18
18
  //#region src/hooks/sunboard-context.ts
19
19
  const SunboardContext = createContext(null);
20
20
 
21
- //#endregion
22
- //#region src/hooks/useSunboardPreviewSpec.ts
23
- function useSunboardPreviewSpec() {
24
- const [state, setState] = useState(() => ({
25
- error: null,
26
- loading: getPreviewSpecUrl() !== null,
27
- source: "none",
28
- spec: null
29
- }));
30
- useEffect(() => {
31
- const previewUrl = getPreviewSpecUrl();
32
- if (!previewUrl) {
33
- setState({
34
- error: null,
35
- loading: false,
36
- source: "none",
37
- spec: null
38
- });
39
- return;
40
- }
41
- const controller = new AbortController();
42
- let cancelled = false;
43
- let eventSource = null;
44
- const loadSpec = async (signal) => {
45
- const response = await fetch(previewUrl.toString(), { signal });
46
- if (!response.ok) throw new Error(`Sunboard preview failed with ${response.status}.`);
47
- const json = await response.json();
48
- if (!isSunboardSpecLike(json)) throw new Error("Sunboard preview returned an invalid spec payload.");
49
- return json;
50
- };
51
- setState({
52
- error: null,
53
- loading: true,
54
- source: "none",
55
- spec: null
56
- });
57
- loadSpec(controller.signal).then((previewSpec) => {
58
- if (cancelled) return;
59
- setState({
60
- error: null,
61
- loading: false,
62
- source: "preview",
63
- spec: previewSpec
64
- });
65
- const eventsUrl = buildEventsUrl(previewUrl);
66
- if (eventsUrl && typeof EventSource !== "undefined") {
67
- eventSource = new EventSource(eventsUrl);
68
- let firstMessage = true;
69
- eventSource.onmessage = () => {
70
- if (firstMessage) {
71
- firstMessage = false;
72
- return;
73
- }
74
- reloadSpec(previewUrl);
75
- };
76
- eventSource.onerror = () => {};
77
- }
78
- }).catch((error) => {
79
- if (controller.signal.aborted || cancelled) return;
80
- setState({
81
- error: error instanceof Error ? error : new Error(String(error)),
82
- loading: false,
83
- source: "none",
84
- spec: null
85
- });
86
- });
87
- const reloadSpec = async (url) => {
88
- try {
89
- const response = await fetch(url.toString());
90
- if (!response.ok) throw new Error(`Sunboard preview failed with ${response.status}.`);
91
- const json = await response.json();
92
- if (!isSunboardSpecLike(json)) throw new Error("Sunboard preview returned an invalid spec payload.");
93
- if (cancelled) return;
94
- setState({
95
- error: null,
96
- loading: false,
97
- source: "preview",
98
- spec: json
99
- });
100
- } catch (error) {
101
- if (cancelled) return;
102
- setState((current) => ({
103
- ...current,
104
- error: error instanceof Error ? error : new Error(String(error))
105
- }));
106
- }
107
- };
108
- return () => {
109
- cancelled = true;
110
- controller.abort();
111
- eventSource?.close();
112
- };
113
- }, []);
114
- return state;
115
- }
116
- function buildEventsUrl(specUrl) {
117
- try {
118
- const eventsUrl = new URL(specUrl.toString());
119
- eventsUrl.pathname = eventsUrl.pathname.replace(/\/spec\.json$/, "/events");
120
- if (eventsUrl.pathname === specUrl.pathname) return null;
121
- return eventsUrl.toString();
122
- } catch {
123
- return null;
124
- }
125
- }
126
- function getPreviewSpecUrl() {
127
- if (typeof window === "undefined") return null;
128
- const pageUrl = new URL(window.location.href);
129
- if (pageUrl.searchParams.get("sunboard_preview") !== "1") return null;
130
- const previewUrlParam = pageUrl.searchParams.get("sunboard_preview_url");
131
- if (!previewUrlParam) return null;
132
- try {
133
- const previewUrl = new URL(previewUrlParam);
134
- return isAllowedLocalPreviewUrl(previewUrl) ? previewUrl : null;
135
- } catch {
136
- return null;
137
- }
138
- }
139
- function isAllowedLocalPreviewUrl(url) {
140
- return url.protocol === "http:" && (url.hostname === "localhost" || url.hostname === "127.0.0.1");
141
- }
142
- function isSunboardSpecLike(value) {
143
- if (!value || typeof value !== "object") return false;
144
- const candidate = value;
145
- if (typeof candidate.key !== "string" || typeof candidate.name !== "string" || typeof candidate.type !== "string") return false;
146
- const hasChecklist = typeof candidate.checklist === "object" && Array.isArray(candidate.checklist?.steps);
147
- const hasSurvey = typeof candidate.survey === "object" && Array.isArray(candidate.survey?.steps);
148
- return hasChecklist || hasSurvey;
149
- }
150
-
151
21
  //#endregion
152
22
  //#region src/version.ts
153
23
  const SDK_VERSION = "0.0.0";
154
24
 
155
25
  //#endregion
156
26
  //#region src/components/SunboardProvider.tsx
157
- function SunboardProvider({ spec: specProp, user, loading: loadingProp, mode: modeProp, route, navigate, apiUrl, publishableKey, environment = "production", onTrack, children }) {
27
+ function SunboardProvider({ spec: specProp, user, loading: loadingProp, route, navigate, apiUrl, publishableKey, onTrack, children }) {
158
28
  const currentRoute = route ?? getBrowserRoute();
159
29
  const currentOrigin = getBrowserOrigin();
160
- const preview = useSunboardPreviewSpec();
161
30
  const client = useMemo(() => {
162
31
  if (!publishableKey) return null;
163
32
  return new SunboardRuntimeClient$1({
33
+ apiKey: publishableKey,
164
34
  apiUrl: apiUrl || DEFAULT_RUNTIME_API_URL,
165
- publishableKey,
166
35
  sdkVersion: SDK_VERSION
167
36
  });
168
37
  }, [apiUrl, publishableKey]);
@@ -171,8 +40,7 @@ function SunboardProvider({ spec: specProp, user, loading: loadingProp, mode: mo
171
40
  setBootstrapNonce((nonce) => nonce + 1);
172
41
  }, []);
173
42
  const bootstrap = useSunboardBootstrap(client, {
174
- enabled: !specProp && preview.source !== "preview" && !preview.loading,
175
- environment,
43
+ enabled: !specProp,
176
44
  nonce: bootstrapNonce,
177
45
  origin: currentOrigin,
178
46
  route: currentRoute,
@@ -181,10 +49,9 @@ function SunboardProvider({ spec: specProp, user, loading: loadingProp, mode: mo
181
49
  const activeExperience = bootstrap.experience;
182
50
  const activeSurvey = bootstrap.survey;
183
51
  const hostedSpec = activeSurvey?.spec ?? activeExperience?.spec ?? null;
184
- const resolvedSpec = specProp ?? preview.spec ?? hostedSpec;
185
- const resolvedLoading = loadingProp ?? (preview.loading || bootstrap.loading);
186
- const resolvedMode = modeProp ?? (preview.source === "preview" ? "preview" : "production");
187
- const hostedMode = client !== null && !specProp && preview.source !== "preview";
52
+ const resolvedSpec = specProp ?? hostedSpec;
53
+ const resolvedLoading = loadingProp ?? bootstrap.loading;
54
+ const hostedMode = client !== null && !specProp;
188
55
  const ownedAttributes = bootstrap.attributes;
189
56
  const effectiveUser = useMemo(() => {
190
57
  if (!user || !ownedAttributes || Object.keys(ownedAttributes).length === 0) return user;
@@ -199,7 +66,6 @@ function SunboardProvider({ spec: specProp, user, loading: loadingProp, mode: mo
199
66
  const [completedStepKeys, setCompletedStepKeys] = useState(() => /* @__PURE__ */ new Set());
200
67
  const [completedTourKeys, setCompletedTourKeys] = useState(() => /* @__PURE__ */ new Set());
201
68
  const [activeTourState, setActiveTourState] = useState(null);
202
- const [tourError, setTourError] = useState(null);
203
69
  const lastNavigatedTourStep = useRef(null);
204
70
  const storageKey = !hostedMode && resolvedSpec && user ? completedToursStorageKey(resolvedSpec.key, user.id) : null;
205
71
  const experienceKey = activeSurvey?.key ?? activeExperience?.key ?? resolvedSpec?.key ?? null;
@@ -207,7 +73,6 @@ function SunboardProvider({ spec: specProp, user, loading: loadingProp, mode: mo
207
73
  useEffect(() => {
208
74
  setCompletedStepKeys(/* @__PURE__ */ new Set());
209
75
  setActiveTourState(null);
210
- setTourError(null);
211
76
  lastNavigatedTourStep.current = null;
212
77
  if (!storageKey) {
213
78
  setCompletedTourKeys(/* @__PURE__ */ new Set());
@@ -272,7 +137,6 @@ function SunboardProvider({ spec: specProp, user, loading: loadingProp, mode: mo
272
137
  useEffect(() => {
273
138
  if (!hostedMode || !client || !activeTour || !user || !experienceKey) return;
274
139
  client.recordTourStep({
275
- environment,
276
140
  experienceKey,
277
141
  origin: currentOrigin,
278
142
  route: currentRoute,
@@ -313,8 +177,7 @@ function SunboardProvider({ spec: specProp, user, loading: loadingProp, mode: mo
313
177
  if (matchingStepKeys.length > 0) setCompletedStepKeys((current) => mergeKeys(current, matchingStepKeys));
314
178
  let networkOk = true;
315
179
  if (hostedMode && client && experienceKey) try {
316
- applyProgress((await client.track({
317
- environment,
180
+ const response = await client.track({
318
181
  eventName,
319
182
  experienceKey,
320
183
  origin: currentOrigin,
@@ -322,7 +185,8 @@ function SunboardProvider({ spec: specProp, user, loading: loadingProp, mode: mo
322
185
  route: currentRoute,
323
186
  user: toRuntimeUser(user),
324
187
  versionId: versionId ?? void 0
325
- })).progress, setCompletedStepKeys, setCompletedTourKeys);
188
+ });
189
+ if (response.progress) applyProgress(response.progress, setCompletedStepKeys, setCompletedTourKeys);
326
190
  } catch {
327
191
  networkOk = false;
328
192
  }
@@ -343,7 +207,6 @@ function SunboardProvider({ spec: specProp, user, loading: loadingProp, mode: mo
343
207
  client,
344
208
  currentRoute,
345
209
  currentOrigin,
346
- environment,
347
210
  experienceKey,
348
211
  hostedMode,
349
212
  onTrack,
@@ -365,7 +228,6 @@ function SunboardProvider({ spec: specProp, user, loading: loadingProp, mode: mo
365
228
  ok: false,
366
229
  reason: "not-survey"
367
230
  };
368
- if (resolvedMode === "preview") return { ok: true };
369
231
  if (!hostedMode || !client || !experienceKey) return {
370
232
  ok: false,
371
233
  reason: "not-ready"
@@ -373,7 +235,6 @@ function SunboardProvider({ spec: specProp, user, loading: loadingProp, mode: mo
373
235
  try {
374
236
  await client.submitSurvey({
375
237
  answers,
376
- environment,
377
238
  experienceKey,
378
239
  origin: currentOrigin,
379
240
  route: currentRoute,
@@ -392,12 +253,10 @@ function SunboardProvider({ spec: specProp, user, loading: loadingProp, mode: mo
392
253
  client,
393
254
  currentOrigin,
394
255
  currentRoute,
395
- environment,
396
256
  experienceKey,
397
257
  hostedMode,
398
258
  refreshBootstrap,
399
259
  resolvedLoading,
400
- resolvedMode,
401
260
  resolvedSpec,
402
261
  user,
403
262
  versionId
@@ -419,14 +278,12 @@ function SunboardProvider({ spec: specProp, user, loading: loadingProp, mode: mo
419
278
  ok: false,
420
279
  reason: "missing-tour"
421
280
  };
422
- setTourError(null);
423
281
  lastNavigatedTourStep.current = null;
424
282
  setActiveTourState({
425
283
  key: tourKey,
426
284
  stepIndex: 0
427
285
  });
428
286
  if (hostedMode && client && experienceKey && user) client.startTour({
429
- environment,
430
287
  experienceKey,
431
288
  origin: currentOrigin,
432
289
  route: currentRoute,
@@ -439,7 +296,6 @@ function SunboardProvider({ spec: specProp, user, loading: loadingProp, mode: mo
439
296
  client,
440
297
  currentRoute,
441
298
  currentOrigin,
442
- environment,
443
299
  experienceKey,
444
300
  hostedMode,
445
301
  resolvedLoading,
@@ -451,10 +307,8 @@ function SunboardProvider({ spec: specProp, user, loading: loadingProp, mode: mo
451
307
  const dismissed = activeTourState;
452
308
  const dismissedStepKey = dismissed ? resolvedSpec?.tours[dismissed.key]?.steps[dismissed.stepIndex]?.key : void 0;
453
309
  setActiveTourState(null);
454
- setTourError(null);
455
310
  lastNavigatedTourStep.current = null;
456
311
  if (!options?.silent && hostedMode && client && experienceKey && user && dismissed && dismissedStepKey) client.dismissTour({
457
- environment,
458
312
  experienceKey,
459
313
  origin: currentOrigin,
460
314
  route: currentRoute,
@@ -469,7 +323,6 @@ function SunboardProvider({ spec: specProp, user, loading: loadingProp, mode: mo
469
323
  client,
470
324
  currentOrigin,
471
325
  currentRoute,
472
- environment,
473
326
  experienceKey,
474
327
  hostedMode,
475
328
  resolvedSpec,
@@ -481,7 +334,6 @@ function SunboardProvider({ spec: specProp, user, loading: loadingProp, mode: mo
481
334
  if (!hostedMode || !client || !experienceKey || !user) return;
482
335
  try {
483
336
  applyProgress((await client.completeTour({
484
- environment,
485
337
  experienceKey,
486
338
  origin: currentOrigin,
487
339
  route: currentRoute,
@@ -495,7 +347,6 @@ function SunboardProvider({ spec: specProp, user, loading: loadingProp, mode: mo
495
347
  completeTour,
496
348
  currentRoute,
497
349
  currentOrigin,
498
- environment,
499
350
  experienceKey,
500
351
  hostedMode,
501
352
  user,
@@ -515,7 +366,6 @@ function SunboardProvider({ spec: specProp, user, loading: loadingProp, mode: mo
515
366
  ok: false,
516
367
  reason: "missing-tour"
517
368
  };
518
- setTourError(null);
519
369
  lastNavigatedTourStep.current = null;
520
370
  if (activeTourState.stepIndex >= tour.steps.length - 1) {
521
371
  const completedKey = activeTourState.key;
@@ -551,21 +401,16 @@ function SunboardProvider({ spec: specProp, user, loading: loadingProp, mode: mo
551
401
  currentRoute,
552
402
  resolvedSpec
553
403
  ]);
554
- const reportTourError = useCallback((error) => {
555
- setTourError(error);
556
- }, []);
557
404
  const value = useMemo(() => ({
558
405
  spec: resolvedSpec,
559
406
  user: effectiveUser,
560
407
  loading: resolvedLoading,
561
- mode: resolvedMode,
562
408
  route: currentRoute,
563
409
  completedStepKeys: effectiveCompletedStepKeys,
564
410
  completedTourKeys: effectiveCompletedTourKeys,
565
411
  completedCount,
566
412
  totalSteps,
567
413
  activeTour,
568
- tourError,
569
414
  track,
570
415
  submitSurvey,
571
416
  startTour,
@@ -573,20 +418,17 @@ function SunboardProvider({ spec: specProp, user, loading: loadingProp, mode: mo
573
418
  advanceTour,
574
419
  navigateToStep,
575
420
  startChecklistStep,
576
- reportTourError,
577
421
  visibleHotspots
578
422
  }), [
579
423
  resolvedSpec,
580
424
  effectiveUser,
581
425
  resolvedLoading,
582
- resolvedMode,
583
426
  currentRoute,
584
427
  effectiveCompletedStepKeys,
585
428
  effectiveCompletedTourKeys,
586
429
  completedCount,
587
430
  totalSteps,
588
431
  activeTour,
589
- tourError,
590
432
  track,
591
433
  submitSurvey,
592
434
  startTour,
@@ -594,7 +436,6 @@ function SunboardProvider({ spec: specProp, user, loading: loadingProp, mode: mo
594
436
  advanceTour,
595
437
  navigateToStep,
596
438
  startChecklistStep,
597
- reportTourError,
598
439
  visibleHotspots
599
440
  ]);
600
441
  return /* @__PURE__ */ jsx(SunboardContext.Provider, {
@@ -603,7 +444,7 @@ function SunboardProvider({ spec: specProp, user, loading: loadingProp, mode: mo
603
444
  });
604
445
  }
605
446
  function useSunboardBootstrap(client, options) {
606
- const { enabled, environment, nonce, origin, route, user } = options;
447
+ const { enabled, nonce, origin, route, user } = options;
607
448
  const [state, setState] = useState({
608
449
  attributes: null,
609
450
  experience: null,
@@ -612,7 +453,7 @@ function useSunboardBootstrap(client, options) {
612
453
  });
613
454
  const warnedSdkAdvisory = useRef(false);
614
455
  useEffect(() => {
615
- if (!client || !enabled || !user?.id || isPreviewPage()) {
456
+ if (!client || !enabled || !user?.id) {
616
457
  setState({
617
458
  attributes: null,
618
459
  experience: null,
@@ -629,13 +470,12 @@ function useSunboardBootstrap(client, options) {
629
470
  survey: null
630
471
  });
631
472
  client.bootstrap({
632
- environment,
633
473
  origin,
634
474
  route,
635
475
  user: toRuntimeUser(user)
636
476
  }).then((response) => {
637
477
  if (controller.signal.aborted) return;
638
- if (response.experiences.length === 0 && !response.survey && isDevEnvironment()) console.warn(`[sunboard] bootstrap returned no active experiences for route "${route}" in environment "${environment}". Check the dashboard for an enabled deployment whose trigger.route matches.`);
478
+ if (response.experiences.length === 0 && !response.survey && isDevEnvironment()) console.warn(`[sunboard] bootstrap returned no active experiences for route "${route}". Check the dashboard for an enabled deployment whose trigger.route matches (the publishable key selects the environment).`);
639
479
  if (response.sdkAdvisory && isDevEnvironment() && !warnedSdkAdvisory.current) {
640
480
  warnedSdkAdvisory.current = true;
641
481
  console.warn(`[sunboard] ${response.sdkAdvisory.message}`);
@@ -662,7 +502,6 @@ function useSunboardBootstrap(client, options) {
662
502
  }, [
663
503
  client,
664
504
  enabled,
665
- environment,
666
505
  nonce,
667
506
  origin,
668
507
  user?.id
@@ -684,10 +523,6 @@ function isDevEnvironment() {
684
523
  if (typeof process === "undefined" || !process.env) return false;
685
524
  return process.env.NODE_ENV !== "production";
686
525
  }
687
- function isPreviewPage() {
688
- if (typeof window === "undefined") return false;
689
- return new URLSearchParams(window.location.search).get("sunboard_preview") === "1";
690
- }
691
526
  function toRuntimeUser(user) {
692
527
  return {
693
528
  email: user.email ?? void 0,
@@ -772,7 +607,6 @@ function useSunboard() {
772
607
 
773
608
  //#endregion
774
609
  //#region src/hooks/useChecklist.ts
775
- const SUPPORTED_CHECKLIST_TYPES = new Set(["checklist", "hybrid"]);
776
610
  function actionLabelFor(step) {
777
611
  if (step.action?.type === "startTour") return "Start";
778
612
  if (step.action || step.route) return "Open";
@@ -784,7 +618,7 @@ function interpolateOptional$1(text, user) {
784
618
  function useChecklist() {
785
619
  const { spec, user, loading, completedStepKeys, completedCount, totalSteps, startChecklistStep } = useSunboard();
786
620
  const [dismissedKey, setDismissedKey] = useState(null);
787
- const hasChecklist = Boolean(spec && SUPPORTED_CHECKLIST_TYPES.has(spec.type));
621
+ const hasChecklist = Boolean(spec && spec.checklist?.steps.length);
788
622
  const allCompleted = totalSteps > 0 && completedCount >= totalSteps;
789
623
  const ready = !loading && !!user && !!spec && hasChecklist;
790
624
  const specKey = spec?.key ?? null;
@@ -1153,7 +987,7 @@ function Hotspot({ hotspot, hotspotKey }) {
1153
987
  //#endregion
1154
988
  //#region src/components/SunboardTour.tsx
1155
989
  function SunboardTour() {
1156
- const { activeTour, advanceTour, dismissTour, mode, reportTourError, route, tourError } = useSunboard();
990
+ const { activeTour, advanceTour, dismissTour, route } = useSunboard();
1157
991
  const arrowRef = useRef(null);
1158
992
  const [spotlightRect, setSpotlightRect] = useState(null);
1159
993
  const routeReady = routeMatches(activeTour?.step.route, route ?? "/");
@@ -1168,36 +1002,16 @@ function SunboardTour() {
1168
1002
  ]
1169
1003
  });
1170
1004
  useEffect(() => {
1171
- if (!activeTour) {
1172
- reportTourError(null);
1173
- return;
1174
- }
1175
- if (!routeReady) return;
1176
- if (!target.timedOut) return;
1177
- const error = {
1178
- message: "Sunboard could not find the tour target.",
1179
- route: activeTour.step.route,
1180
- stepKey: activeTour.step.key,
1181
- target: activeTour.step.target,
1182
- tourKey: activeTour.key
1183
- };
1184
- if (mode === "preview") {
1185
- reportTourError(error);
1186
- return;
1187
- }
1188
- reportTourError(null);
1005
+ if (!activeTour || !routeReady || !target.timedOut) return;
1189
1006
  dismissTour({ silent: true });
1190
1007
  }, [
1191
1008
  activeTour,
1192
1009
  dismissTour,
1193
- mode,
1194
- reportTourError,
1195
1010
  routeReady,
1196
1011
  target.timedOut
1197
1012
  ]);
1198
1013
  useEffect(() => {
1199
1014
  if (!activeTour || !target.element) return;
1200
- reportTourError(null);
1201
1015
  refs.setReference(target.element);
1202
1016
  target.element.scrollIntoView({
1203
1017
  block: "center",
@@ -1207,7 +1021,6 @@ function SunboardTour() {
1207
1021
  }, [
1208
1022
  activeTour,
1209
1023
  refs,
1210
- reportTourError,
1211
1024
  target.element,
1212
1025
  update
1213
1026
  ]);
@@ -1250,22 +1063,6 @@ function SunboardTour() {
1250
1063
  const stepTitle = useInterpolated(activeTour?.step.title);
1251
1064
  const stepBody = useInterpolated(activeTour?.step.body);
1252
1065
  if (!activeTour) return null;
1253
- if (tourError && mode === "preview") return /* @__PURE__ */ jsxs("div", {
1254
- className: "sunboard-debug",
1255
- role: "alert",
1256
- children: [
1257
- /* @__PURE__ */ jsx("strong", { children: tourError.message }),
1258
- /* @__PURE__ */ jsxs("span", { children: ["Tour: ", tourError.tourKey] }),
1259
- /* @__PURE__ */ jsxs("span", { children: ["Step: ", tourError.stepKey] }),
1260
- tourError.route ? /* @__PURE__ */ jsxs("span", { children: ["Route: ", tourError.route] }) : null,
1261
- tourError.target ? /* @__PURE__ */ jsxs("span", { children: ["Target: ", tourError.target] }) : null,
1262
- /* @__PURE__ */ jsx("button", {
1263
- onClick: () => void dismissTour(),
1264
- type: "button",
1265
- children: "Dismiss"
1266
- })
1267
- ]
1268
- });
1269
1066
  if (!routeReady || !target.element) return null;
1270
1067
  const isLastStep = activeTour.stepIndex === activeTour.totalSteps - 1;
1271
1068
  return /* @__PURE__ */ jsxs(FloatingPortal, { children: [/* @__PURE__ */ jsx(SunboardTourScrim, { rect: spotlightRect }), /* @__PURE__ */ jsxs("section", {
@@ -1600,5 +1397,5 @@ const Sunboard = Object.assign(SunboardChecklist, {
1600
1397
  });
1601
1398
 
1602
1399
  //#endregion
1603
- export { Sunboard, SunboardChecklist, SunboardHotspots, SunboardProvider, SunboardRuntimeApiError, SunboardRuntimeClient, SunboardSurvey, SunboardTour, useChecklist, useSunboard, useSunboardPreviewSpec, useSurvey };
1400
+ export { Sunboard, SunboardChecklist, SunboardHotspots, SunboardProvider, SunboardRuntimeApiError, SunboardRuntimeClient, SunboardSurvey, SunboardTour, useChecklist, useSunboard, useSurvey };
1604
1401
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["eventSource: EventSource | null","SunboardRuntimeClient","resolvedMode: SunboardRuntimeMode","flat: FlatUser","current: unknown","interpolateOptional","next"],"sources":["../src/adapters/navigation.ts","../src/hooks/sunboard-context.ts","../src/hooks/useSunboardPreviewSpec.ts","../src/version.ts","../src/components/SunboardProvider.tsx","../src/lib/interpolate-tokens.ts","../src/hooks/useSunboard.ts","../src/hooks/useChecklist.ts","../src/hooks/useSurvey.ts","../src/icons/index.tsx","../src/components/ChecklistStep.tsx","../src/hooks/useInterpolated.ts","../src/hooks/useTargetElement.ts","../src/components/SunboardHotspots.tsx","../src/components/SunboardTour.tsx","../src/components/Sunboard.tsx"],"sourcesContent":["export type NavigateCallback = (url: string) => void | Promise<void>;\n\nexport async function navigateWithFallback(\n url: string,\n navigate?: NavigateCallback\n): Promise<void> {\n if (navigate) {\n await navigate(url);\n return;\n }\n\n if (typeof window !== \"undefined\") {\n window.location.assign(url);\n }\n}\n","import { createContext } from \"react\";\nimport type {\n NormalizedSunboardSpec,\n SunboardHotspot,\n SunboardStep,\n SunboardTourStep,\n TrackEventName\n} from \"@sunboard/core\";\n\nexport type SunboardUser = {\n id: string;\n email?: string | null;\n group?: string | null;\n properties?: Record<string, unknown>;\n};\n\nexport type TrackProperties = Record<string, unknown>;\n\nexport type TrackResult =\n | { ok: true }\n | { ok: false; reason: \"not-ready\" | \"missing-user\" | \"network-error\" };\n\nexport type SurveyResult =\n | { ok: true }\n | {\n ok: false;\n reason: \"not-ready\" | \"missing-user\" | \"not-survey\" | \"network-error\";\n };\n\nexport type TourResult =\n | { ok: true }\n | { ok: false; reason: \"not-ready\" | \"missing-user\" | \"missing-tour\" };\n\nexport type SunboardRuntimeMode = \"production\" | \"preview\";\n\nexport type SunboardTourError = {\n message: string;\n route?: string;\n stepKey?: string;\n target?: string;\n tourKey: string;\n};\n\nexport type ActiveSunboardTour = {\n key: string;\n name: string;\n step: SunboardTourStep;\n stepIndex: number;\n totalSteps: number;\n};\n\nexport type SunboardContextValue = {\n spec?: NormalizedSunboardSpec | null;\n user?: SunboardUser | null;\n loading: boolean;\n route?: string;\n mode: SunboardRuntimeMode;\n completedStepKeys: ReadonlySet<string>;\n completedTourKeys: ReadonlySet<string>;\n completedCount: number;\n totalSteps: number;\n activeTour: ActiveSunboardTour | null;\n tourError: SunboardTourError | null;\n track: (\n eventName: TrackEventName,\n properties?: TrackProperties\n ) => Promise<TrackResult>;\n /**\n * Submit survey answers for the active survey experience. On success the\n * runtime re-bootstraps so the freshly collected attributes route the user\n * into the next experience in-session.\n */\n submitSurvey: (answers: Record<string, unknown>) => Promise<SurveyResult>;\n startTour: (tourKey: string) => Promise<TourResult>;\n /**\n * Close the active tour. User-initiated dismissals are reported to the\n * runtime API as a negative signal (\"the user bailed at this step\");\n * pass `silent: true` for programmatic closes (e.g. a missing tour\n * target) that say nothing about the user's intent.\n */\n dismissTour: (options?: { silent?: boolean }) => Promise<TourResult>;\n advanceTour: () => Promise<TourResult>;\n navigateToStep: (step: SunboardStep) => Promise<void>;\n startChecklistStep: (step: SunboardStep) => Promise<TourResult | void>;\n reportTourError: (error: SunboardTourError | null) => void;\n visibleHotspots: Array<{ key: string; hotspot: SunboardHotspot }>;\n};\n\nexport const SunboardContext = createContext<SunboardContextValue | null>(null);\n","\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport type { NormalizedSunboardSpec } from \"@sunboard/core\";\n\nexport type SunboardPreviewSpecState = {\n error: Error | null;\n loading: boolean;\n source: \"none\" | \"preview\";\n spec: NormalizedSunboardSpec | null;\n};\n\nexport function useSunboardPreviewSpec(): SunboardPreviewSpecState {\n const [state, setState] = useState<SunboardPreviewSpecState>(() => ({\n error: null,\n loading: getPreviewSpecUrl() !== null,\n source: \"none\",\n spec: null\n }));\n\n useEffect(() => {\n const previewUrl = getPreviewSpecUrl();\n\n if (!previewUrl) {\n setState({\n error: null,\n loading: false,\n source: \"none\",\n spec: null\n });\n return;\n }\n\n const controller = new AbortController();\n let cancelled = false;\n let eventSource: EventSource | null = null;\n\n const loadSpec = async (signal: AbortSignal) => {\n const response = await fetch(previewUrl.toString(), { signal });\n\n if (!response.ok) {\n throw new Error(`Sunboard preview failed with ${response.status}.`);\n }\n\n const json = (await response.json()) as NormalizedSunboardSpec;\n\n if (!isSunboardSpecLike(json)) {\n throw new Error(\"Sunboard preview returned an invalid spec payload.\");\n }\n\n return json;\n };\n\n setState({\n error: null,\n loading: true,\n source: \"none\",\n spec: null\n });\n\n loadSpec(controller.signal)\n .then((previewSpec) => {\n if (cancelled) return;\n\n setState({\n error: null,\n loading: false,\n source: \"preview\",\n spec: previewSpec\n });\n\n const eventsUrl = buildEventsUrl(previewUrl);\n if (eventsUrl && typeof EventSource !== \"undefined\") {\n eventSource = new EventSource(eventsUrl);\n let firstMessage = true;\n eventSource.onmessage = () => {\n // The sidecar sends the current version on connect; skip the\n // first message so we don't refetch the spec we just loaded.\n if (firstMessage) {\n firstMessage = false;\n return;\n }\n\n void reloadSpec(previewUrl);\n };\n eventSource.onerror = () => {\n // EventSource auto-reconnects with backoff. Nothing to do.\n };\n }\n })\n .catch((error: unknown) => {\n if (controller.signal.aborted || cancelled) {\n return;\n }\n\n setState({\n error: error instanceof Error ? error : new Error(String(error)),\n loading: false,\n source: \"none\",\n spec: null\n });\n });\n\n const reloadSpec = async (url: URL) => {\n try {\n const response = await fetch(url.toString());\n\n if (!response.ok) {\n throw new Error(`Sunboard preview failed with ${response.status}.`);\n }\n\n const json = (await response.json()) as NormalizedSunboardSpec;\n\n if (!isSunboardSpecLike(json)) {\n throw new Error(\"Sunboard preview returned an invalid spec payload.\");\n }\n\n if (cancelled) return;\n\n setState({\n error: null,\n loading: false,\n source: \"preview\",\n spec: json\n });\n } catch (error) {\n if (cancelled) return;\n // Keep the existing spec on transient reload failure; surface the\n // error in state so dev tools can show it.\n setState((current) => ({\n ...current,\n error: error instanceof Error ? error : new Error(String(error))\n }));\n }\n };\n\n return () => {\n cancelled = true;\n controller.abort();\n eventSource?.close();\n };\n }, []);\n\n return state;\n}\n\nfunction buildEventsUrl(specUrl: URL): string | null {\n try {\n const eventsUrl = new URL(specUrl.toString());\n eventsUrl.pathname = eventsUrl.pathname.replace(/\\/spec\\.json$/, \"/events\");\n if (eventsUrl.pathname === specUrl.pathname) {\n return null;\n }\n return eventsUrl.toString();\n } catch {\n return null;\n }\n}\n\nfunction getPreviewSpecUrl(): URL | null {\n if (typeof window === \"undefined\") {\n return null;\n }\n\n const pageUrl = new URL(window.location.href);\n\n if (pageUrl.searchParams.get(\"sunboard_preview\") !== \"1\") {\n return null;\n }\n\n const previewUrlParam = pageUrl.searchParams.get(\"sunboard_preview_url\");\n\n if (!previewUrlParam) {\n return null;\n }\n\n try {\n const previewUrl = new URL(previewUrlParam);\n return isAllowedLocalPreviewUrl(previewUrl) ? previewUrl : null;\n } catch {\n return null;\n }\n}\n\nfunction isAllowedLocalPreviewUrl(url: URL): boolean {\n return (\n url.protocol === \"http:\" &&\n (url.hostname === \"localhost\" || url.hostname === \"127.0.0.1\")\n );\n}\n\nfunction isSunboardSpecLike(value: unknown): value is NormalizedSunboardSpec {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n\n const candidate = value as Partial<NormalizedSunboardSpec> & {\n survey?: { steps?: unknown };\n };\n\n if (\n typeof candidate.key !== \"string\" ||\n typeof candidate.name !== \"string\" ||\n typeof candidate.type !== \"string\"\n ) {\n return false;\n }\n\n // A checklist-bearing spec has `checklist.steps`; a survey spec has\n // `survey.steps` instead. Accept either so previewing a `type: survey` spec\n // doesn't get rejected (and fall back to the hosted bootstrap).\n const hasChecklist =\n typeof candidate.checklist === \"object\" &&\n Array.isArray(candidate.checklist?.steps);\n const hasSurvey =\n typeof candidate.survey === \"object\" &&\n Array.isArray(candidate.survey?.steps);\n\n return hasChecklist || hasSurvey;\n}\n","export const SDK_VERSION = \"0.0.0\";\n","import {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n type Dispatch,\n type ReactNode,\n type SetStateAction\n} from \"react\";\nimport type {\n NormalizedSunboardSpec,\n SunboardStep,\n TrackEventName\n} from \"@sunboard/core\";\nimport {\n DEFAULT_RUNTIME_API_URL,\n SunboardRuntimeClient,\n type RuntimeExperience,\n type RuntimeProgress,\n type RuntimeSurvey\n} from \"@sunboard/core/runtime-api\";\nimport {\n navigateWithFallback,\n type NavigateCallback\n} from \"../adapters/navigation\";\nimport { routeMatches } from \"../adapters/routes\";\nimport {\n SunboardContext,\n type ActiveSunboardTour,\n type SunboardContextValue,\n type SunboardRuntimeMode,\n type SunboardTourError,\n type SunboardUser,\n type TrackProperties\n} from \"../hooks/sunboard-context\";\nimport { useSunboardPreviewSpec } from \"../hooks/useSunboardPreviewSpec\";\nimport { SDK_VERSION } from \"../version\";\n\nexport type SunboardProviderProps = {\n spec?: NormalizedSunboardSpec | null;\n user?: SunboardUser | null;\n loading?: boolean;\n mode?: SunboardRuntimeMode;\n route?: string;\n navigate?: NavigateCallback;\n /**\n * Override the runtime API host. Dev-only — leave unset in production so the\n * SDK targets the hosted Sunboard runtime API. Defaults to\n * `DEFAULT_RUNTIME_API_URL`.\n */\n apiUrl?: string;\n publishableKey?: string;\n environment?: string;\n onTrack?: (\n eventName: TrackEventName,\n properties?: TrackProperties\n ) => void | Promise<void>;\n children: ReactNode;\n};\n\ntype ActiveTourState = {\n key: string;\n stepIndex: number;\n};\n\ntype BootstrapState = {\n experience: RuntimeExperience | null;\n survey: RuntimeSurvey | null;\n attributes: Record<string, unknown> | null;\n loading: boolean;\n};\n\nexport function SunboardProvider({\n spec: specProp,\n user,\n loading: loadingProp,\n mode: modeProp,\n route,\n navigate,\n apiUrl,\n publishableKey,\n environment = \"production\",\n onTrack,\n children\n}: SunboardProviderProps) {\n const currentRoute = route ?? getBrowserRoute();\n const currentOrigin = getBrowserOrigin();\n const preview = useSunboardPreviewSpec();\n const client = useMemo(() => {\n if (!publishableKey) {\n return null;\n }\n\n // `apiUrl` is a dev-only override; production apps only supply a key and\n // talk to the hosted runtime API by default.\n return new SunboardRuntimeClient({\n apiUrl: apiUrl || DEFAULT_RUNTIME_API_URL,\n publishableKey,\n sdkVersion: SDK_VERSION\n });\n }, [apiUrl, publishableKey]);\n // Bump to force a bootstrap refetch in-session (e.g. after a survey submit,\n // so freshly collected attributes route the user without a navigation).\n const [bootstrapNonce, setBootstrapNonce] = useState(0);\n const refreshBootstrap = useCallback(() => {\n setBootstrapNonce((nonce) => nonce + 1);\n }, []);\n\n const bootstrap = useSunboardBootstrap(client, {\n enabled:\n !specProp && preview.source !== \"preview\" && !preview.loading,\n environment,\n nonce: bootstrapNonce,\n origin: currentOrigin,\n route: currentRoute,\n user\n });\n\n const activeExperience = bootstrap.experience;\n // A survey pre-empts experiences (the server returns no experiences while one\n // is owed). Render the survey's spec when present, otherwise the experience's.\n const activeSurvey = bootstrap.survey;\n const hostedSpec = activeSurvey?.spec ?? activeExperience?.spec ?? null;\n const resolvedSpec = specProp ?? preview.spec ?? hostedSpec;\n const resolvedLoading =\n loadingProp ?? (preview.loading || bootstrap.loading);\n const resolvedMode: SunboardRuntimeMode =\n modeProp ?? (preview.source === \"preview\" ? \"preview\" : \"production\");\n const hostedMode = client !== null && !specProp && preview.source !== \"preview\";\n\n // The user view used for token interpolation/display merges the server-owned\n // attributes (the `onboarding.*` namespace returned by bootstrap) into the\n // host user, so `{{user.onboarding.*}}` resolves. Network calls keep using\n // the original `user` — owned attributes must never be echoed back as host\n // properties (which bootstrap overwrites).\n const ownedAttributes = bootstrap.attributes;\n const effectiveUser = useMemo<SunboardUser | null | undefined>(() => {\n if (!user || !ownedAttributes || Object.keys(ownedAttributes).length === 0) {\n return user;\n }\n\n return {\n ...user,\n properties: { ...(user.properties ?? {}), ...ownedAttributes }\n };\n }, [user, ownedAttributes]);\n\n const [completedStepKeys, setCompletedStepKeys] = useState<Set<string>>(\n () => new Set()\n );\n const [completedTourKeys, setCompletedTourKeys] = useState<Set<string>>(\n () => new Set()\n );\n const [activeTourState, setActiveTourState] = useState<ActiveTourState | null>(\n null\n );\n const [tourError, setTourError] = useState<SunboardTourError | null>(null);\n const lastNavigatedTourStep = useRef<string | null>(null);\n const storageKey =\n !hostedMode && resolvedSpec && user\n ? completedToursStorageKey(resolvedSpec.key, user.id)\n : null;\n const experienceKey =\n activeSurvey?.key ?? activeExperience?.key ?? resolvedSpec?.key ?? null;\n const versionId =\n activeSurvey?.version.id ?? activeExperience?.version.id ?? null;\n\n useEffect(() => {\n setCompletedStepKeys(new Set());\n setActiveTourState(null);\n setTourError(null);\n lastNavigatedTourStep.current = null;\n\n if (!storageKey) {\n setCompletedTourKeys(new Set());\n return;\n }\n\n setCompletedTourKeys(readCompletedTours(storageKey));\n }, [resolvedSpec?.key, storageKey, user?.id]);\n\n useEffect(() => {\n if (!hostedMode || !activeExperience) {\n return;\n }\n\n applyProgress(\n activeExperience.progress,\n setCompletedStepKeys,\n setCompletedTourKeys\n );\n }, [activeExperience, hostedMode]);\n\n const effectiveCompletedStepKeys = useMemo(() => {\n if (!hostedMode || !activeExperience) {\n return completedStepKeys;\n }\n\n return mergeKeys(\n completedStepKeys,\n activeExperience.progress.completedStepKeys\n );\n }, [activeExperience, completedStepKeys, hostedMode]);\n\n const effectiveCompletedTourKeys = useMemo(() => {\n if (!hostedMode || !activeExperience) {\n return completedTourKeys;\n }\n\n return mergeKeys(\n completedTourKeys,\n activeExperience.progress.completedTourKeys\n );\n }, [activeExperience, completedTourKeys, hostedMode]);\n\n useEffect(() => {\n if (!resolvedSpec) {\n return;\n }\n\n const tourCompletedStepKeys = (resolvedSpec.checklist?.steps ?? [])\n .filter(\n (step) =>\n step.completeWhen?.tour &&\n effectiveCompletedTourKeys.has(step.completeWhen.tour)\n )\n .map((step) => step.key);\n\n if (tourCompletedStepKeys.length === 0) {\n return;\n }\n\n setCompletedStepKeys((current) => mergeKeys(current, tourCompletedStepKeys));\n }, [effectiveCompletedTourKeys, resolvedSpec]);\n\n const activeTour = useMemo<ActiveSunboardTour | null>(() => {\n if (!resolvedSpec || !activeTourState) {\n return null;\n }\n\n const tour = resolvedSpec.tours[activeTourState.key];\n const step = tour?.steps[activeTourState.stepIndex];\n\n if (!tour || !step) {\n return null;\n }\n\n return {\n key: activeTourState.key,\n name: tour.name,\n step,\n stepIndex: activeTourState.stepIndex,\n totalSteps: tour.steps.length\n };\n }, [activeTourState, resolvedSpec]);\n\n useEffect(() => {\n if (!activeTour?.step.route || routeMatches(activeTour.step.route, currentRoute)) {\n return;\n }\n\n const navigationKey = `${activeTour.key}:${activeTour.step.key}:${activeTour.step.route}`;\n\n if (lastNavigatedTourStep.current === navigationKey) {\n return;\n }\n\n lastNavigatedTourStep.current = navigationKey;\n void navigateWithFallback(activeTour.step.route, navigate);\n }, [activeTour, currentRoute, navigate]);\n\n // Record per-step tour progress. Fires once per (tour, step) transition;\n // the dep array tracks step.key + tour key so re-renders don't spam the\n // endpoint and re-entries refresh `lastSeenAt` server-side.\n useEffect(() => {\n if (\n !hostedMode ||\n !client ||\n !activeTour ||\n !user ||\n !experienceKey\n ) {\n return;\n }\n\n void client\n .recordTourStep({\n environment,\n experienceKey,\n origin: currentOrigin,\n route: currentRoute,\n stepKey: activeTour.step.key,\n tourKey: activeTour.key,\n user: toRuntimeUser(user),\n versionId: versionId ?? undefined\n })\n .catch(() => {\n // Best-effort tracking — never break the host app on a 4xx/5xx.\n });\n // Intentionally narrow deps to the tour/step identity. We do not refire\n // when `currentRoute` changes mid-step, because the route adapter scrolls\n // the same popover into a new viewport, not a new logical step.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [activeTour?.key, activeTour?.step.key, client, currentOrigin, experienceKey, hostedMode, user?.id]);\n\n const totalSteps = resolvedSpec?.checklist?.steps.length ?? 0;\n const completedCount =\n resolvedSpec?.checklist?.steps.filter((step) =>\n effectiveCompletedStepKeys.has(step.key)\n ).length ?? 0;\n\n const completeTour = useCallback(\n (tourKey: string) => {\n setCompletedTourKeys((current) => {\n const next = new Set(current);\n next.add(tourKey);\n\n if (storageKey) {\n writeCompletedTours(storageKey, next);\n }\n\n return next;\n });\n },\n [storageKey]\n );\n\n const track = useCallback<SunboardContextValue[\"track\"]>(\n async (eventName, properties) => {\n if (resolvedLoading || !resolvedSpec) {\n return { ok: false, reason: \"not-ready\" };\n }\n\n if (!user) {\n return { ok: false, reason: \"missing-user\" };\n }\n\n const matchingStepKeys = (resolvedSpec.checklist?.steps ?? [])\n .filter((step) => step.completeWhen?.event === eventName)\n .map((step) => step.key);\n\n if (matchingStepKeys.length > 0) {\n // `mergeKeys` returns `current` unchanged when every key is already\n // present, so re-firing an already-completed step's event is a no-op\n // and does not churn state/recreate the context value. This is what\n // keeps a consumer that calls `track()` from an effect depending on the\n // whole context value from looping render→track→render.\n setCompletedStepKeys((current) => mergeKeys(current, matchingStepKeys));\n }\n\n let networkOk = true;\n\n if (hostedMode && client && experienceKey) {\n try {\n const response = await client.track({\n environment,\n eventName,\n experienceKey,\n origin: currentOrigin,\n properties,\n route: currentRoute,\n user: toRuntimeUser(user),\n versionId: versionId ?? undefined\n });\n applyProgress(\n response.progress,\n setCompletedStepKeys,\n setCompletedTourKeys\n );\n } catch {\n networkOk = false;\n }\n }\n\n try {\n await onTrack?.(eventName, properties);\n } catch {\n return { ok: false, reason: \"network-error\" };\n }\n\n if (!networkOk) {\n return { ok: false, reason: \"network-error\" };\n }\n\n return { ok: true };\n },\n [\n client,\n currentRoute,\n currentOrigin,\n environment,\n experienceKey,\n hostedMode,\n onTrack,\n resolvedLoading,\n resolvedSpec,\n user,\n versionId\n ]\n );\n\n const submitSurvey = useCallback<SunboardContextValue[\"submitSurvey\"]>(\n async (answers) => {\n if (resolvedLoading || !resolvedSpec) {\n return { ok: false, reason: \"not-ready\" };\n }\n\n if (!user) {\n return { ok: false, reason: \"missing-user\" };\n }\n\n if (resolvedSpec.type !== \"survey\" || !resolvedSpec.survey) {\n return { ok: false, reason: \"not-survey\" };\n }\n\n // Preview has no runtime API to write to. Treat Finish as a local success\n // so the survey can be stepped through and \"completed\" visually — nothing\n // is persisted and there's no routing (that needs the live runtime).\n if (resolvedMode === \"preview\") {\n return { ok: true };\n }\n\n // Surveys are server-backed: a survey can only be answered when the\n // runtime resolved it for this user.\n if (!hostedMode || !client || !experienceKey) {\n return { ok: false, reason: \"not-ready\" };\n }\n\n try {\n await client.submitSurvey({\n answers,\n environment,\n experienceKey,\n origin: currentOrigin,\n route: currentRoute,\n user: toRuntimeUser(user),\n versionId: versionId ?? undefined\n });\n } catch {\n return { ok: false, reason: \"network-error\" };\n }\n\n // Re-bootstrap so the freshly collected attributes route the user now.\n refreshBootstrap();\n return { ok: true };\n },\n [\n client,\n currentOrigin,\n currentRoute,\n environment,\n experienceKey,\n hostedMode,\n refreshBootstrap,\n resolvedLoading,\n resolvedMode,\n resolvedSpec,\n user,\n versionId\n ]\n );\n\n const navigateToStep = useCallback(\n async (step: SunboardStep) => {\n if (!step.route) {\n return;\n }\n\n await navigateWithFallback(step.route, navigate);\n },\n [navigate]\n );\n\n const startTour = useCallback<SunboardContextValue[\"startTour\"]>(\n async (tourKey) => {\n if (resolvedLoading || !resolvedSpec) {\n return { ok: false, reason: \"not-ready\" };\n }\n\n if (!user) {\n return { ok: false, reason: \"missing-user\" };\n }\n\n if (!resolvedSpec.tours[tourKey]) {\n return { ok: false, reason: \"missing-tour\" };\n }\n\n setTourError(null);\n lastNavigatedTourStep.current = null;\n setActiveTourState({ key: tourKey, stepIndex: 0 });\n\n // Fire-and-forget tour-start. The per-step record fires from the\n // useEffect below when activeTour resolves, so step[0] gets recorded\n // independently. We deliberately don't await — popover UX must not\n // block on network.\n if (hostedMode && client && experienceKey && user) {\n void client\n .startTour({\n environment,\n experienceKey,\n origin: currentOrigin,\n route: currentRoute,\n tourKey,\n user: toRuntimeUser(user),\n versionId: versionId ?? undefined\n })\n .catch(() => {\n // Tracking is best-effort. Network failures shouldn't surface\n // to the host app.\n });\n }\n\n return { ok: true };\n },\n [\n client,\n currentRoute,\n currentOrigin,\n environment,\n experienceKey,\n hostedMode,\n resolvedLoading,\n resolvedSpec,\n user,\n versionId\n ]\n );\n\n const dismissTour = useCallback<SunboardContextValue[\"dismissTour\"]>(\n async (options) => {\n // Capture what was on screen before clearing state — the dismissal\n // report needs the tour + step the user actually bailed on.\n const dismissed = activeTourState;\n const dismissedStepKey = dismissed\n ? resolvedSpec?.tours[dismissed.key]?.steps[dismissed.stepIndex]?.key\n : undefined;\n\n setActiveTourState(null);\n setTourError(null);\n lastNavigatedTourStep.current = null;\n\n // Best-effort negative-signal tracking, same contract as startTour:\n // never block the UI on network, never surface failures to the host.\n // Silent dismissals (missing target etc.) are not user intent and are\n // deliberately not reported.\n if (\n !options?.silent &&\n hostedMode &&\n client &&\n experienceKey &&\n user &&\n dismissed &&\n dismissedStepKey\n ) {\n void client\n .dismissTour({\n environment,\n experienceKey,\n origin: currentOrigin,\n route: currentRoute,\n stepKey: dismissedStepKey,\n tourKey: dismissed.key,\n user: toRuntimeUser(user),\n versionId: versionId ?? undefined\n })\n .catch(() => {\n // Tracking is best-effort.\n });\n }\n\n return { ok: true };\n },\n [\n activeTourState,\n client,\n currentOrigin,\n currentRoute,\n environment,\n experienceKey,\n hostedMode,\n resolvedSpec,\n user,\n versionId\n ]\n );\n\n const finishActiveTour = useCallback(\n async (tourKey: string) => {\n completeTour(tourKey);\n\n if (!hostedMode || !client || !experienceKey || !user) {\n return;\n }\n\n try {\n const response = await client.completeTour({\n environment,\n experienceKey,\n origin: currentOrigin,\n route: currentRoute,\n tourKey,\n user: toRuntimeUser(user),\n versionId: versionId ?? undefined\n });\n applyProgress(\n response.progress,\n setCompletedStepKeys,\n setCompletedTourKeys\n );\n } catch {\n // Fail soft: keep local optimistic completion, never break the host app.\n }\n },\n [\n client,\n completeTour,\n currentRoute,\n currentOrigin,\n environment,\n experienceKey,\n hostedMode,\n user,\n versionId\n ]\n );\n\n const advanceTour = useCallback<SunboardContextValue[\"advanceTour\"]>(\n async () => {\n if (resolvedLoading || !resolvedSpec || !activeTourState) {\n return { ok: false, reason: \"not-ready\" };\n }\n\n if (!user) {\n return { ok: false, reason: \"missing-user\" };\n }\n\n const tour = resolvedSpec.tours[activeTourState.key];\n\n if (!tour) {\n return { ok: false, reason: \"missing-tour\" };\n }\n\n setTourError(null);\n lastNavigatedTourStep.current = null;\n\n if (activeTourState.stepIndex >= tour.steps.length - 1) {\n const completedKey = activeTourState.key;\n setActiveTourState(null);\n await finishActiveTour(completedKey);\n return { ok: true };\n }\n\n setActiveTourState({\n key: activeTourState.key,\n stepIndex: activeTourState.stepIndex + 1\n });\n return { ok: true };\n },\n [activeTourState, finishActiveTour, resolvedLoading, resolvedSpec, user]\n );\n\n const startChecklistStep = useCallback<\n SunboardContextValue[\"startChecklistStep\"]\n >(\n async (step) => {\n if (step.action?.type === \"startTour\") {\n return startTour(step.action.tour);\n }\n\n await navigateToStep(step);\n },\n [navigateToStep, startTour]\n );\n\n const visibleHotspots = useMemo<SunboardContextValue[\"visibleHotspots\"]>(() => {\n if (!resolvedSpec || activeTour) {\n return [];\n }\n\n return Object.entries(resolvedSpec.hotspots)\n .filter(([, hotspot]) => routeMatches(hotspot.route, currentRoute))\n .filter(\n ([, hotspot]) =>\n hotspot.repeatable ||\n !effectiveCompletedTourKeys.has(hotspot.action.tour)\n )\n .map(([key, hotspot]) => ({ key, hotspot }));\n }, [activeTour, effectiveCompletedTourKeys, currentRoute, resolvedSpec]);\n\n const reportTourError = useCallback((error: SunboardTourError | null) => {\n setTourError(error);\n }, []);\n\n const value = useMemo<SunboardContextValue>(\n () => ({\n spec: resolvedSpec,\n user: effectiveUser,\n loading: resolvedLoading,\n mode: resolvedMode,\n route: currentRoute,\n completedStepKeys: effectiveCompletedStepKeys,\n completedTourKeys: effectiveCompletedTourKeys,\n completedCount,\n totalSteps,\n activeTour,\n tourError,\n track,\n submitSurvey,\n startTour,\n dismissTour,\n advanceTour,\n navigateToStep,\n startChecklistStep,\n reportTourError,\n visibleHotspots\n }),\n [\n resolvedSpec,\n effectiveUser,\n resolvedLoading,\n resolvedMode,\n currentRoute,\n effectiveCompletedStepKeys,\n effectiveCompletedTourKeys,\n completedCount,\n totalSteps,\n activeTour,\n tourError,\n track,\n submitSurvey,\n startTour,\n dismissTour,\n advanceTour,\n navigateToStep,\n startChecklistStep,\n reportTourError,\n visibleHotspots\n ]\n );\n\n return (\n <SunboardContext.Provider value={value}>\n {children}\n </SunboardContext.Provider>\n );\n}\n\nfunction useSunboardBootstrap(\n client: SunboardRuntimeClient | null,\n options: {\n enabled: boolean;\n environment: string;\n nonce: number;\n origin?: string;\n route: string;\n user?: SunboardUser | null;\n }\n): BootstrapState {\n const { enabled, environment, nonce, origin, route, user } = options;\n const [state, setState] = useState<BootstrapState>({\n attributes: null,\n experience: null,\n loading: false,\n survey: null\n });\n const warnedSdkAdvisory = useRef(false);\n\n useEffect(() => {\n // Never hit the hosted runtime API on a preview page. `enabled` already goes\n // false once the preview spec loads, but there's a first-render window\n // (SSR has no `window`, so the preview hook starts `loading: false`) where\n // it would briefly fire and fail with \"Failed to fetch\". This guard closes\n // that race — preview renders entirely from the sidecar spec.\n if (!client || !enabled || !user?.id || isPreviewPage()) {\n setState({\n attributes: null,\n experience: null,\n loading: false,\n survey: null\n });\n return;\n }\n\n // TODO: plumb `controller.signal` through SunboardRuntimeClient.post into\n // the underlying fetch so aborted requests cancel at the network layer. In\n // React 19 dev StrictMode the provider mounts twice; the first effect's\n // fetch keeps flying and can surface as `TypeError: Failed to fetch` in the\n // browser console. Harmless (we guard with signal.aborted before touching\n // state) but noisy in dev.\n const controller = new AbortController();\n setState({\n attributes: null,\n experience: null,\n loading: true,\n survey: null\n });\n\n client\n .bootstrap({\n environment,\n origin,\n route,\n user: toRuntimeUser(user)\n })\n .then((response) => {\n if (controller.signal.aborted) {\n return;\n }\n\n if (\n response.experiences.length === 0 &&\n !response.survey &&\n isDevEnvironment()\n ) {\n console.warn(\n `[sunboard] bootstrap returned no active experiences for route \"${route}\" in environment \"${environment}\". Check the dashboard for an enabled deployment whose trigger.route matches.`\n );\n }\n\n if (\n response.sdkAdvisory &&\n isDevEnvironment() &&\n !warnedSdkAdvisory.current\n ) {\n warnedSdkAdvisory.current = true;\n console.warn(`[sunboard] ${response.sdkAdvisory.message}`);\n }\n\n setState({\n attributes: response.attributes ?? null,\n experience: response.experiences[0] ?? null,\n loading: false,\n survey: response.survey ?? null\n });\n })\n .catch((error: unknown) => {\n if (controller.signal.aborted) {\n return;\n }\n\n if (isDevEnvironment()) {\n console.warn(\"[sunboard] bootstrap failed\", error);\n }\n\n setState({\n attributes: null,\n experience: null,\n loading: false,\n survey: null\n });\n });\n\n return () => {\n controller.abort();\n };\n // user.id is the identity key; we deliberately do not refetch when the\n // user object reference changes without an id change. `route` is\n // intentionally excluded so navigation does not wipe the active experience\n // — per-route visibility is handled by the runtime via step/tour/hotspot\n // route matching.\n // `nonce` is included so an explicit refreshBootstrap() (e.g. after a survey\n // submit) refetches even though client/user/route are unchanged.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [client, enabled, environment, nonce, origin, user?.id]);\n\n return state;\n}\n\nfunction applyProgress(\n progress: RuntimeProgress,\n setSteps: Dispatch<SetStateAction<Set<string>>>,\n setTours: Dispatch<SetStateAction<Set<string>>>\n) {\n setSteps((current) => mergeKeys(current, progress.completedStepKeys));\n setTours((current) => mergeKeys(current, progress.completedTourKeys));\n}\n\nfunction mergeKeys(current: Set<string>, incoming: string[]): Set<string> {\n if (incoming.length === 0) {\n return current;\n }\n\n const next = new Set(current);\n\n for (const key of incoming) {\n next.add(key);\n }\n\n if (next.size === current.size) {\n return current;\n }\n\n return next;\n}\n\nfunction isDevEnvironment(): boolean {\n if (typeof process === \"undefined\" || !process.env) {\n return false;\n }\n\n return process.env.NODE_ENV !== \"production\";\n}\n\n// True when the current page is a Sunboard preview (the CLI appends\n// `?sunboard_preview=1`). On a preview page the spec comes from the local\n// sidecar, so the hosted bootstrap must never run.\nfunction isPreviewPage(): boolean {\n if (typeof window === \"undefined\") {\n return false;\n }\n\n return (\n new URLSearchParams(window.location.search).get(\"sunboard_preview\") === \"1\"\n );\n}\n\nfunction toRuntimeUser(user: SunboardUser) {\n return {\n email: user.email ?? undefined,\n group: user.group ?? undefined,\n id: user.id,\n properties: user.properties\n };\n}\n\nfunction completedToursStorageKey(specKey: string, userId: string): string {\n return `sunboard:${specKey}:${userId}:completed-tours`;\n}\n\nfunction getBrowserRoute(): string {\n if (typeof window === \"undefined\") {\n return \"/\";\n }\n\n return `${window.location.pathname}${window.location.search}`;\n}\n\nfunction getBrowserOrigin(): string | undefined {\n if (typeof window === \"undefined\") {\n return undefined;\n }\n\n return window.location.origin;\n}\n\nfunction readCompletedTours(storageKey: string): Set<string> {\n if (typeof window === \"undefined\") {\n return new Set();\n }\n\n try {\n const rawValue = window.localStorage.getItem(storageKey);\n const parsed = rawValue ? (JSON.parse(rawValue) as unknown) : [];\n\n if (!Array.isArray(parsed)) {\n return new Set();\n }\n\n return new Set(parsed.filter((value): value is string => typeof value === \"string\"));\n } catch {\n return new Set();\n }\n}\n\nfunction writeCompletedTours(storageKey: string, tourKeys: Set<string>) {\n if (typeof window === \"undefined\") {\n return;\n }\n\n try {\n window.localStorage.setItem(storageKey, JSON.stringify([...tourKeys].sort()));\n } catch {\n // Ignore storage failures; persistence should never break the host app.\n }\n}\n","import type { SunboardUser } from \"../hooks/sunboard-context\";\n\nconst TOKEN_PATTERN =\n /\\{\\{\\s*user\\.([\\w][\\w.]*)(?:\\s*\\|\\s*(?:\"([^\"]*)\"|'([^']*)'))?\\s*\\}\\}/g;\n\ntype FlatUser = Record<string, unknown>;\n\nfunction flattenUser(user: SunboardUser | null | undefined): FlatUser {\n if (!user) return {};\n const properties = (user.properties ?? {}) as Record<string, unknown>;\n const flat: FlatUser = { ...properties };\n flat.id = user.id;\n if (user.email !== undefined) flat.email = user.email;\n if (user.group !== undefined) flat.group = user.group;\n return flat;\n}\n\nfunction lookupPath(root: FlatUser, path: string): unknown {\n const segments = path.split(\".\");\n let current: unknown = root;\n for (const segment of segments) {\n if (current === null || current === undefined) return undefined;\n if (typeof current !== \"object\") return undefined;\n current = (current as Record<string, unknown>)[segment];\n }\n return current;\n}\n\nfunction coerceValue(value: unknown): string | null {\n if (value === null || value === undefined) return null;\n if (typeof value === \"string\") return value.length > 0 ? value : null;\n if (typeof value === \"number\" || typeof value === \"boolean\") {\n return String(value);\n }\n return null;\n}\n\nexport function interpolateTokens(\n template: string,\n user: SunboardUser | null | undefined\n): string {\n if (!template || !template.includes(\"{{\")) return template;\n const flat = flattenUser(user);\n return template.replace(\n TOKEN_PATTERN,\n (_match, path: string, doubleQuoted?: string, singleQuoted?: string) => {\n const resolved = coerceValue(lookupPath(flat, path));\n if (resolved !== null) return resolved;\n const fallback = doubleQuoted ?? singleQuoted;\n return fallback ?? \"\";\n }\n );\n}\n\nexport function extractUserTokens(template: string): string[] {\n if (!template || !template.includes(\"{{\")) return [];\n const paths: string[] = [];\n TOKEN_PATTERN.lastIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = TOKEN_PATTERN.exec(template)) !== null) {\n const path = match[1];\n if (path) paths.push(path);\n }\n return paths;\n}\n","import { useContext } from \"react\";\nimport { SunboardContext, type SunboardContextValue } from \"./sunboard-context\";\n\nexport function useSunboard(): SunboardContextValue {\n const context = useContext(SunboardContext);\n\n if (!context) {\n throw new Error(\"useSunboard must be used within SunboardProvider.\");\n }\n\n return context;\n}\n","import { useCallback, useMemo, useRef, useState } from \"react\";\r\nimport type { SunboardStep } from \"@sunboard/core\";\r\nimport { interpolateTokens } from \"../lib/interpolate-tokens\";\r\nimport { useSunboard } from \"./useSunboard\";\r\n\r\nexport type ChecklistViewStep = {\r\n key: string;\r\n title: string;\r\n description?: string;\r\n badge?: string;\r\n actionLabel?: string;\r\n};\r\n\r\nexport type ChecklistCompletion = {\r\n title: string;\r\n subtitle?: string;\r\n message?: string;\r\n dismissAfterMs?: number;\r\n};\r\n\r\nexport type UseChecklistResult = {\r\n visible: boolean;\r\n title?: string;\r\n subtitle?: string;\r\n steps: ChecklistViewStep[];\r\n completedStepKeys: ReadonlySet<string>;\r\n completedCount: number;\r\n totalSteps: number;\r\n remainingCount: number;\r\n progress: number;\r\n isCompleted: (key: string) => boolean;\r\n hasAction: (step: ChecklistViewStep) => boolean;\r\n startStep: (step: ChecklistViewStep) => void;\r\n completion?: ChecklistCompletion;\r\n dismissCompletion: () => void;\r\n};\r\n\r\nconst SUPPORTED_CHECKLIST_TYPES = new Set([\"checklist\", \"hybrid\"]);\r\n\r\nfunction actionLabelFor(step: SunboardStep): string | undefined {\r\n if (step.action?.type === \"startTour\") return \"Start\";\r\n if (step.action || step.route) return \"Open\";\r\n return undefined;\r\n}\r\n\r\nfunction interpolateOptional(\r\n text: string | undefined,\r\n user: Parameters<typeof interpolateTokens>[1]\r\n): string | undefined {\r\n if (!text) return undefined;\r\n const interpolated = interpolateTokens(text, user);\r\n return interpolated || undefined;\r\n}\r\n\r\nexport function useChecklist(): UseChecklistResult {\r\n const {\r\n spec,\r\n user,\r\n loading,\r\n completedStepKeys,\r\n completedCount,\r\n totalSteps,\r\n startChecklistStep\r\n } = useSunboard();\r\n\r\n const [dismissedKey, setDismissedKey] = useState<string | null>(null);\r\n\r\n const hasChecklist = Boolean(spec && SUPPORTED_CHECKLIST_TYPES.has(spec.type));\r\n const allCompleted = totalSteps > 0 && completedCount >= totalSteps;\r\n const ready = !loading && !!user && !!spec && hasChecklist;\r\n\r\n // The completion screen celebrates the *transition* from incomplete to\r\n // complete in this session — not the steady \"already done\" state. The server\r\n // already knows per-user completion via bootstrap, so a user who finished\r\n // on another device sees `allCompleted === true` from the first ready\r\n // render and we suppress the celebration. We only show it if we observed\r\n // them being incomplete first.\r\n //\r\n // Scoped to (spec, user) so switching either restarts the observation.\n const specKey = spec?.key ?? null;\n const userId = user?.id ?? null;\n const observationKey =\n ready && specKey && userId ? `${specKey}:${userId}` : null;\n const observationRef = useRef<{ key: string; sawIncomplete: boolean } | null>(\n null\n );\n if (observationKey && observationRef.current?.key !== observationKey) {\n observationRef.current = {\n key: observationKey,\n sawIncomplete: !allCompleted\n };\n } else if (observationKey && observationRef.current && !allCompleted) {\n observationRef.current.sawIncomplete = true;\n }\n const sawIncompleteFirst = observationRef.current?.sawIncomplete ?? false;\r\n\r\n const dismissed = dismissedKey !== null && dismissedKey === observationKey;\r\n\r\n const rawSteps = useMemo<SunboardStep[]>(\r\n () => spec?.checklist?.steps ?? [],\r\n [spec]\r\n );\r\n\r\n const title = useMemo(\r\n () => interpolateOptional(spec?.title, user ?? null),\r\n [spec?.title, user]\r\n );\r\n\r\n const subtitle = useMemo(\r\n () => interpolateOptional(spec?.subtitle, user ?? null),\r\n [spec?.subtitle, user]\r\n );\r\n\r\n const completion = useMemo<ChecklistCompletion | undefined>(() => {\r\n const raw = spec?.completion;\r\n if (!raw) return undefined;\r\n const interpolatedTitle = interpolateOptional(raw.title, user ?? null);\r\n if (!interpolatedTitle) return undefined;\r\n return {\r\n title: interpolatedTitle,\r\n subtitle: interpolateOptional(raw.subtitle, user ?? null),\r\n message: interpolateOptional(raw.message, user ?? null),\r\n dismissAfterMs: raw.dismissAfterMs\r\n };\r\n }, [spec?.completion, user]);\r\n\r\n const showingCompletion =\r\n ready && allCompleted && !!completion && sawIncompleteFirst && !dismissed;\r\n const visible = (ready && !allCompleted) || showingCompletion;\r\n\r\n const steps = useMemo<ChecklistViewStep[]>(() => {\r\n return rawSteps.map((step) => ({\r\n key: step.key,\r\n title: interpolateTokens(step.title, user ?? null),\r\n description: step.description\r\n ? interpolateTokens(step.description, user ?? null)\r\n : undefined,\r\n badge: step.badge\r\n ? interpolateTokens(step.badge, user ?? null)\r\n : undefined,\r\n actionLabel: actionLabelFor(step)\r\n }));\r\n }, [rawSteps, user]);\r\n\r\n const stepsByKey = useMemo(() => {\r\n const map = new Map<string, SunboardStep>();\r\n for (const step of rawSteps) {\r\n map.set(step.key, step);\r\n }\r\n return map;\r\n }, [rawSteps]);\r\n\r\n const remainingCount = Math.max(totalSteps - completedCount, 0);\r\n const progress =\r\n totalSteps > 0 ? Math.round((completedCount / totalSteps) * 100) : 0;\r\n\r\n const dismissCompletion = useCallback(() => {\r\n if (observationKey) {\r\n setDismissedKey(observationKey);\r\n }\r\n }, [observationKey]);\r\n\r\n return {\r\n visible,\r\n title,\r\n subtitle,\r\n steps,\r\n completedStepKeys,\r\n completedCount,\r\n totalSteps,\r\n remainingCount,\r\n progress,\r\n isCompleted: (key) => completedStepKeys.has(key),\r\n hasAction: (step) => step.actionLabel !== undefined,\r\n startStep: (step) => {\r\n const raw = stepsByKey.get(step.key);\r\n if (raw) {\r\n void startChecklistStep(raw);\r\n }\r\n },\r\n completion: showingCompletion ? completion : undefined,\r\n dismissCompletion\r\n };\r\n}\r\n","import { useCallback, useMemo, useRef, useState } from \"react\";\nimport type { SurveyQuestionType } from \"@sunboard/core\";\nimport { interpolateTokens } from \"../lib/interpolate-tokens\";\nimport { useSunboard } from \"./useSunboard\";\n\nexport type SurveyViewOption = {\n value: string;\n label: string;\n};\n\nexport type SurveyViewQuestion = {\n key: string;\n prompt: string;\n type: SurveyQuestionType;\n required: boolean;\n options: SurveyViewOption[];\n};\n\nexport type SurveyViewStep = {\n title?: string;\n subtitle?: string;\n questions: SurveyViewQuestion[];\n};\n\nexport type UseSurveyResult = {\n visible: boolean;\n title?: string;\n subtitle?: string;\n /** Every question across all steps — for all-at-once renderers (the built-in modal). */\n questions: SurveyViewQuestion[];\n /** The authored step grouping (one question per step by default). */\n steps: SurveyViewStep[];\n /** Hook-owned navigation. */\n stepIndex: number;\n totalSteps: number;\n currentStep?: SurveyViewStep;\n progress: number; // 0..100, by step\n isFirstStep: boolean;\n isLastStep: boolean;\n /** True when the current step's required questions are answered. */\n canAdvance: boolean;\n /** Advance to the next step, or submit on the last step. */\n next: () => void;\n back: () => void;\n answers: Record<string, unknown>;\n setAnswer: (key: string, value: string) => void;\n toggleAnswer: (key: string, value: string) => void;\n isAnswered: (key: string) => boolean;\n /** True when every required question (across all steps) is answered. */\n canSubmit: boolean;\n submitting: boolean;\n error: string | null;\n submit: () => Promise<void>;\n};\n\nfunction interpolateOptional(\n text: string | undefined,\n user: Parameters<typeof interpolateTokens>[1]\n): string | undefined {\n if (!text) return undefined;\n const interpolated = interpolateTokens(text, user);\n return interpolated || undefined;\n}\n\nfunction answered(value: unknown): boolean {\n if (value === undefined || value === null) return false;\n if (typeof value === \"string\") return value.trim().length > 0;\n if (Array.isArray(value)) return value.length > 0;\n return true;\n}\n\nexport function useSurvey(): UseSurveyResult {\n const { spec, user, loading, submitSurvey } = useSunboard();\n\n const [answers, setAnswers] = useState<Record<string, unknown>>({});\n const [submitting, setSubmitting] = useState(false);\n const [submitted, setSubmitted] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [stepIndex, setStepIndex] = useState(0);\n\n const isSurvey = Boolean(spec && spec.type === \"survey\" && spec.survey);\n const ready = !loading && !!user && isSurvey;\n\n const title = useMemo(\n () => interpolateOptional(spec?.title, user ?? null),\n [spec?.title, user]\n );\n const subtitle = useMemo(\n () => interpolateOptional(spec?.subtitle, user ?? null),\n [spec?.subtitle, user]\n );\n\n const steps = useMemo<SurveyViewStep[]>(() => {\n const rawSteps = spec?.survey?.steps ?? [];\n return rawSteps.map((step) => ({\n title: interpolateOptional(step.title, user ?? null),\n subtitle: interpolateOptional(step.subtitle, user ?? null),\n questions: step.questions.map((question) => ({\n key: question.key,\n prompt: interpolateTokens(question.prompt, user ?? null),\n type: question.type,\n required: question.required,\n options: (question.options ?? []).map((option) => ({\n value: option.value,\n label: interpolateTokens(option.label, user ?? null)\n }))\n }))\n }));\n }, [spec?.survey, user]);\n\n const questions = useMemo<SurveyViewQuestion[]>(\n () => steps.flatMap((step) => step.questions),\n [steps]\n );\n\n // Restart at the first step whenever the question set changes (a new survey).\n const signature = questions.map((question) => question.key).join(\"|\");\n const signatureRef = useRef(signature);\n if (signatureRef.current !== signature) {\n signatureRef.current = signature;\n if (stepIndex !== 0) setStepIndex(0);\n }\n\n const totalSteps = steps.length;\n const safeIndex =\n totalSteps > 0 ? Math.min(stepIndex, totalSteps - 1) : 0;\n const currentStep = steps[safeIndex];\n\n const setAnswer = useCallback((key: string, value: string) => {\n setAnswers((current) => ({ ...current, [key]: value }));\n }, []);\n\n const toggleAnswer = useCallback((key: string, value: string) => {\n setAnswers((current) => {\n const existing = Array.isArray(current[key])\n ? (current[key] as string[])\n : [];\n const next = existing.includes(value)\n ? existing.filter((item) => item !== value)\n : [...existing, value];\n return { ...current, [key]: next };\n });\n }, []);\n\n const isAnswered = useCallback(\n (key: string) => answered(answers[key]),\n [answers]\n );\n\n const canSubmit = useMemo(() => {\n if (!ready || submitting) return false;\n return questions\n .filter((question) => question.required)\n .every((question) => answered(answers[question.key]));\n }, [answers, questions, ready, submitting]);\n\n const canAdvance = useMemo(() => {\n if (!currentStep) return false;\n return currentStep.questions\n .filter((question) => question.required)\n .every((question) => answered(answers[question.key]));\n }, [answers, currentStep]);\n\n const submit = useCallback(async () => {\n if (!canSubmit) return;\n setSubmitting(true);\n setError(null);\n\n const result = await submitSurvey(answers);\n setSubmitting(false);\n\n if (result.ok) {\n setSubmitted(true);\n return;\n }\n\n setError(\n result.reason === \"network-error\"\n ? \"Something went wrong. Please try again.\"\n : \"Survey is not ready.\"\n );\n }, [answers, canSubmit, submitSurvey]);\n\n const isLastStep = totalSteps === 0 || safeIndex >= totalSteps - 1;\n const isFirstStep = safeIndex === 0;\n\n const next = useCallback(() => {\n if (isLastStep) {\n void submit();\n } else {\n setStepIndex(safeIndex + 1);\n }\n }, [isLastStep, safeIndex, submit]);\n\n const back = useCallback(() => {\n setStepIndex((current) => Math.max(0, current - 1));\n }, []);\n\n return {\n visible: ready && !submitted,\n title,\n subtitle,\n questions,\n steps,\n stepIndex: safeIndex,\n totalSteps,\n currentStep,\n progress: totalSteps > 0 ? ((safeIndex + 1) / totalSteps) * 100 : 0,\n isFirstStep,\n isLastStep,\n canAdvance,\n next,\n back,\n answers,\n setAnswer,\n toggleAnswer,\n isAnswered,\n canSubmit,\n submitting,\n error,\n submit\n };\n}\n","\r\nexport const CheckIcon = () => {\r\n return <svg xmlns=\"http://www.w3.org/2000/svg\"\r\n width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\"\r\n stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\"\r\n className=\"lucide lucide-check-icon lucide-check\"><path d=\"M20 6 9 17l-5-5\" /></svg>\r\n}","import { CheckIcon } from \"../icons\";\nimport type { ChecklistViewStep } from \"../hooks/useChecklist\";\n\ntype ChecklistStepProps = {\n completed: boolean;\n onStart: () => void;\n step: ChecklistViewStep;\n};\n\nexport function ChecklistStep({\n completed,\n onStart,\n step\n}: ChecklistStepProps) {\n const hasAction = step.actionLabel !== undefined;\n\n const status = (\n <span\n className=\"sunboard-step-status\"\n aria-label={completed ? \"Completed\" : \"Incomplete\"}\n >\n {completed ? <CheckIcon /> : null}\n </span>\n );\n\n const content = (\n <div className=\"sunboard-step-content\">\n <div className=\"sunboard-step-heading\">\n <h3 className=\"sunboard-step-title\">{step.title}</h3>\n {step.badge ? (\n <span className=\"sunboard-step-badge\">{step.badge}</span>\n ) : null}\n </div>\n\n {step.description ? (\n <p className=\"sunboard-step-description\">{step.description}</p>\n ) : null}\n </div>\n );\n\n return (\n <li className=\"sunboard-step\" data-completed={completed}>\n {hasAction ? (\n <button\n className=\"sunboard-step-row\"\n type=\"button\"\n onClick={onStart}\n aria-label={`${step.actionLabel} ${step.title}`}\n >\n {status}\n {content}\n </button>\n ) : (\n <div className=\"sunboard-step-row\">\n {status}\n {content}\n </div>\n )}\n </li>\n );\n}\n","import { useMemo } from \"react\";\nimport { interpolateTokens } from \"../lib/interpolate-tokens\";\nimport { useSunboard } from \"./useSunboard\";\n\nexport function useInterpolated(text: string): string;\nexport function useInterpolated(text: string | undefined): string | undefined;\nexport function useInterpolated(text: string | undefined): string | undefined {\n const { user } = useSunboard();\n return useMemo(() => {\n if (text === undefined) return undefined;\n return interpolateTokens(text, user);\n }, [text, user]);\n}\n","import { useEffect, useState } from \"react\";\n\nexport type TargetElementState = {\n element: HTMLElement | null;\n timedOut: boolean;\n};\n\nexport function useTargetElement(\n selector: string | null,\n enabled: boolean,\n timeoutMs = 5_000\n): TargetElementState {\n const [state, setState] = useState<TargetElementState>({\n element: null,\n timedOut: false\n });\n\n useEffect(() => {\n if (!enabled || !selector) {\n setState({ element: null, timedOut: false });\n return;\n }\n\n let cancelled = false;\n const startedAt = Date.now();\n\n const findTarget = () => {\n if (cancelled) {\n return;\n }\n\n const element = document.querySelector(selector);\n\n if (element instanceof HTMLElement) {\n setState({ element, timedOut: false });\n return;\n }\n\n if (Date.now() - startedAt >= timeoutMs) {\n setState({ element: null, timedOut: true });\n return;\n }\n\n window.setTimeout(findTarget, 100);\n };\n\n setState({ element: null, timedOut: false });\n findTarget();\n\n return () => {\n cancelled = true;\n };\n }, [enabled, selector, timeoutMs]);\n\n return state;\n}\n","import {\r\n FloatingPortal,\r\n autoUpdate,\r\n offset,\r\n useFloating\r\n} from \"@floating-ui/react\";\r\nimport { useEffect } from \"react\";\r\nimport type { SunboardHotspot } from \"@sunboard/core\";\r\nimport { useInterpolated } from \"../hooks/useInterpolated\";\r\nimport { useSunboard } from \"../hooks/useSunboard\";\r\nimport { useTargetElement } from \"../hooks/useTargetElement\";\r\n\r\ntype HotspotProps = {\r\n hotspot: SunboardHotspot;\r\n hotspotKey: string;\r\n};\r\n\r\nconst HOTSPOT_RADIUS_PX = 9;\r\n\r\nexport function SunboardHotspots() {\r\n const { visibleHotspots } = useSunboard();\r\n\r\n return (\r\n <>\r\n {visibleHotspots.map(({ key, hotspot }) => (\r\n <Hotspot hotspot={hotspot} hotspotKey={key} key={key} />\r\n ))}\r\n </>\r\n );\r\n}\r\n\r\nfunction Hotspot({ hotspot, hotspotKey }: HotspotProps) {\r\n const { startTour } = useSunboard();\r\n const label = useInterpolated(hotspot.label);\r\n const target = useTargetElement(hotspot.target, true);\r\n const { refs, floatingStyles } = useFloating({\r\n placement: hotspot.placement,\r\n strategy: \"fixed\",\r\n middleware: [offset(-HOTSPOT_RADIUS_PX)],\r\n whileElementsMounted: autoUpdate\r\n });\r\n\r\n useEffect(() => {\r\n if (!target.element) {\r\n return;\r\n }\r\n\r\n refs.setReference(target.element);\r\n }, [refs, target.element]);\r\n\r\n if (!target.element) {\r\n return null;\r\n }\r\n\r\n return (\r\n <FloatingPortal>\r\n <button\r\n aria-label={label ?? `Start ${hotspot.action.tour} tour`}\r\n className=\"sunboard-hotspot\"\r\n data-pulse={hotspot.pulse}\r\n data-sunboard-hotspot={hotspotKey}\r\n onClick={() => {\r\n void startTour(hotspot.action.tour);\r\n }}\r\n ref={refs.setFloating}\r\n style={floatingStyles}\r\n type=\"button\"\r\n >\r\n <span className=\"sunboard-hotspot-dot\" />\r\n </button>\r\n </FloatingPortal>\r\n );\r\n}\r\n","import {\r\n arrow,\r\n FloatingArrow,\r\n FloatingPortal,\r\n flip,\r\n offset,\r\n shift,\r\n useFloating\r\n} from \"@floating-ui/react\";\r\nimport { useEffect, useRef, useState } from \"react\";\r\nimport { routeMatches } from \"../adapters/routes\";\r\nimport { useInterpolated } from \"../hooks/useInterpolated\";\r\nimport { useSunboard } from \"../hooks/useSunboard\";\r\nimport { useTargetElement } from \"../hooks/useTargetElement\";\r\n\r\nexport function SunboardTour() {\r\n const {\r\n activeTour,\r\n advanceTour,\r\n dismissTour,\r\n mode,\r\n reportTourError,\r\n route,\r\n tourError\r\n } = useSunboard();\r\n const arrowRef = useRef<SVGSVGElement | null>(null);\r\n const [spotlightRect, setSpotlightRect] = useState<DOMRect | null>(null);\r\n const routeReady = routeMatches(activeTour?.step.route, route ?? \"/\");\r\n const target = useTargetElement(\r\n activeTour?.step.target ?? null,\r\n Boolean(activeTour) && routeReady,\r\n 1_000\r\n );\r\n const { context, refs, floatingStyles, update } = useFloating({\r\n placement: activeTour?.step.placement ?? \"bottom\",\r\n middleware: [\r\n offset(16),\r\n flip(),\r\n shift({ padding: 12 }),\r\n arrow({ element: arrowRef })\r\n ]\r\n });\r\n\r\n useEffect(() => {\r\n if (!activeTour) {\r\n reportTourError(null);\r\n return;\r\n }\r\n\r\n if (!routeReady) {\r\n return;\r\n }\r\n\r\n if (!target.timedOut) {\r\n return;\r\n }\r\n\r\n const error = {\r\n message: \"Sunboard could not find the tour target.\",\r\n route: activeTour.step.route,\r\n stepKey: activeTour.step.key,\r\n target: activeTour.step.target,\r\n tourKey: activeTour.key\r\n };\r\n\r\n if (mode === \"preview\") {\r\n reportTourError(error);\r\n return;\r\n }\r\n\r\n reportTourError(null);\r\n // Programmatic close — the target is missing, the user didn't reject\r\n // anything, so don't record a dismissal signal.\r\n void dismissTour({ silent: true });\r\n }, [activeTour, dismissTour, mode, reportTourError, routeReady, target.timedOut]);\r\n\r\n useEffect(() => {\r\n if (!activeTour || !target.element) {\r\n return;\r\n }\r\n\r\n reportTourError(null);\r\n refs.setReference(target.element);\r\n target.element.scrollIntoView({ block: \"center\", inline: \"center\" });\r\n void update();\r\n }, [activeTour, refs, reportTourError, target.element, update]);\r\n\r\n useEffect(() => {\r\n if (!activeTour || !target.element) {\r\n setSpotlightRect(null);\r\n return;\r\n }\r\n\r\n const updateSpotlight = () => {\r\n setSpotlightRect(target.element?.getBoundingClientRect() ?? null);\r\n };\r\n\r\n updateSpotlight();\r\n window.addEventListener(\"resize\", updateSpotlight);\r\n window.addEventListener(\"scroll\", updateSpotlight, true);\r\n\r\n target.element.setAttribute(\"data-sunboard-tour-target\", \"true\");\r\n\r\n return () => {\r\n window.removeEventListener(\"resize\", updateSpotlight);\r\n window.removeEventListener(\"scroll\", updateSpotlight, true);\r\n target.element?.removeAttribute(\"data-sunboard-tour-target\");\r\n setSpotlightRect(null);\r\n };\r\n }, [activeTour, target.element]);\r\n\r\n useEffect(() => {\r\n if (!activeTour || !target.element) {\r\n return;\r\n }\r\n\r\n const advanceOnTargetClick = () => {\r\n window.setTimeout(() => {\r\n void advanceTour();\r\n }, 0);\r\n };\r\n\r\n target.element.addEventListener(\"click\", advanceOnTargetClick);\r\n\r\n return () => {\r\n target.element?.removeEventListener(\"click\", advanceOnTargetClick);\r\n };\r\n }, [activeTour, advanceTour, target.element]);\r\n\r\n const tourName = useInterpolated(activeTour?.name);\r\n const stepTitle = useInterpolated(activeTour?.step.title);\r\n const stepBody = useInterpolated(activeTour?.step.body);\r\n\r\n if (!activeTour) {\r\n return null;\r\n }\r\n\r\n if (tourError && mode === \"preview\") {\r\n return (\r\n <div className=\"sunboard-debug\" role=\"alert\">\r\n <strong>{tourError.message}</strong>\r\n <span>Tour: {tourError.tourKey}</span>\r\n <span>Step: {tourError.stepKey}</span>\r\n {tourError.route ? <span>Route: {tourError.route}</span> : null}\r\n {tourError.target ? <span>Target: {tourError.target}</span> : null}\r\n <button onClick={() => void dismissTour()} type=\"button\">\r\n Dismiss\r\n </button>\r\n </div>\r\n );\r\n }\r\n\r\n if (!routeReady || !target.element) {\r\n return null;\r\n }\r\n\r\n const isLastStep = activeTour.stepIndex === activeTour.totalSteps - 1;\r\n\r\n return (\r\n <FloatingPortal>\r\n <SunboardTourScrim rect={spotlightRect} />\r\n <section\r\n aria-label={`${tourName} tour step`}\r\n className=\"sunboard-tour\"\r\n ref={refs.setFloating}\r\n role=\"dialog\"\r\n style={floatingStyles}\r\n >\r\n <FloatingArrow\r\n className=\"sunboard-tour-arrow\"\r\n context={context}\r\n fill=\"var(--sunboard-tour-background)\"\r\n ref={arrowRef}\r\n stroke=\"var(--sunboard-tour-border)\"\r\n strokeWidth={1}\r\n tipRadius={2}\r\n />\r\n <div className=\"sunboard-tour-meta\">\r\n {activeTour.stepIndex + 1} / {activeTour.totalSteps}\r\n </div>\r\n <h2 className=\"sunboard-tour-title\">{stepTitle}</h2>\r\n {stepBody ? (\r\n <p className=\"sunboard-tour-body\">{stepBody}</p>\r\n ) : null}\r\n <div className=\"sunboard-tour-actions\">\r\n <button\r\n className=\"sunboard-tour-secondary\"\r\n onClick={() => void dismissTour()}\r\n type=\"button\"\r\n >\r\n Dismiss\r\n </button>\r\n <button\r\n className=\"sunboard-tour-primary\"\r\n onClick={() => void advanceTour()}\r\n type=\"button\"\r\n >\r\n {isLastStep ? \"Finish\" : \"Next\"}\r\n </button>\r\n </div>\r\n </section>\r\n </FloatingPortal>\r\n );\r\n}\r\n\r\nfunction SunboardTourScrim({ rect }: { rect: DOMRect | null }) {\r\n if (!rect) {\r\n return <div className=\"sunboard-tour-scrim\" />;\r\n }\r\n\r\n const padding = 8;\r\n const radius = 12;\r\n const left = Math.max(rect.left - padding, 0);\r\n const top = Math.max(rect.top - padding, 0);\r\n const width = rect.width + padding * 2;\r\n const height = rect.height + padding * 2;\r\n const maskId = \"sunboard-tour-spotlight-mask\";\r\n\r\n return (\r\n <svg\r\n aria-hidden=\"true\"\r\n className=\"sunboard-tour-scrim sunboard-tour-spotlight\"\r\n focusable=\"false\"\r\n >\r\n <defs>\r\n <mask id={maskId}>\r\n <rect fill=\"white\" height=\"100%\" width=\"100%\" x=\"0\" y=\"0\" />\r\n <rect\r\n fill=\"black\"\r\n height={height}\r\n rx={radius}\r\n ry={radius}\r\n width={width}\r\n x={left}\r\n y={top}\r\n />\r\n </mask>\r\n </defs>\r\n <rect\r\n fill=\"var(--sunboard-overlay-color)\"\r\n height=\"100%\"\r\n mask={`url(#${maskId})`}\r\n width=\"100%\"\r\n x=\"0\"\r\n y=\"0\"\r\n />\r\n </svg>\r\n );\r\n}\r\n","import { useEffect, useState } from \"react\";\r\nimport { useChecklist } from \"../hooks/useChecklist\";\r\nimport { useSurvey } from \"../hooks/useSurvey\";\r\nimport { ChecklistStep } from \"./ChecklistStep\";\r\nimport { SunboardHotspots } from \"./SunboardHotspots\";\r\nimport { SunboardTour } from \"./SunboardTour\";\r\n\r\nexport function SunboardChecklist() {\r\n const [open, setOpen] = useState(true);\r\n const {\r\n visible,\r\n title,\r\n subtitle,\r\n steps,\r\n completedCount,\r\n totalSteps,\r\n remainingCount,\r\n progress,\r\n isCompleted,\r\n startStep,\r\n completion,\r\n dismissCompletion\r\n } = useChecklist();\r\n\r\n // Auto-dismiss the completion screen after the spec-author-specified delay.\r\n // Lives in the widget rather than the hook so custom checklists can choose\r\n // their own timing UX (e.g. dismiss on tab change) without inheriting ours.\r\n useEffect(() => {\r\n if (!completion?.dismissAfterMs) return;\r\n const handle = window.setTimeout(dismissCompletion, completion.dismissAfterMs);\r\n return () => window.clearTimeout(handle);\r\n }, [completion, dismissCompletion]);\r\n\r\n if (!visible || !title) {\r\n return null;\r\n }\r\n\r\n if (completion) {\r\n return (\r\n <aside\r\n className=\"sunboard-root sunboard-root-completion\"\r\n aria-label={`${title} complete`}\r\n role=\"status\"\r\n aria-live=\"polite\"\r\n >\r\n <button\r\n className=\"sunboard-close sunboard-completion-close\"\r\n type=\"button\"\r\n onClick={dismissCompletion}\r\n aria-label=\"Dismiss completion message\"\r\n >\r\n x\r\n </button>\r\n <div className=\"sunboard-completion\">\r\n <div className=\"sunboard-completion-check\" aria-hidden=\"true\">\r\n <svg\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"3\"\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n >\r\n <polyline points=\"5 12 10 17 19 8\" />\r\n </svg>\r\n </div>\r\n {completion.subtitle ? (\r\n <p className=\"sunboard-completion-subtitle\">{completion.subtitle}</p>\r\n ) : null}\r\n <h2 className=\"sunboard-completion-title\">{completion.title}</h2>\r\n {completion.message ? (\r\n <p className=\"sunboard-completion-message\">{completion.message}</p>\r\n ) : null}\r\n </div>\r\n </aside>\r\n );\r\n }\r\n\r\n if (!open) {\r\n return (\r\n <button\r\n className=\"sunboard-launcher\"\r\n type=\"button\"\r\n onClick={() => setOpen(true)}\r\n aria-label={`Open ${title} checklist, ${remainingCount} tasks remaining`}\r\n >\r\n <span className=\"sunboard-launcher-count\">{remainingCount}</span>\r\n </button>\r\n );\r\n }\r\n\r\n return (\r\n <aside className=\"sunboard-root\" aria-label={`${title} checklist`}>\r\n <div className=\"sunboard-header\">\r\n <div>\r\n {subtitle ? <p className=\"sunboard-eyebrow\">{subtitle}</p> : null}\r\n <h2 className=\"sunboard-title\">{title}</h2>\r\n </div>\r\n <div className=\"sunboard-header-actions\">\r\n <p\r\n className=\"sunboard-progress-count\"\r\n aria-label=\"Checklist progress\"\r\n >\r\n {completedCount} / {totalSteps}\r\n </p>\r\n <button\r\n className=\"sunboard-close\"\r\n type=\"button\"\r\n onClick={() => setOpen(false)}\r\n aria-label={`Close ${title} checklist`}\r\n >\r\n x\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <div\r\n className=\"sunboard-progress\"\r\n role=\"progressbar\"\r\n aria-label={`${title} progress`}\r\n aria-valuemin={0}\r\n aria-valuemax={totalSteps}\r\n aria-valuenow={completedCount}\r\n >\r\n <div\r\n className=\"sunboard-progress-fill\"\r\n style={{ width: `${progress}%` }}\r\n />\r\n </div>\r\n\r\n <ol className=\"sunboard-step-list\">\r\n {steps.map((step) => (\r\n <ChecklistStep\r\n completed={isCompleted(step.key)}\r\n key={step.key}\r\n onStart={() => startStep(step)}\r\n step={step}\r\n />\r\n ))}\r\n </ol>\r\n </aside>\r\n );\r\n}\r\n\r\nexport function SunboardSurvey() {\r\n const {\r\n visible,\r\n title,\r\n subtitle,\r\n currentStep,\r\n stepIndex,\r\n totalSteps,\r\n progress,\r\n isFirstStep,\r\n isLastStep,\r\n canAdvance,\r\n canSubmit,\r\n next,\r\n back,\r\n answers,\r\n setAnswer,\r\n toggleAnswer,\r\n submitting,\r\n error\r\n } = useSurvey();\r\n\r\n if (!visible || !title || !currentStep) {\r\n return null;\r\n }\r\n\r\n const showProgress = totalSteps > 1;\r\n\r\n return (\r\n <div className=\"sunboard-survey-overlay\">\r\n <section\r\n className=\"sunboard-survey-card\"\r\n role=\"dialog\"\r\n aria-modal=\"true\"\r\n aria-label={`${title} survey`}\r\n >\r\n <div className=\"sunboard-survey-head\">\r\n {subtitle ? <p className=\"sunboard-eyebrow\">{subtitle}</p> : null}\r\n <h2 className=\"sunboard-title\">{title}</h2>\r\n </div>\r\n\r\n {showProgress ? (\r\n <div className=\"sunboard-survey-progress-row\">\r\n <p className=\"sunboard-survey-stepcount\">\r\n Step {stepIndex + 1} of {totalSteps}\r\n </p>\r\n <div\r\n className=\"sunboard-progress\"\r\n role=\"progressbar\"\r\n aria-label={`${title} progress`}\r\n aria-valuemin={1}\r\n aria-valuemax={totalSteps}\r\n aria-valuenow={stepIndex + 1}\r\n >\r\n <div\r\n className=\"sunboard-progress-fill\"\r\n style={{ width: `${progress}%` }}\r\n />\r\n </div>\r\n </div>\r\n ) : null}\r\n\r\n <form\r\n className=\"sunboard-survey-form\"\r\n onSubmit={(event) => {\r\n event.preventDefault();\r\n next();\r\n }}\r\n >\r\n {currentStep.title ? (\r\n <p className=\"sunboard-survey-step-title\">{currentStep.title}</p>\r\n ) : null}\r\n\r\n <ol className=\"sunboard-survey-questions\">\r\n {currentStep.questions.map((question) => (\r\n <li className=\"sunboard-survey-question\" key={question.key}>\r\n <p className=\"sunboard-survey-prompt\">\r\n {question.prompt}\r\n {question.required ? (\r\n <span aria-hidden=\"true\" className=\"sunboard-survey-required\">\r\n {\" *\"}\r\n </span>\r\n ) : null}\r\n </p>\r\n\r\n {question.type === \"text\" ? (\r\n <input\r\n className=\"sunboard-survey-input\"\r\n type=\"text\"\r\n value={\r\n typeof answers[question.key] === \"string\"\r\n ? (answers[question.key] as string)\r\n : \"\"\r\n }\r\n required={question.required}\r\n aria-label={question.prompt}\r\n onChange={(event) =>\r\n setAnswer(question.key, event.target.value)\r\n }\r\n />\r\n ) : (\r\n <div\r\n className=\"sunboard-survey-options\"\r\n role=\"group\"\r\n aria-label={question.prompt}\r\n >\r\n {question.options.map((option) => {\r\n const selected =\r\n question.type === \"multi-select\"\r\n ? Array.isArray(answers[question.key]) &&\r\n (answers[question.key] as string[]).includes(\r\n option.value\r\n )\r\n : answers[question.key] === option.value;\r\n\r\n return (\r\n <button\r\n className=\"sunboard-survey-option\"\r\n type=\"button\"\r\n key={option.value}\r\n aria-pressed={selected}\r\n data-selected={selected ? \"true\" : \"false\"}\r\n onClick={() =>\r\n question.type === \"multi-select\"\r\n ? toggleAnswer(question.key, option.value)\r\n : setAnswer(question.key, option.value)\r\n }\r\n >\r\n {option.label}\r\n </button>\r\n );\r\n })}\r\n </div>\r\n )}\r\n </li>\r\n ))}\r\n </ol>\r\n\r\n {error ? (\r\n <p className=\"sunboard-survey-error\" role=\"alert\">\r\n {error}\r\n </p>\r\n ) : null}\r\n\r\n <div className=\"sunboard-survey-nav\">\r\n {!isFirstStep ? (\r\n <button\r\n className=\"sunboard-survey-back\"\r\n type=\"button\"\r\n onClick={back}\r\n >\r\n Back\r\n </button>\r\n ) : (\r\n <span />\r\n )}\r\n <button\r\n className=\"sunboard-survey-submit\"\r\n type=\"submit\"\r\n disabled={isLastStep ? !canSubmit : !canAdvance}\r\n >\r\n {isLastStep ? (submitting ? \"Submitting…\" : \"Finish\") : \"Continue\"}\r\n </button>\r\n </div>\r\n </form>\r\n </section>\r\n </div>\r\n );\r\n}\r\n\r\nexport const Sunboard = Object.assign(SunboardChecklist, {\r\n Checklist: SunboardChecklist,\r\n Survey: SunboardSurvey,\r\n Hotspots: SunboardHotspots,\r\n Tour: SunboardTour\r\n});\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["SunboardRuntimeClient","flat: FlatUser","current: unknown","interpolateOptional","next"],"sources":["../src/adapters/navigation.ts","../src/hooks/sunboard-context.ts","../src/version.ts","../src/components/SunboardProvider.tsx","../src/lib/interpolate-tokens.ts","../src/hooks/useSunboard.ts","../src/hooks/useChecklist.ts","../src/hooks/useSurvey.ts","../src/icons/index.tsx","../src/components/ChecklistStep.tsx","../src/hooks/useInterpolated.ts","../src/hooks/useTargetElement.ts","../src/components/SunboardHotspots.tsx","../src/components/SunboardTour.tsx","../src/components/Sunboard.tsx"],"sourcesContent":["export type NavigateCallback = (url: string) => void | Promise<void>;\n\nexport async function navigateWithFallback(\n url: string,\n navigate?: NavigateCallback\n): Promise<void> {\n if (navigate) {\n await navigate(url);\n return;\n }\n\n if (typeof window !== \"undefined\") {\n window.location.assign(url);\n }\n}\n","import { createContext } from \"react\";\nimport type {\n NormalizedSunboardSpec,\n SunboardHotspot,\n SunboardStep,\n SunboardTourStep,\n TrackEventName\n} from \"@sunboard/core\";\n\nexport type SunboardUser = {\n id: string;\n email?: string | null;\n group?: string | null;\n properties?: Record<string, unknown>;\n};\n\nexport type TrackProperties = Record<string, unknown>;\n\nexport type TrackResult =\n | { ok: true }\n | { ok: false; reason: \"not-ready\" | \"missing-user\" | \"network-error\" };\n\nexport type SurveyResult =\n | { ok: true }\n | {\n ok: false;\n reason: \"not-ready\" | \"missing-user\" | \"not-survey\" | \"network-error\";\n };\n\nexport type TourResult =\n | { ok: true }\n | { ok: false; reason: \"not-ready\" | \"missing-user\" | \"missing-tour\" };\n\nexport type ActiveSunboardTour = {\n key: string;\n name: string;\n step: SunboardTourStep;\n stepIndex: number;\n totalSteps: number;\n};\n\nexport type SunboardContextValue = {\n spec?: NormalizedSunboardSpec | null;\n user?: SunboardUser | null;\n loading: boolean;\n route?: string;\n completedStepKeys: ReadonlySet<string>;\n completedTourKeys: ReadonlySet<string>;\n completedCount: number;\n totalSteps: number;\n activeTour: ActiveSunboardTour | null;\n track: (\n eventName: TrackEventName,\n properties?: TrackProperties\n ) => Promise<TrackResult>;\n /**\n * Submit survey answers for the active survey experience. On success the\n * runtime re-bootstraps so the freshly collected attributes route the user\n * into the next experience in-session.\n */\n submitSurvey: (answers: Record<string, unknown>) => Promise<SurveyResult>;\n startTour: (tourKey: string) => Promise<TourResult>;\n /**\n * Close the active tour. User-initiated dismissals are reported to the\n * runtime API as a negative signal (\"the user bailed at this step\");\n * pass `silent: true` for programmatic closes (e.g. a missing tour\n * target) that say nothing about the user's intent.\n */\n dismissTour: (options?: { silent?: boolean }) => Promise<TourResult>;\n advanceTour: () => Promise<TourResult>;\n navigateToStep: (step: SunboardStep) => Promise<void>;\n startChecklistStep: (step: SunboardStep) => Promise<TourResult | void>;\n visibleHotspots: Array<{ key: string; hotspot: SunboardHotspot }>;\n};\n\nexport const SunboardContext = createContext<SunboardContextValue | null>(null);\n","export const SDK_VERSION = \"0.0.0\";\n","import {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n type Dispatch,\n type ReactNode,\n type SetStateAction\n} from \"react\";\nimport type {\n NormalizedSunboardSpec,\n SunboardStep,\n TrackEventName\n} from \"@sunboard/core\";\nimport {\n DEFAULT_RUNTIME_API_URL,\n SunboardRuntimeClient,\n type RuntimeExperience,\n type RuntimeProgress,\n type RuntimeSurvey\n} from \"@sunboard/core/runtime-api\";\nimport {\n navigateWithFallback,\n type NavigateCallback\n} from \"../adapters/navigation\";\nimport { routeMatches } from \"../adapters/routes\";\nimport {\n SunboardContext,\n type ActiveSunboardTour,\n type SunboardContextValue,\n type SunboardUser,\n type TrackProperties\n} from \"../hooks/sunboard-context\";\nimport { SDK_VERSION } from \"../version\";\n\nexport type SunboardProviderProps = {\n spec?: NormalizedSunboardSpec | null;\n user?: SunboardUser | null;\n loading?: boolean;\n route?: string;\n navigate?: NavigateCallback;\n /**\n * Override the runtime API host. Dev-only — leave unset in production so the\n * SDK targets the hosted Sunboard runtime API. Defaults to\n * `DEFAULT_RUNTIME_API_URL`.\n */\n apiUrl?: string;\n publishableKey?: string;\n onTrack?: (\n eventName: TrackEventName,\n properties?: TrackProperties\n ) => void | Promise<void>;\n children: ReactNode;\n};\n\ntype ActiveTourState = {\n key: string;\n stepIndex: number;\n};\n\ntype BootstrapState = {\n experience: RuntimeExperience | null;\n survey: RuntimeSurvey | null;\n attributes: Record<string, unknown> | null;\n loading: boolean;\n};\n\nexport function SunboardProvider({\n spec: specProp,\n user,\n loading: loadingProp,\n route,\n navigate,\n apiUrl,\n publishableKey,\n onTrack,\n children\n}: SunboardProviderProps) {\n const currentRoute = route ?? getBrowserRoute();\n const currentOrigin = getBrowserOrigin();\n const client = useMemo(() => {\n if (!publishableKey) {\n return null;\n }\n\n // `apiUrl` is a dev-only override; production apps only supply a key and\n // talk to the hosted runtime API by default.\n return new SunboardRuntimeClient({\n apiKey: publishableKey,\n apiUrl: apiUrl || DEFAULT_RUNTIME_API_URL,\n sdkVersion: SDK_VERSION\n });\n }, [apiUrl, publishableKey]);\n // Bump to force a bootstrap refetch in-session (e.g. after a survey submit,\n // so freshly collected attributes route the user without a navigation).\n const [bootstrapNonce, setBootstrapNonce] = useState(0);\n const refreshBootstrap = useCallback(() => {\n setBootstrapNonce((nonce) => nonce + 1);\n }, []);\n\n const bootstrap = useSunboardBootstrap(client, {\n enabled: !specProp,\n nonce: bootstrapNonce,\n origin: currentOrigin,\n route: currentRoute,\n user\n });\n\n const activeExperience = bootstrap.experience;\n // A survey pre-empts experiences (the server returns no experiences while one\n // is owed). Render the survey's spec when present, otherwise the experience's.\n const activeSurvey = bootstrap.survey;\n const hostedSpec = activeSurvey?.spec ?? activeExperience?.spec ?? null;\n const resolvedSpec = specProp ?? hostedSpec;\n const resolvedLoading = loadingProp ?? bootstrap.loading;\n const hostedMode = client !== null && !specProp;\n\n // The user view used for token interpolation/display merges the server-owned\n // attributes (the `onboarding.*` namespace returned by bootstrap) into the\n // host user, so `{{user.onboarding.*}}` resolves. Network calls keep using\n // the original `user` — owned attributes must never be echoed back as host\n // properties (which bootstrap overwrites).\n const ownedAttributes = bootstrap.attributes;\n const effectiveUser = useMemo<SunboardUser | null | undefined>(() => {\n if (!user || !ownedAttributes || Object.keys(ownedAttributes).length === 0) {\n return user;\n }\n\n return {\n ...user,\n properties: { ...(user.properties ?? {}), ...ownedAttributes }\n };\n }, [user, ownedAttributes]);\n\n const [completedStepKeys, setCompletedStepKeys] = useState<Set<string>>(\n () => new Set()\n );\n const [completedTourKeys, setCompletedTourKeys] = useState<Set<string>>(\n () => new Set()\n );\n const [activeTourState, setActiveTourState] = useState<ActiveTourState | null>(\n null\n );\n const lastNavigatedTourStep = useRef<string | null>(null);\n const storageKey =\n !hostedMode && resolvedSpec && user\n ? completedToursStorageKey(resolvedSpec.key, user.id)\n : null;\n const experienceKey =\n activeSurvey?.key ?? activeExperience?.key ?? resolvedSpec?.key ?? null;\n const versionId =\n activeSurvey?.version.id ?? activeExperience?.version.id ?? null;\n\n useEffect(() => {\n setCompletedStepKeys(new Set());\n setActiveTourState(null);\n lastNavigatedTourStep.current = null;\n\n if (!storageKey) {\n setCompletedTourKeys(new Set());\n return;\n }\n\n setCompletedTourKeys(readCompletedTours(storageKey));\n }, [resolvedSpec?.key, storageKey, user?.id]);\n\n useEffect(() => {\n if (!hostedMode || !activeExperience) {\n return;\n }\n\n applyProgress(\n activeExperience.progress,\n setCompletedStepKeys,\n setCompletedTourKeys\n );\n }, [activeExperience, hostedMode]);\n\n const effectiveCompletedStepKeys = useMemo(() => {\n if (!hostedMode || !activeExperience) {\n return completedStepKeys;\n }\n\n return mergeKeys(\n completedStepKeys,\n activeExperience.progress.completedStepKeys\n );\n }, [activeExperience, completedStepKeys, hostedMode]);\n\n const effectiveCompletedTourKeys = useMemo(() => {\n if (!hostedMode || !activeExperience) {\n return completedTourKeys;\n }\n\n return mergeKeys(\n completedTourKeys,\n activeExperience.progress.completedTourKeys\n );\n }, [activeExperience, completedTourKeys, hostedMode]);\n\n useEffect(() => {\n if (!resolvedSpec) {\n return;\n }\n\n const tourCompletedStepKeys = (resolvedSpec.checklist?.steps ?? [])\n .filter(\n (step) =>\n step.completeWhen?.tour &&\n effectiveCompletedTourKeys.has(step.completeWhen.tour)\n )\n .map((step) => step.key);\n\n if (tourCompletedStepKeys.length === 0) {\n return;\n }\n\n setCompletedStepKeys((current) => mergeKeys(current, tourCompletedStepKeys));\n }, [effectiveCompletedTourKeys, resolvedSpec]);\n\n const activeTour = useMemo<ActiveSunboardTour | null>(() => {\n if (!resolvedSpec || !activeTourState) {\n return null;\n }\n\n const tour = resolvedSpec.tours[activeTourState.key];\n const step = tour?.steps[activeTourState.stepIndex];\n\n if (!tour || !step) {\n return null;\n }\n\n return {\n key: activeTourState.key,\n name: tour.name,\n step,\n stepIndex: activeTourState.stepIndex,\n totalSteps: tour.steps.length\n };\n }, [activeTourState, resolvedSpec]);\n\n useEffect(() => {\n if (!activeTour?.step.route || routeMatches(activeTour.step.route, currentRoute)) {\n return;\n }\n\n const navigationKey = `${activeTour.key}:${activeTour.step.key}:${activeTour.step.route}`;\n\n if (lastNavigatedTourStep.current === navigationKey) {\n return;\n }\n\n lastNavigatedTourStep.current = navigationKey;\n void navigateWithFallback(activeTour.step.route, navigate);\n }, [activeTour, currentRoute, navigate]);\n\n // Record per-step tour progress. Fires once per (tour, step) transition;\n // the dep array tracks step.key + tour key so re-renders don't spam the\n // endpoint and re-entries refresh `lastSeenAt` server-side.\n useEffect(() => {\n if (\n !hostedMode ||\n !client ||\n !activeTour ||\n !user ||\n !experienceKey\n ) {\n return;\n }\n\n void client\n .recordTourStep({\n experienceKey,\n origin: currentOrigin,\n route: currentRoute,\n stepKey: activeTour.step.key,\n tourKey: activeTour.key,\n user: toRuntimeUser(user),\n versionId: versionId ?? undefined\n })\n .catch(() => {\n // Best-effort tracking — never break the host app on a 4xx/5xx.\n });\n // Intentionally narrow deps to the tour/step identity. We do not refire\n // when `currentRoute` changes mid-step, because the route adapter scrolls\n // the same popover into a new viewport, not a new logical step.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [activeTour?.key, activeTour?.step.key, client, currentOrigin, experienceKey, hostedMode, user?.id]);\n\n const totalSteps = resolvedSpec?.checklist?.steps.length ?? 0;\n const completedCount =\n resolvedSpec?.checklist?.steps.filter((step) =>\n effectiveCompletedStepKeys.has(step.key)\n ).length ?? 0;\n\n const completeTour = useCallback(\n (tourKey: string) => {\n setCompletedTourKeys((current) => {\n const next = new Set(current);\n next.add(tourKey);\n\n if (storageKey) {\n writeCompletedTours(storageKey, next);\n }\n\n return next;\n });\n },\n [storageKey]\n );\n\n const track = useCallback<SunboardContextValue[\"track\"]>(\n async (eventName, properties) => {\n if (resolvedLoading || !resolvedSpec) {\n return { ok: false, reason: \"not-ready\" };\n }\n\n if (!user) {\n return { ok: false, reason: \"missing-user\" };\n }\n\n const matchingStepKeys = (resolvedSpec.checklist?.steps ?? [])\n .filter((step) => step.completeWhen?.event === eventName)\n .map((step) => step.key);\n\n if (matchingStepKeys.length > 0) {\n // `mergeKeys` returns `current` unchanged when every key is already\n // present, so re-firing an already-completed step's event is a no-op\n // and does not churn state/recreate the context value. This is what\n // keeps a consumer that calls `track()` from an effect depending on the\n // whole context value from looping render→track→render.\n setCompletedStepKeys((current) => mergeKeys(current, matchingStepKeys));\n }\n\n let networkOk = true;\n\n if (hostedMode && client && experienceKey) {\n try {\n const response = await client.track({\n eventName,\n experienceKey,\n origin: currentOrigin,\n properties,\n route: currentRoute,\n user: toRuntimeUser(user),\n versionId: versionId ?? undefined\n });\n // A scoped track always returns progress; guard anyway since the\n // shared response type allows null for experience-agnostic tracks.\n if (response.progress) {\n applyProgress(\n response.progress,\n setCompletedStepKeys,\n setCompletedTourKeys\n );\n }\n } catch {\n networkOk = false;\n }\n }\n\n try {\n await onTrack?.(eventName, properties);\n } catch {\n return { ok: false, reason: \"network-error\" };\n }\n\n if (!networkOk) {\n return { ok: false, reason: \"network-error\" };\n }\n\n return { ok: true };\n },\n [\n client,\n currentRoute,\n currentOrigin,\n experienceKey,\n hostedMode,\n onTrack,\n resolvedLoading,\n resolvedSpec,\n user,\n versionId\n ]\n );\n\n const submitSurvey = useCallback<SunboardContextValue[\"submitSurvey\"]>(\n async (answers) => {\n if (resolvedLoading || !resolvedSpec) {\n return { ok: false, reason: \"not-ready\" };\n }\n\n if (!user) {\n return { ok: false, reason: \"missing-user\" };\n }\n\n if (resolvedSpec.type !== \"survey\" || !resolvedSpec.survey) {\n return { ok: false, reason: \"not-survey\" };\n }\n\n // Surveys are server-backed: a survey can only be answered when the\n // runtime resolved it for this user.\n if (!hostedMode || !client || !experienceKey) {\n return { ok: false, reason: \"not-ready\" };\n }\n\n try {\n await client.submitSurvey({\n answers,\n experienceKey,\n origin: currentOrigin,\n route: currentRoute,\n user: toRuntimeUser(user),\n versionId: versionId ?? undefined\n });\n } catch {\n return { ok: false, reason: \"network-error\" };\n }\n\n // Re-bootstrap so the freshly collected attributes route the user now.\n refreshBootstrap();\n return { ok: true };\n },\n [\n client,\n currentOrigin,\n currentRoute,\n experienceKey,\n hostedMode,\n refreshBootstrap,\n resolvedLoading,\n resolvedSpec,\n user,\n versionId\n ]\n );\n\n const navigateToStep = useCallback(\n async (step: SunboardStep) => {\n if (!step.route) {\n return;\n }\n\n await navigateWithFallback(step.route, navigate);\n },\n [navigate]\n );\n\n const startTour = useCallback<SunboardContextValue[\"startTour\"]>(\n async (tourKey) => {\n if (resolvedLoading || !resolvedSpec) {\n return { ok: false, reason: \"not-ready\" };\n }\n\n if (!user) {\n return { ok: false, reason: \"missing-user\" };\n }\n\n if (!resolvedSpec.tours[tourKey]) {\n return { ok: false, reason: \"missing-tour\" };\n }\n\n lastNavigatedTourStep.current = null;\n setActiveTourState({ key: tourKey, stepIndex: 0 });\n\n // Fire-and-forget tour-start. The per-step record fires from the\n // useEffect below when activeTour resolves, so step[0] gets recorded\n // independently. We deliberately don't await — popover UX must not\n // block on network.\n if (hostedMode && client && experienceKey && user) {\n void client\n .startTour({\n experienceKey,\n origin: currentOrigin,\n route: currentRoute,\n tourKey,\n user: toRuntimeUser(user),\n versionId: versionId ?? undefined\n })\n .catch(() => {\n // Tracking is best-effort. Network failures shouldn't surface\n // to the host app.\n });\n }\n\n return { ok: true };\n },\n [\n client,\n currentRoute,\n currentOrigin,\n experienceKey,\n hostedMode,\n resolvedLoading,\n resolvedSpec,\n user,\n versionId\n ]\n );\n\n const dismissTour = useCallback<SunboardContextValue[\"dismissTour\"]>(\n async (options) => {\n // Capture what was on screen before clearing state — the dismissal\n // report needs the tour + step the user actually bailed on.\n const dismissed = activeTourState;\n const dismissedStepKey = dismissed\n ? resolvedSpec?.tours[dismissed.key]?.steps[dismissed.stepIndex]?.key\n : undefined;\n\n setActiveTourState(null);\n lastNavigatedTourStep.current = null;\n\n // Best-effort negative-signal tracking, same contract as startTour:\n // never block the UI on network, never surface failures to the host.\n // Silent dismissals (missing target etc.) are not user intent and are\n // deliberately not reported.\n if (\n !options?.silent &&\n hostedMode &&\n client &&\n experienceKey &&\n user &&\n dismissed &&\n dismissedStepKey\n ) {\n void client\n .dismissTour({\n experienceKey,\n origin: currentOrigin,\n route: currentRoute,\n stepKey: dismissedStepKey,\n tourKey: dismissed.key,\n user: toRuntimeUser(user),\n versionId: versionId ?? undefined\n })\n .catch(() => {\n // Tracking is best-effort.\n });\n }\n\n return { ok: true };\n },\n [\n activeTourState,\n client,\n currentOrigin,\n currentRoute,\n experienceKey,\n hostedMode,\n resolvedSpec,\n user,\n versionId\n ]\n );\n\n const finishActiveTour = useCallback(\n async (tourKey: string) => {\n completeTour(tourKey);\n\n if (!hostedMode || !client || !experienceKey || !user) {\n return;\n }\n\n try {\n const response = await client.completeTour({\n experienceKey,\n origin: currentOrigin,\n route: currentRoute,\n tourKey,\n user: toRuntimeUser(user),\n versionId: versionId ?? undefined\n });\n applyProgress(\n response.progress,\n setCompletedStepKeys,\n setCompletedTourKeys\n );\n } catch {\n // Fail soft: keep local optimistic completion, never break the host app.\n }\n },\n [\n client,\n completeTour,\n currentRoute,\n currentOrigin,\n experienceKey,\n hostedMode,\n user,\n versionId\n ]\n );\n\n const advanceTour = useCallback<SunboardContextValue[\"advanceTour\"]>(\n async () => {\n if (resolvedLoading || !resolvedSpec || !activeTourState) {\n return { ok: false, reason: \"not-ready\" };\n }\n\n if (!user) {\n return { ok: false, reason: \"missing-user\" };\n }\n\n const tour = resolvedSpec.tours[activeTourState.key];\n\n if (!tour) {\n return { ok: false, reason: \"missing-tour\" };\n }\n\n lastNavigatedTourStep.current = null;\n\n if (activeTourState.stepIndex >= tour.steps.length - 1) {\n const completedKey = activeTourState.key;\n setActiveTourState(null);\n await finishActiveTour(completedKey);\n return { ok: true };\n }\n\n setActiveTourState({\n key: activeTourState.key,\n stepIndex: activeTourState.stepIndex + 1\n });\n return { ok: true };\n },\n [activeTourState, finishActiveTour, resolvedLoading, resolvedSpec, user]\n );\n\n const startChecklistStep = useCallback<\n SunboardContextValue[\"startChecklistStep\"]\n >(\n async (step) => {\n if (step.action?.type === \"startTour\") {\n return startTour(step.action.tour);\n }\n\n await navigateToStep(step);\n },\n [navigateToStep, startTour]\n );\n\n const visibleHotspots = useMemo<SunboardContextValue[\"visibleHotspots\"]>(() => {\n if (!resolvedSpec || activeTour) {\n return [];\n }\n\n return Object.entries(resolvedSpec.hotspots)\n .filter(([, hotspot]) => routeMatches(hotspot.route, currentRoute))\n .filter(\n ([, hotspot]) =>\n hotspot.repeatable ||\n !effectiveCompletedTourKeys.has(hotspot.action.tour)\n )\n .map(([key, hotspot]) => ({ key, hotspot }));\n }, [activeTour, effectiveCompletedTourKeys, currentRoute, resolvedSpec]);\n\n const value = useMemo<SunboardContextValue>(\n () => ({\n spec: resolvedSpec,\n user: effectiveUser,\n loading: resolvedLoading,\n route: currentRoute,\n completedStepKeys: effectiveCompletedStepKeys,\n completedTourKeys: effectiveCompletedTourKeys,\n completedCount,\n totalSteps,\n activeTour,\n track,\n submitSurvey,\n startTour,\n dismissTour,\n advanceTour,\n navigateToStep,\n startChecklistStep,\n visibleHotspots\n }),\n [\n resolvedSpec,\n effectiveUser,\n resolvedLoading,\n currentRoute,\n effectiveCompletedStepKeys,\n effectiveCompletedTourKeys,\n completedCount,\n totalSteps,\n activeTour,\n track,\n submitSurvey,\n startTour,\n dismissTour,\n advanceTour,\n navigateToStep,\n startChecklistStep,\n visibleHotspots\n ]\n );\n\n return (\n <SunboardContext.Provider value={value}>\n {children}\n </SunboardContext.Provider>\n );\n}\n\nfunction useSunboardBootstrap(\n client: SunboardRuntimeClient | null,\n options: {\n enabled: boolean;\n nonce: number;\n origin?: string;\n route: string;\n user?: SunboardUser | null;\n }\n): BootstrapState {\n const { enabled, nonce, origin, route, user } = options;\n const [state, setState] = useState<BootstrapState>({\n attributes: null,\n experience: null,\n loading: false,\n survey: null\n });\n const warnedSdkAdvisory = useRef(false);\n\n useEffect(() => {\n if (!client || !enabled || !user?.id) {\n setState({\n attributes: null,\n experience: null,\n loading: false,\n survey: null\n });\n return;\n }\n\n // TODO: plumb `controller.signal` through SunboardRuntimeClient.post into\n // the underlying fetch so aborted requests cancel at the network layer. In\n // React 19 dev StrictMode the provider mounts twice; the first effect's\n // fetch keeps flying and can surface as `TypeError: Failed to fetch` in the\n // browser console. Harmless (we guard with signal.aborted before touching\n // state) but noisy in dev.\n const controller = new AbortController();\n setState({\n attributes: null,\n experience: null,\n loading: true,\n survey: null\n });\n\n client\n .bootstrap({\n origin,\n route,\n user: toRuntimeUser(user)\n })\n .then((response) => {\n if (controller.signal.aborted) {\n return;\n }\n\n if (\n response.experiences.length === 0 &&\n !response.survey &&\n isDevEnvironment()\n ) {\n console.warn(\n `[sunboard] bootstrap returned no active experiences for route \"${route}\". Check the dashboard for an enabled deployment whose trigger.route matches (the publishable key selects the environment).`\n );\n }\n\n if (\n response.sdkAdvisory &&\n isDevEnvironment() &&\n !warnedSdkAdvisory.current\n ) {\n warnedSdkAdvisory.current = true;\n console.warn(`[sunboard] ${response.sdkAdvisory.message}`);\n }\n\n setState({\n attributes: response.attributes ?? null,\n experience: response.experiences[0] ?? null,\n loading: false,\n survey: response.survey ?? null\n });\n })\n .catch((error: unknown) => {\n if (controller.signal.aborted) {\n return;\n }\n\n if (isDevEnvironment()) {\n console.warn(\"[sunboard] bootstrap failed\", error);\n }\n\n setState({\n attributes: null,\n experience: null,\n loading: false,\n survey: null\n });\n });\n\n return () => {\n controller.abort();\n };\n // user.id is the identity key; we deliberately do not refetch when the\n // user object reference changes without an id change. `route` is\n // intentionally excluded so navigation does not wipe the active experience\n // — per-route visibility is handled by the runtime via step/tour/hotspot\n // route matching.\n // `nonce` is included so an explicit refreshBootstrap() (e.g. after a survey\n // submit) refetches even though client/user/route are unchanged.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [client, enabled, nonce, origin, user?.id]);\n\n return state;\n}\n\nfunction applyProgress(\n progress: RuntimeProgress,\n setSteps: Dispatch<SetStateAction<Set<string>>>,\n setTours: Dispatch<SetStateAction<Set<string>>>\n) {\n setSteps((current) => mergeKeys(current, progress.completedStepKeys));\n setTours((current) => mergeKeys(current, progress.completedTourKeys));\n}\n\nfunction mergeKeys(current: Set<string>, incoming: string[]): Set<string> {\n if (incoming.length === 0) {\n return current;\n }\n\n const next = new Set(current);\n\n for (const key of incoming) {\n next.add(key);\n }\n\n if (next.size === current.size) {\n return current;\n }\n\n return next;\n}\n\nfunction isDevEnvironment(): boolean {\n if (typeof process === \"undefined\" || !process.env) {\n return false;\n }\n\n return process.env.NODE_ENV !== \"production\";\n}\n\nfunction toRuntimeUser(user: SunboardUser) {\n return {\n email: user.email ?? undefined,\n group: user.group ?? undefined,\n id: user.id,\n properties: user.properties\n };\n}\n\nfunction completedToursStorageKey(specKey: string, userId: string): string {\n return `sunboard:${specKey}:${userId}:completed-tours`;\n}\n\nfunction getBrowserRoute(): string {\n if (typeof window === \"undefined\") {\n return \"/\";\n }\n\n return `${window.location.pathname}${window.location.search}`;\n}\n\nfunction getBrowserOrigin(): string | undefined {\n if (typeof window === \"undefined\") {\n return undefined;\n }\n\n return window.location.origin;\n}\n\nfunction readCompletedTours(storageKey: string): Set<string> {\n if (typeof window === \"undefined\") {\n return new Set();\n }\n\n try {\n const rawValue = window.localStorage.getItem(storageKey);\n const parsed = rawValue ? (JSON.parse(rawValue) as unknown) : [];\n\n if (!Array.isArray(parsed)) {\n return new Set();\n }\n\n return new Set(parsed.filter((value): value is string => typeof value === \"string\"));\n } catch {\n return new Set();\n }\n}\n\nfunction writeCompletedTours(storageKey: string, tourKeys: Set<string>) {\n if (typeof window === \"undefined\") {\n return;\n }\n\n try {\n window.localStorage.setItem(storageKey, JSON.stringify([...tourKeys].sort()));\n } catch {\n // Ignore storage failures; persistence should never break the host app.\n }\n}\n","import type { SunboardUser } from \"../hooks/sunboard-context\";\n\nconst TOKEN_PATTERN =\n /\\{\\{\\s*user\\.([\\w][\\w.]*)(?:\\s*\\|\\s*(?:\"([^\"]*)\"|'([^']*)'))?\\s*\\}\\}/g;\n\ntype FlatUser = Record<string, unknown>;\n\nfunction flattenUser(user: SunboardUser | null | undefined): FlatUser {\n if (!user) return {};\n const properties = (user.properties ?? {}) as Record<string, unknown>;\n const flat: FlatUser = { ...properties };\n flat.id = user.id;\n if (user.email !== undefined) flat.email = user.email;\n if (user.group !== undefined) flat.group = user.group;\n return flat;\n}\n\nfunction lookupPath(root: FlatUser, path: string): unknown {\n const segments = path.split(\".\");\n let current: unknown = root;\n for (const segment of segments) {\n if (current === null || current === undefined) return undefined;\n if (typeof current !== \"object\") return undefined;\n current = (current as Record<string, unknown>)[segment];\n }\n return current;\n}\n\nfunction coerceValue(value: unknown): string | null {\n if (value === null || value === undefined) return null;\n if (typeof value === \"string\") return value.length > 0 ? value : null;\n if (typeof value === \"number\" || typeof value === \"boolean\") {\n return String(value);\n }\n return null;\n}\n\nexport function interpolateTokens(\n template: string,\n user: SunboardUser | null | undefined\n): string {\n if (!template || !template.includes(\"{{\")) return template;\n const flat = flattenUser(user);\n return template.replace(\n TOKEN_PATTERN,\n (_match, path: string, doubleQuoted?: string, singleQuoted?: string) => {\n const resolved = coerceValue(lookupPath(flat, path));\n if (resolved !== null) return resolved;\n const fallback = doubleQuoted ?? singleQuoted;\n return fallback ?? \"\";\n }\n );\n}\n\nexport function extractUserTokens(template: string): string[] {\n if (!template || !template.includes(\"{{\")) return [];\n const paths: string[] = [];\n TOKEN_PATTERN.lastIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = TOKEN_PATTERN.exec(template)) !== null) {\n const path = match[1];\n if (path) paths.push(path);\n }\n return paths;\n}\n","import { useContext } from \"react\";\nimport { SunboardContext, type SunboardContextValue } from \"./sunboard-context\";\n\nexport function useSunboard(): SunboardContextValue {\n const context = useContext(SunboardContext);\n\n if (!context) {\n throw new Error(\"useSunboard must be used within SunboardProvider.\");\n }\n\n return context;\n}\n","import { useCallback, useMemo, useRef, useState } from \"react\";\r\nimport type { SunboardStep } from \"@sunboard/core\";\r\nimport { interpolateTokens } from \"../lib/interpolate-tokens\";\r\nimport { useSunboard } from \"./useSunboard\";\r\n\r\nexport type ChecklistViewStep = {\r\n key: string;\r\n title: string;\r\n description?: string;\r\n badge?: string;\r\n actionLabel?: string;\r\n};\r\n\r\nexport type ChecklistCompletion = {\r\n title: string;\r\n subtitle?: string;\r\n message?: string;\r\n dismissAfterMs?: number;\r\n};\r\n\r\nexport type UseChecklistResult = {\r\n visible: boolean;\r\n title?: string;\r\n subtitle?: string;\r\n steps: ChecklistViewStep[];\r\n completedStepKeys: ReadonlySet<string>;\r\n completedCount: number;\r\n totalSteps: number;\r\n remainingCount: number;\r\n progress: number;\r\n isCompleted: (key: string) => boolean;\r\n hasAction: (step: ChecklistViewStep) => boolean;\r\n startStep: (step: ChecklistViewStep) => void;\r\n completion?: ChecklistCompletion;\r\n dismissCompletion: () => void;\r\n};\r\n\r\nfunction actionLabelFor(step: SunboardStep): string | undefined {\r\n if (step.action?.type === \"startTour\") return \"Start\";\r\n if (step.action || step.route) return \"Open\";\r\n return undefined;\r\n}\r\n\r\nfunction interpolateOptional(\r\n text: string | undefined,\r\n user: Parameters<typeof interpolateTokens>[1]\r\n): string | undefined {\r\n if (!text) return undefined;\r\n const interpolated = interpolateTokens(text, user);\r\n return interpolated || undefined;\r\n}\r\n\r\nexport function useChecklist(): UseChecklistResult {\r\n const {\r\n spec,\r\n user,\r\n loading,\r\n completedStepKeys,\r\n completedCount,\r\n totalSteps,\r\n startChecklistStep\r\n } = useSunboard();\r\n\r\n const [dismissedKey, setDismissedKey] = useState<string | null>(null);\r\n\r\n // A guide renders whatever surfaces it actually declares, so the checklist\r\n // shows whenever the spec carries checklist steps — not based on the type.\r\n // (Surveys deliver through their own surface and never carry a checklist.)\r\n const hasChecklist = Boolean(spec && spec.checklist?.steps.length);\r\n const allCompleted = totalSteps > 0 && completedCount >= totalSteps;\r\n const ready = !loading && !!user && !!spec && hasChecklist;\r\n\r\n // The completion screen celebrates the *transition* from incomplete to\r\n // complete in this session — not the steady \"already done\" state. The server\r\n // already knows per-user completion via bootstrap, so a user who finished\r\n // on another device sees `allCompleted === true` from the first ready\r\n // render and we suppress the celebration. We only show it if we observed\r\n // them being incomplete first.\r\n //\r\n // Scoped to (spec, user) so switching either restarts the observation.\r\n const specKey = spec?.key ?? null;\r\n const userId = user?.id ?? null;\r\n const observationKey =\r\n ready && specKey && userId ? `${specKey}:${userId}` : null;\r\n const observationRef = useRef<{ key: string; sawIncomplete: boolean } | null>(\r\n null\r\n );\r\n if (observationKey && observationRef.current?.key !== observationKey) {\r\n observationRef.current = {\r\n key: observationKey,\r\n sawIncomplete: !allCompleted\r\n };\r\n } else if (observationKey && observationRef.current && !allCompleted) {\r\n observationRef.current.sawIncomplete = true;\r\n }\r\n const sawIncompleteFirst = observationRef.current?.sawIncomplete ?? false;\r\n\r\n const dismissed = dismissedKey !== null && dismissedKey === observationKey;\r\n\r\n const rawSteps = useMemo<SunboardStep[]>(\r\n () => spec?.checklist?.steps ?? [],\r\n [spec]\r\n );\r\n\r\n const title = useMemo(\r\n () => interpolateOptional(spec?.title, user ?? null),\r\n [spec?.title, user]\r\n );\r\n\r\n const subtitle = useMemo(\r\n () => interpolateOptional(spec?.subtitle, user ?? null),\r\n [spec?.subtitle, user]\r\n );\r\n\r\n const completion = useMemo<ChecklistCompletion | undefined>(() => {\r\n const raw = spec?.completion;\r\n if (!raw) return undefined;\r\n const interpolatedTitle = interpolateOptional(raw.title, user ?? null);\r\n if (!interpolatedTitle) return undefined;\r\n return {\r\n title: interpolatedTitle,\r\n subtitle: interpolateOptional(raw.subtitle, user ?? null),\r\n message: interpolateOptional(raw.message, user ?? null),\r\n dismissAfterMs: raw.dismissAfterMs\r\n };\r\n }, [spec?.completion, user]);\r\n\r\n const showingCompletion =\r\n ready && allCompleted && !!completion && sawIncompleteFirst && !dismissed;\r\n const visible = (ready && !allCompleted) || showingCompletion;\r\n\r\n const steps = useMemo<ChecklistViewStep[]>(() => {\r\n return rawSteps.map((step) => ({\r\n key: step.key,\r\n title: interpolateTokens(step.title, user ?? null),\r\n description: step.description\r\n ? interpolateTokens(step.description, user ?? null)\r\n : undefined,\r\n badge: step.badge\r\n ? interpolateTokens(step.badge, user ?? null)\r\n : undefined,\r\n actionLabel: actionLabelFor(step)\r\n }));\r\n }, [rawSteps, user]);\r\n\r\n const stepsByKey = useMemo(() => {\r\n const map = new Map<string, SunboardStep>();\r\n for (const step of rawSteps) {\r\n map.set(step.key, step);\r\n }\r\n return map;\r\n }, [rawSteps]);\r\n\r\n const remainingCount = Math.max(totalSteps - completedCount, 0);\r\n const progress =\r\n totalSteps > 0 ? Math.round((completedCount / totalSteps) * 100) : 0;\r\n\r\n const dismissCompletion = useCallback(() => {\r\n if (observationKey) {\r\n setDismissedKey(observationKey);\r\n }\r\n }, [observationKey]);\r\n\r\n return {\r\n visible,\r\n title,\r\n subtitle,\r\n steps,\r\n completedStepKeys,\r\n completedCount,\r\n totalSteps,\r\n remainingCount,\r\n progress,\r\n isCompleted: (key) => completedStepKeys.has(key),\r\n hasAction: (step) => step.actionLabel !== undefined,\r\n startStep: (step) => {\r\n const raw = stepsByKey.get(step.key);\r\n if (raw) {\r\n void startChecklistStep(raw);\r\n }\r\n },\r\n completion: showingCompletion ? completion : undefined,\r\n dismissCompletion\r\n };\r\n}\r\n","import { useCallback, useMemo, useRef, useState } from \"react\";\nimport type { SurveyQuestionType } from \"@sunboard/core\";\nimport { interpolateTokens } from \"../lib/interpolate-tokens\";\nimport { useSunboard } from \"./useSunboard\";\n\nexport type SurveyViewOption = {\n value: string;\n label: string;\n};\n\nexport type SurveyViewQuestion = {\n key: string;\n prompt: string;\n type: SurveyQuestionType;\n required: boolean;\n options: SurveyViewOption[];\n};\n\nexport type SurveyViewStep = {\n title?: string;\n subtitle?: string;\n questions: SurveyViewQuestion[];\n};\n\nexport type UseSurveyResult = {\n visible: boolean;\n title?: string;\n subtitle?: string;\n /** Every question across all steps — for all-at-once renderers (the built-in modal). */\n questions: SurveyViewQuestion[];\n /** The authored step grouping (one question per step by default). */\n steps: SurveyViewStep[];\n /** Hook-owned navigation. */\n stepIndex: number;\n totalSteps: number;\n currentStep?: SurveyViewStep;\n progress: number; // 0..100, by step\n isFirstStep: boolean;\n isLastStep: boolean;\n /** True when the current step's required questions are answered. */\n canAdvance: boolean;\n /** Advance to the next step, or submit on the last step. */\n next: () => void;\n back: () => void;\n answers: Record<string, unknown>;\n setAnswer: (key: string, value: string) => void;\n toggleAnswer: (key: string, value: string) => void;\n isAnswered: (key: string) => boolean;\n /** True when every required question (across all steps) is answered. */\n canSubmit: boolean;\n submitting: boolean;\n error: string | null;\n submit: () => Promise<void>;\n};\n\nfunction interpolateOptional(\n text: string | undefined,\n user: Parameters<typeof interpolateTokens>[1]\n): string | undefined {\n if (!text) return undefined;\n const interpolated = interpolateTokens(text, user);\n return interpolated || undefined;\n}\n\nfunction answered(value: unknown): boolean {\n if (value === undefined || value === null) return false;\n if (typeof value === \"string\") return value.trim().length > 0;\n if (Array.isArray(value)) return value.length > 0;\n return true;\n}\n\nexport function useSurvey(): UseSurveyResult {\n const { spec, user, loading, submitSurvey } = useSunboard();\n\n const [answers, setAnswers] = useState<Record<string, unknown>>({});\n const [submitting, setSubmitting] = useState(false);\n const [submitted, setSubmitted] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [stepIndex, setStepIndex] = useState(0);\n\n const isSurvey = Boolean(spec && spec.type === \"survey\" && spec.survey);\n const ready = !loading && !!user && isSurvey;\n\n const title = useMemo(\n () => interpolateOptional(spec?.title, user ?? null),\n [spec?.title, user]\n );\n const subtitle = useMemo(\n () => interpolateOptional(spec?.subtitle, user ?? null),\n [spec?.subtitle, user]\n );\n\n const steps = useMemo<SurveyViewStep[]>(() => {\n const rawSteps = spec?.survey?.steps ?? [];\n return rawSteps.map((step) => ({\n title: interpolateOptional(step.title, user ?? null),\n subtitle: interpolateOptional(step.subtitle, user ?? null),\n questions: step.questions.map((question) => ({\n key: question.key,\n prompt: interpolateTokens(question.prompt, user ?? null),\n type: question.type,\n required: question.required,\n options: (question.options ?? []).map((option) => ({\n value: option.value,\n label: interpolateTokens(option.label, user ?? null)\n }))\n }))\n }));\n }, [spec?.survey, user]);\n\n const questions = useMemo<SurveyViewQuestion[]>(\n () => steps.flatMap((step) => step.questions),\n [steps]\n );\n\n // Restart at the first step whenever the question set changes (a new survey).\n const signature = questions.map((question) => question.key).join(\"|\");\n const signatureRef = useRef(signature);\n if (signatureRef.current !== signature) {\n signatureRef.current = signature;\n if (stepIndex !== 0) setStepIndex(0);\n }\n\n const totalSteps = steps.length;\n const safeIndex =\n totalSteps > 0 ? Math.min(stepIndex, totalSteps - 1) : 0;\n const currentStep = steps[safeIndex];\n\n const setAnswer = useCallback((key: string, value: string) => {\n setAnswers((current) => ({ ...current, [key]: value }));\n }, []);\n\n const toggleAnswer = useCallback((key: string, value: string) => {\n setAnswers((current) => {\n const existing = Array.isArray(current[key])\n ? (current[key] as string[])\n : [];\n const next = existing.includes(value)\n ? existing.filter((item) => item !== value)\n : [...existing, value];\n return { ...current, [key]: next };\n });\n }, []);\n\n const isAnswered = useCallback(\n (key: string) => answered(answers[key]),\n [answers]\n );\n\n const canSubmit = useMemo(() => {\n if (!ready || submitting) return false;\n return questions\n .filter((question) => question.required)\n .every((question) => answered(answers[question.key]));\n }, [answers, questions, ready, submitting]);\n\n const canAdvance = useMemo(() => {\n if (!currentStep) return false;\n return currentStep.questions\n .filter((question) => question.required)\n .every((question) => answered(answers[question.key]));\n }, [answers, currentStep]);\n\n const submit = useCallback(async () => {\n if (!canSubmit) return;\n setSubmitting(true);\n setError(null);\n\n const result = await submitSurvey(answers);\n setSubmitting(false);\n\n if (result.ok) {\n setSubmitted(true);\n return;\n }\n\n setError(\n result.reason === \"network-error\"\n ? \"Something went wrong. Please try again.\"\n : \"Survey is not ready.\"\n );\n }, [answers, canSubmit, submitSurvey]);\n\n const isLastStep = totalSteps === 0 || safeIndex >= totalSteps - 1;\n const isFirstStep = safeIndex === 0;\n\n const next = useCallback(() => {\n if (isLastStep) {\n void submit();\n } else {\n setStepIndex(safeIndex + 1);\n }\n }, [isLastStep, safeIndex, submit]);\n\n const back = useCallback(() => {\n setStepIndex((current) => Math.max(0, current - 1));\n }, []);\n\n return {\n visible: ready && !submitted,\n title,\n subtitle,\n questions,\n steps,\n stepIndex: safeIndex,\n totalSteps,\n currentStep,\n progress: totalSteps > 0 ? ((safeIndex + 1) / totalSteps) * 100 : 0,\n isFirstStep,\n isLastStep,\n canAdvance,\n next,\n back,\n answers,\n setAnswer,\n toggleAnswer,\n isAnswered,\n canSubmit,\n submitting,\n error,\n submit\n };\n}\n","\r\nexport const CheckIcon = () => {\r\n return <svg xmlns=\"http://www.w3.org/2000/svg\"\r\n width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\"\r\n stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\"\r\n className=\"lucide lucide-check-icon lucide-check\"><path d=\"M20 6 9 17l-5-5\" /></svg>\r\n}","import { CheckIcon } from \"../icons\";\nimport type { ChecklistViewStep } from \"../hooks/useChecklist\";\n\ntype ChecklistStepProps = {\n completed: boolean;\n onStart: () => void;\n step: ChecklistViewStep;\n};\n\nexport function ChecklistStep({\n completed,\n onStart,\n step\n}: ChecklistStepProps) {\n const hasAction = step.actionLabel !== undefined;\n\n const status = (\n <span\n className=\"sunboard-step-status\"\n aria-label={completed ? \"Completed\" : \"Incomplete\"}\n >\n {completed ? <CheckIcon /> : null}\n </span>\n );\n\n const content = (\n <div className=\"sunboard-step-content\">\n <div className=\"sunboard-step-heading\">\n <h3 className=\"sunboard-step-title\">{step.title}</h3>\n {step.badge ? (\n <span className=\"sunboard-step-badge\">{step.badge}</span>\n ) : null}\n </div>\n\n {step.description ? (\n <p className=\"sunboard-step-description\">{step.description}</p>\n ) : null}\n </div>\n );\n\n return (\n <li className=\"sunboard-step\" data-completed={completed}>\n {hasAction ? (\n <button\n className=\"sunboard-step-row\"\n type=\"button\"\n onClick={onStart}\n aria-label={`${step.actionLabel} ${step.title}`}\n >\n {status}\n {content}\n </button>\n ) : (\n <div className=\"sunboard-step-row\">\n {status}\n {content}\n </div>\n )}\n </li>\n );\n}\n","import { useMemo } from \"react\";\nimport { interpolateTokens } from \"../lib/interpolate-tokens\";\nimport { useSunboard } from \"./useSunboard\";\n\nexport function useInterpolated(text: string): string;\nexport function useInterpolated(text: string | undefined): string | undefined;\nexport function useInterpolated(text: string | undefined): string | undefined {\n const { user } = useSunboard();\n return useMemo(() => {\n if (text === undefined) return undefined;\n return interpolateTokens(text, user);\n }, [text, user]);\n}\n","import { useEffect, useState } from \"react\";\n\nexport type TargetElementState = {\n element: HTMLElement | null;\n timedOut: boolean;\n};\n\nexport function useTargetElement(\n selector: string | null,\n enabled: boolean,\n timeoutMs = 5_000\n): TargetElementState {\n const [state, setState] = useState<TargetElementState>({\n element: null,\n timedOut: false\n });\n\n useEffect(() => {\n if (!enabled || !selector) {\n setState({ element: null, timedOut: false });\n return;\n }\n\n let cancelled = false;\n const startedAt = Date.now();\n\n const findTarget = () => {\n if (cancelled) {\n return;\n }\n\n const element = document.querySelector(selector);\n\n if (element instanceof HTMLElement) {\n setState({ element, timedOut: false });\n return;\n }\n\n if (Date.now() - startedAt >= timeoutMs) {\n setState({ element: null, timedOut: true });\n return;\n }\n\n window.setTimeout(findTarget, 100);\n };\n\n setState({ element: null, timedOut: false });\n findTarget();\n\n return () => {\n cancelled = true;\n };\n }, [enabled, selector, timeoutMs]);\n\n return state;\n}\n","import {\r\n FloatingPortal,\r\n autoUpdate,\r\n offset,\r\n useFloating\r\n} from \"@floating-ui/react\";\r\nimport { useEffect } from \"react\";\r\nimport type { SunboardHotspot } from \"@sunboard/core\";\r\nimport { useInterpolated } from \"../hooks/useInterpolated\";\r\nimport { useSunboard } from \"../hooks/useSunboard\";\r\nimport { useTargetElement } from \"../hooks/useTargetElement\";\r\n\r\ntype HotspotProps = {\r\n hotspot: SunboardHotspot;\r\n hotspotKey: string;\r\n};\r\n\r\nconst HOTSPOT_RADIUS_PX = 9;\r\n\r\nexport function SunboardHotspots() {\r\n const { visibleHotspots } = useSunboard();\r\n\r\n return (\r\n <>\r\n {visibleHotspots.map(({ key, hotspot }) => (\r\n <Hotspot hotspot={hotspot} hotspotKey={key} key={key} />\r\n ))}\r\n </>\r\n );\r\n}\r\n\r\nfunction Hotspot({ hotspot, hotspotKey }: HotspotProps) {\r\n const { startTour } = useSunboard();\r\n const label = useInterpolated(hotspot.label);\r\n const target = useTargetElement(hotspot.target, true);\r\n const { refs, floatingStyles } = useFloating({\r\n placement: hotspot.placement,\r\n strategy: \"fixed\",\r\n middleware: [offset(-HOTSPOT_RADIUS_PX)],\r\n whileElementsMounted: autoUpdate\r\n });\r\n\r\n useEffect(() => {\r\n if (!target.element) {\r\n return;\r\n }\r\n\r\n refs.setReference(target.element);\r\n }, [refs, target.element]);\r\n\r\n if (!target.element) {\r\n return null;\r\n }\r\n\r\n return (\r\n <FloatingPortal>\r\n <button\r\n aria-label={label ?? `Start ${hotspot.action.tour} tour`}\r\n className=\"sunboard-hotspot\"\r\n data-pulse={hotspot.pulse}\r\n data-sunboard-hotspot={hotspotKey}\r\n onClick={() => {\r\n void startTour(hotspot.action.tour);\r\n }}\r\n ref={refs.setFloating}\r\n style={floatingStyles}\r\n type=\"button\"\r\n >\r\n <span className=\"sunboard-hotspot-dot\" />\r\n </button>\r\n </FloatingPortal>\r\n );\r\n}\r\n","import {\r\n arrow,\r\n FloatingArrow,\r\n FloatingPortal,\r\n flip,\r\n offset,\r\n shift,\r\n useFloating\r\n} from \"@floating-ui/react\";\r\nimport { useEffect, useRef, useState } from \"react\";\r\nimport { routeMatches } from \"../adapters/routes\";\r\nimport { useInterpolated } from \"../hooks/useInterpolated\";\r\nimport { useSunboard } from \"../hooks/useSunboard\";\r\nimport { useTargetElement } from \"../hooks/useTargetElement\";\r\n\r\nexport function SunboardTour() {\r\n const { activeTour, advanceTour, dismissTour, route } = useSunboard();\r\n const arrowRef = useRef<SVGSVGElement | null>(null);\r\n const [spotlightRect, setSpotlightRect] = useState<DOMRect | null>(null);\r\n const routeReady = routeMatches(activeTour?.step.route, route ?? \"/\");\r\n const target = useTargetElement(\r\n activeTour?.step.target ?? null,\r\n Boolean(activeTour) && routeReady,\r\n 1_000\r\n );\r\n const { context, refs, floatingStyles, update } = useFloating({\r\n placement: activeTour?.step.placement ?? \"bottom\",\r\n middleware: [\r\n offset(16),\r\n flip(),\r\n shift({ padding: 12 }),\r\n arrow({ element: arrowRef })\r\n ]\r\n });\r\n\r\n useEffect(() => {\r\n if (!activeTour || !routeReady || !target.timedOut) {\r\n return;\r\n }\r\n\r\n // The target is missing and the user didn't reject anything, so close\r\n // programmatically without recording a dismissal signal.\r\n void dismissTour({ silent: true });\r\n }, [activeTour, dismissTour, routeReady, target.timedOut]);\r\n\r\n useEffect(() => {\r\n if (!activeTour || !target.element) {\r\n return;\r\n }\r\n\r\n refs.setReference(target.element);\r\n target.element.scrollIntoView({ block: \"center\", inline: \"center\" });\r\n void update();\r\n }, [activeTour, refs, target.element, update]);\r\n\r\n useEffect(() => {\r\n if (!activeTour || !target.element) {\r\n setSpotlightRect(null);\r\n return;\r\n }\r\n\r\n const updateSpotlight = () => {\r\n setSpotlightRect(target.element?.getBoundingClientRect() ?? null);\r\n };\r\n\r\n updateSpotlight();\r\n window.addEventListener(\"resize\", updateSpotlight);\r\n window.addEventListener(\"scroll\", updateSpotlight, true);\r\n\r\n target.element.setAttribute(\"data-sunboard-tour-target\", \"true\");\r\n\r\n return () => {\r\n window.removeEventListener(\"resize\", updateSpotlight);\r\n window.removeEventListener(\"scroll\", updateSpotlight, true);\r\n target.element?.removeAttribute(\"data-sunboard-tour-target\");\r\n setSpotlightRect(null);\r\n };\r\n }, [activeTour, target.element]);\r\n\r\n useEffect(() => {\r\n if (!activeTour || !target.element) {\r\n return;\r\n }\r\n\r\n const advanceOnTargetClick = () => {\r\n window.setTimeout(() => {\r\n void advanceTour();\r\n }, 0);\r\n };\r\n\r\n target.element.addEventListener(\"click\", advanceOnTargetClick);\r\n\r\n return () => {\r\n target.element?.removeEventListener(\"click\", advanceOnTargetClick);\r\n };\r\n }, [activeTour, advanceTour, target.element]);\r\n\r\n const tourName = useInterpolated(activeTour?.name);\r\n const stepTitle = useInterpolated(activeTour?.step.title);\r\n const stepBody = useInterpolated(activeTour?.step.body);\r\n\r\n if (!activeTour) {\r\n return null;\r\n }\r\n\r\n if (!routeReady || !target.element) {\r\n return null;\r\n }\r\n\r\n const isLastStep = activeTour.stepIndex === activeTour.totalSteps - 1;\r\n\r\n return (\r\n <FloatingPortal>\r\n <SunboardTourScrim rect={spotlightRect} />\r\n <section\r\n aria-label={`${tourName} tour step`}\r\n className=\"sunboard-tour\"\r\n ref={refs.setFloating}\r\n role=\"dialog\"\r\n style={floatingStyles}\r\n >\r\n <FloatingArrow\r\n className=\"sunboard-tour-arrow\"\r\n context={context}\r\n fill=\"var(--sunboard-tour-background)\"\r\n ref={arrowRef}\r\n stroke=\"var(--sunboard-tour-border)\"\r\n strokeWidth={1}\r\n tipRadius={2}\r\n />\r\n <div className=\"sunboard-tour-meta\">\r\n {activeTour.stepIndex + 1} / {activeTour.totalSteps}\r\n </div>\r\n <h2 className=\"sunboard-tour-title\">{stepTitle}</h2>\r\n {stepBody ? (\r\n <p className=\"sunboard-tour-body\">{stepBody}</p>\r\n ) : null}\r\n <div className=\"sunboard-tour-actions\">\r\n <button\r\n className=\"sunboard-tour-secondary\"\r\n onClick={() => void dismissTour()}\r\n type=\"button\"\r\n >\r\n Dismiss\r\n </button>\r\n <button\r\n className=\"sunboard-tour-primary\"\r\n onClick={() => void advanceTour()}\r\n type=\"button\"\r\n >\r\n {isLastStep ? \"Finish\" : \"Next\"}\r\n </button>\r\n </div>\r\n </section>\r\n </FloatingPortal>\r\n );\r\n}\r\n\r\nfunction SunboardTourScrim({ rect }: { rect: DOMRect | null }) {\r\n if (!rect) {\r\n return <div className=\"sunboard-tour-scrim\" />;\r\n }\r\n\r\n const padding = 8;\r\n const radius = 12;\r\n const left = Math.max(rect.left - padding, 0);\r\n const top = Math.max(rect.top - padding, 0);\r\n const width = rect.width + padding * 2;\r\n const height = rect.height + padding * 2;\r\n const maskId = \"sunboard-tour-spotlight-mask\";\r\n\r\n return (\r\n <svg\r\n aria-hidden=\"true\"\r\n className=\"sunboard-tour-scrim sunboard-tour-spotlight\"\r\n focusable=\"false\"\r\n >\r\n <defs>\r\n <mask id={maskId}>\r\n <rect fill=\"white\" height=\"100%\" width=\"100%\" x=\"0\" y=\"0\" />\r\n <rect\r\n fill=\"black\"\r\n height={height}\r\n rx={radius}\r\n ry={radius}\r\n width={width}\r\n x={left}\r\n y={top}\r\n />\r\n </mask>\r\n </defs>\r\n <rect\r\n fill=\"var(--sunboard-overlay-color)\"\r\n height=\"100%\"\r\n mask={`url(#${maskId})`}\r\n width=\"100%\"\r\n x=\"0\"\r\n y=\"0\"\r\n />\r\n </svg>\r\n );\r\n}\r\n","import { useEffect, useState } from \"react\";\r\nimport { useChecklist } from \"../hooks/useChecklist\";\r\nimport { useSurvey } from \"../hooks/useSurvey\";\r\nimport { ChecklistStep } from \"./ChecklistStep\";\r\nimport { SunboardHotspots } from \"./SunboardHotspots\";\r\nimport { SunboardTour } from \"./SunboardTour\";\r\n\r\nexport function SunboardChecklist() {\r\n const [open, setOpen] = useState(true);\r\n const {\r\n visible,\r\n title,\r\n subtitle,\r\n steps,\r\n completedCount,\r\n totalSteps,\r\n remainingCount,\r\n progress,\r\n isCompleted,\r\n startStep,\r\n completion,\r\n dismissCompletion\r\n } = useChecklist();\r\n\r\n // Auto-dismiss the completion screen after the spec-author-specified delay.\r\n // Lives in the widget rather than the hook so custom checklists can choose\r\n // their own timing UX (e.g. dismiss on tab change) without inheriting ours.\r\n useEffect(() => {\r\n if (!completion?.dismissAfterMs) return;\r\n const handle = window.setTimeout(dismissCompletion, completion.dismissAfterMs);\r\n return () => window.clearTimeout(handle);\r\n }, [completion, dismissCompletion]);\r\n\r\n if (!visible || !title) {\r\n return null;\r\n }\r\n\r\n if (completion) {\r\n return (\r\n <aside\r\n className=\"sunboard-root sunboard-root-completion\"\r\n aria-label={`${title} complete`}\r\n role=\"status\"\r\n aria-live=\"polite\"\r\n >\r\n <button\r\n className=\"sunboard-close sunboard-completion-close\"\r\n type=\"button\"\r\n onClick={dismissCompletion}\r\n aria-label=\"Dismiss completion message\"\r\n >\r\n x\r\n </button>\r\n <div className=\"sunboard-completion\">\r\n <div className=\"sunboard-completion-check\" aria-hidden=\"true\">\r\n <svg\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"3\"\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n >\r\n <polyline points=\"5 12 10 17 19 8\" />\r\n </svg>\r\n </div>\r\n {completion.subtitle ? (\r\n <p className=\"sunboard-completion-subtitle\">{completion.subtitle}</p>\r\n ) : null}\r\n <h2 className=\"sunboard-completion-title\">{completion.title}</h2>\r\n {completion.message ? (\r\n <p className=\"sunboard-completion-message\">{completion.message}</p>\r\n ) : null}\r\n </div>\r\n </aside>\r\n );\r\n }\r\n\r\n if (!open) {\r\n return (\r\n <button\r\n className=\"sunboard-launcher\"\r\n type=\"button\"\r\n onClick={() => setOpen(true)}\r\n aria-label={`Open ${title} checklist, ${remainingCount} tasks remaining`}\r\n >\r\n <span className=\"sunboard-launcher-count\">{remainingCount}</span>\r\n </button>\r\n );\r\n }\r\n\r\n return (\r\n <aside className=\"sunboard-root\" aria-label={`${title} checklist`}>\r\n <div className=\"sunboard-header\">\r\n <div>\r\n {subtitle ? <p className=\"sunboard-eyebrow\">{subtitle}</p> : null}\r\n <h2 className=\"sunboard-title\">{title}</h2>\r\n </div>\r\n <div className=\"sunboard-header-actions\">\r\n <p\r\n className=\"sunboard-progress-count\"\r\n aria-label=\"Checklist progress\"\r\n >\r\n {completedCount} / {totalSteps}\r\n </p>\r\n <button\r\n className=\"sunboard-close\"\r\n type=\"button\"\r\n onClick={() => setOpen(false)}\r\n aria-label={`Close ${title} checklist`}\r\n >\r\n x\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <div\r\n className=\"sunboard-progress\"\r\n role=\"progressbar\"\r\n aria-label={`${title} progress`}\r\n aria-valuemin={0}\r\n aria-valuemax={totalSteps}\r\n aria-valuenow={completedCount}\r\n >\r\n <div\r\n className=\"sunboard-progress-fill\"\r\n style={{ width: `${progress}%` }}\r\n />\r\n </div>\r\n\r\n <ol className=\"sunboard-step-list\">\r\n {steps.map((step) => (\r\n <ChecklistStep\r\n completed={isCompleted(step.key)}\r\n key={step.key}\r\n onStart={() => startStep(step)}\r\n step={step}\r\n />\r\n ))}\r\n </ol>\r\n </aside>\r\n );\r\n}\r\n\r\nexport function SunboardSurvey() {\r\n const {\r\n visible,\r\n title,\r\n subtitle,\r\n currentStep,\r\n stepIndex,\r\n totalSteps,\r\n progress,\r\n isFirstStep,\r\n isLastStep,\r\n canAdvance,\r\n canSubmit,\r\n next,\r\n back,\r\n answers,\r\n setAnswer,\r\n toggleAnswer,\r\n submitting,\r\n error\r\n } = useSurvey();\r\n\r\n if (!visible || !title || !currentStep) {\r\n return null;\r\n }\r\n\r\n const showProgress = totalSteps > 1;\r\n\r\n return (\r\n <div className=\"sunboard-survey-overlay\">\r\n <section\r\n className=\"sunboard-survey-card\"\r\n role=\"dialog\"\r\n aria-modal=\"true\"\r\n aria-label={`${title} survey`}\r\n >\r\n <div className=\"sunboard-survey-head\">\r\n {subtitle ? <p className=\"sunboard-eyebrow\">{subtitle}</p> : null}\r\n <h2 className=\"sunboard-title\">{title}</h2>\r\n </div>\r\n\r\n {showProgress ? (\r\n <div className=\"sunboard-survey-progress-row\">\r\n <p className=\"sunboard-survey-stepcount\">\r\n Step {stepIndex + 1} of {totalSteps}\r\n </p>\r\n <div\r\n className=\"sunboard-progress\"\r\n role=\"progressbar\"\r\n aria-label={`${title} progress`}\r\n aria-valuemin={1}\r\n aria-valuemax={totalSteps}\r\n aria-valuenow={stepIndex + 1}\r\n >\r\n <div\r\n className=\"sunboard-progress-fill\"\r\n style={{ width: `${progress}%` }}\r\n />\r\n </div>\r\n </div>\r\n ) : null}\r\n\r\n <form\r\n className=\"sunboard-survey-form\"\r\n onSubmit={(event) => {\r\n event.preventDefault();\r\n next();\r\n }}\r\n >\r\n {currentStep.title ? (\r\n <p className=\"sunboard-survey-step-title\">{currentStep.title}</p>\r\n ) : null}\r\n\r\n <ol className=\"sunboard-survey-questions\">\r\n {currentStep.questions.map((question) => (\r\n <li className=\"sunboard-survey-question\" key={question.key}>\r\n <p className=\"sunboard-survey-prompt\">\r\n {question.prompt}\r\n {question.required ? (\r\n <span aria-hidden=\"true\" className=\"sunboard-survey-required\">\r\n {\" *\"}\r\n </span>\r\n ) : null}\r\n </p>\r\n\r\n {question.type === \"text\" ? (\r\n <input\r\n className=\"sunboard-survey-input\"\r\n type=\"text\"\r\n value={\r\n typeof answers[question.key] === \"string\"\r\n ? (answers[question.key] as string)\r\n : \"\"\r\n }\r\n required={question.required}\r\n aria-label={question.prompt}\r\n onChange={(event) =>\r\n setAnswer(question.key, event.target.value)\r\n }\r\n />\r\n ) : (\r\n <div\r\n className=\"sunboard-survey-options\"\r\n role=\"group\"\r\n aria-label={question.prompt}\r\n >\r\n {question.options.map((option) => {\r\n const selected =\r\n question.type === \"multi-select\"\r\n ? Array.isArray(answers[question.key]) &&\r\n (answers[question.key] as string[]).includes(\r\n option.value\r\n )\r\n : answers[question.key] === option.value;\r\n\r\n return (\r\n <button\r\n className=\"sunboard-survey-option\"\r\n type=\"button\"\r\n key={option.value}\r\n aria-pressed={selected}\r\n data-selected={selected ? \"true\" : \"false\"}\r\n onClick={() =>\r\n question.type === \"multi-select\"\r\n ? toggleAnswer(question.key, option.value)\r\n : setAnswer(question.key, option.value)\r\n }\r\n >\r\n {option.label}\r\n </button>\r\n );\r\n })}\r\n </div>\r\n )}\r\n </li>\r\n ))}\r\n </ol>\r\n\r\n {error ? (\r\n <p className=\"sunboard-survey-error\" role=\"alert\">\r\n {error}\r\n </p>\r\n ) : null}\r\n\r\n <div className=\"sunboard-survey-nav\">\r\n {!isFirstStep ? (\r\n <button\r\n className=\"sunboard-survey-back\"\r\n type=\"button\"\r\n onClick={back}\r\n >\r\n Back\r\n </button>\r\n ) : (\r\n <span />\r\n )}\r\n <button\r\n className=\"sunboard-survey-submit\"\r\n type=\"submit\"\r\n disabled={isLastStep ? !canSubmit : !canAdvance}\r\n >\r\n {isLastStep ? (submitting ? \"Submitting…\" : \"Finish\") : \"Continue\"}\r\n </button>\r\n </div>\r\n </form>\r\n </section>\r\n </div>\r\n );\r\n}\r\n\r\nexport const Sunboard = Object.assign(SunboardChecklist, {\r\n Checklist: SunboardChecklist,\r\n Survey: SunboardSurvey,\r\n Hotspots: SunboardHotspots,\r\n Tour: SunboardTour\r\n});\r\n"],"mappings":";;;;;;;;AAEA,eAAsB,qBACpB,KACA,UACe;AACf,KAAI,UAAU;AACZ,QAAM,SAAS,IAAI;AACnB;;AAGF,KAAI,OAAO,WAAW,YACpB,QAAO,SAAS,OAAO,IAAI;;;;;AC+D/B,MAAa,kBAAkB,cAA2C,KAAK;;;;AC3E/E,MAAa,cAAc;;;;ACoE3B,SAAgB,iBAAiB,EAC/B,MAAM,UACN,MACA,SAAS,aACT,OACA,UACA,QACA,gBACA,SACA,YACwB;CACxB,MAAM,eAAe,SAAS,iBAAiB;CAC/C,MAAM,gBAAgB,kBAAkB;CACxC,MAAM,SAAS,cAAc;AAC3B,MAAI,CAAC,eACH,QAAO;AAKT,SAAO,IAAIA,wBAAsB;GAC/B,QAAQ;GACR,QAAQ,UAAU;GAClB,YAAY;GACb,CAAC;IACD,CAAC,QAAQ,eAAe,CAAC;CAG5B,MAAM,CAAC,gBAAgB,qBAAqB,SAAS,EAAE;CACvD,MAAM,mBAAmB,kBAAkB;AACzC,qBAAmB,UAAU,QAAQ,EAAE;IACtC,EAAE,CAAC;CAEN,MAAM,YAAY,qBAAqB,QAAQ;EAC7C,SAAS,CAAC;EACV,OAAO;EACP,QAAQ;EACR,OAAO;EACP;EACD,CAAC;CAEF,MAAM,mBAAmB,UAAU;CAGnC,MAAM,eAAe,UAAU;CAC/B,MAAM,aAAa,cAAc,QAAQ,kBAAkB,QAAQ;CACnE,MAAM,eAAe,YAAY;CACjC,MAAM,kBAAkB,eAAe,UAAU;CACjD,MAAM,aAAa,WAAW,QAAQ,CAAC;CAOvC,MAAM,kBAAkB,UAAU;CAClC,MAAM,gBAAgB,cAA+C;AACnE,MAAI,CAAC,QAAQ,CAAC,mBAAmB,OAAO,KAAK,gBAAgB,CAAC,WAAW,EACvE,QAAO;AAGT,SAAO;GACL,GAAG;GACH,YAAY;IAAE,GAAI,KAAK,cAAc,EAAE;IAAG,GAAG;IAAiB;GAC/D;IACA,CAAC,MAAM,gBAAgB,CAAC;CAE3B,MAAM,CAAC,mBAAmB,wBAAwB,+BAC1C,IAAI,KAAK,CAChB;CACD,MAAM,CAAC,mBAAmB,wBAAwB,+BAC1C,IAAI,KAAK,CAChB;CACD,MAAM,CAAC,iBAAiB,sBAAsB,SAC5C,KACD;CACD,MAAM,wBAAwB,OAAsB,KAAK;CACzD,MAAM,aACJ,CAAC,cAAc,gBAAgB,OAC3B,yBAAyB,aAAa,KAAK,KAAK,GAAG,GACnD;CACN,MAAM,gBACJ,cAAc,OAAO,kBAAkB,OAAO,cAAc,OAAO;CACrE,MAAM,YACJ,cAAc,QAAQ,MAAM,kBAAkB,QAAQ,MAAM;AAE9D,iBAAgB;AACd,uCAAqB,IAAI,KAAK,CAAC;AAC/B,qBAAmB,KAAK;AACxB,wBAAsB,UAAU;AAEhC,MAAI,CAAC,YAAY;AACf,wCAAqB,IAAI,KAAK,CAAC;AAC/B;;AAGF,uBAAqB,mBAAmB,WAAW,CAAC;IACnD;EAAC,cAAc;EAAK;EAAY,MAAM;EAAG,CAAC;AAE7C,iBAAgB;AACd,MAAI,CAAC,cAAc,CAAC,iBAClB;AAGF,gBACE,iBAAiB,UACjB,sBACA,qBACD;IACA,CAAC,kBAAkB,WAAW,CAAC;CAElC,MAAM,6BAA6B,cAAc;AAC/C,MAAI,CAAC,cAAc,CAAC,iBAClB,QAAO;AAGT,SAAO,UACL,mBACA,iBAAiB,SAAS,kBAC3B;IACA;EAAC;EAAkB;EAAmB;EAAW,CAAC;CAErD,MAAM,6BAA6B,cAAc;AAC/C,MAAI,CAAC,cAAc,CAAC,iBAClB,QAAO;AAGT,SAAO,UACL,mBACA,iBAAiB,SAAS,kBAC3B;IACA;EAAC;EAAkB;EAAmB;EAAW,CAAC;AAErD,iBAAgB;AACd,MAAI,CAAC,aACH;EAGF,MAAM,yBAAyB,aAAa,WAAW,SAAS,EAAE,EAC/D,QACE,SACC,KAAK,cAAc,QACnB,2BAA2B,IAAI,KAAK,aAAa,KAAK,CACzD,CACA,KAAK,SAAS,KAAK,IAAI;AAE1B,MAAI,sBAAsB,WAAW,EACnC;AAGF,wBAAsB,YAAY,UAAU,SAAS,sBAAsB,CAAC;IAC3E,CAAC,4BAA4B,aAAa,CAAC;CAE9C,MAAM,aAAa,cAAyC;AAC1D,MAAI,CAAC,gBAAgB,CAAC,gBACpB,QAAO;EAGT,MAAM,OAAO,aAAa,MAAM,gBAAgB;EAChD,MAAM,OAAO,MAAM,MAAM,gBAAgB;AAEzC,MAAI,CAAC,QAAQ,CAAC,KACZ,QAAO;AAGT,SAAO;GACL,KAAK,gBAAgB;GACrB,MAAM,KAAK;GACX;GACA,WAAW,gBAAgB;GAC3B,YAAY,KAAK,MAAM;GACxB;IACA,CAAC,iBAAiB,aAAa,CAAC;AAEnC,iBAAgB;AACd,MAAI,CAAC,YAAY,KAAK,SAAS,aAAa,WAAW,KAAK,OAAO,aAAa,CAC9E;EAGF,MAAM,gBAAgB,GAAG,WAAW,IAAI,GAAG,WAAW,KAAK,IAAI,GAAG,WAAW,KAAK;AAElF,MAAI,sBAAsB,YAAY,cACpC;AAGF,wBAAsB,UAAU;AAChC,EAAK,qBAAqB,WAAW,KAAK,OAAO,SAAS;IACzD;EAAC;EAAY;EAAc;EAAS,CAAC;AAKxC,iBAAgB;AACd,MACE,CAAC,cACD,CAAC,UACD,CAAC,cACD,CAAC,QACD,CAAC,cAED;AAGF,EAAK,OACF,eAAe;GACZ;GACF,QAAQ;GACR,OAAO;GACP,SAAS,WAAW,KAAK;GACzB,SAAS,WAAW;GACpB,MAAM,cAAc,KAAK;GACzB,WAAW,aAAa;GACzB,CAAC,CACD,YAAY,GAEX;IAKH;EAAC,YAAY;EAAK,YAAY,KAAK;EAAK;EAAQ;EAAe;EAAe;EAAY,MAAM;EAAG,CAAC;CAEvG,MAAM,aAAa,cAAc,WAAW,MAAM,UAAU;CAC5D,MAAM,iBACJ,cAAc,WAAW,MAAM,QAAQ,SACrC,2BAA2B,IAAI,KAAK,IAAI,CACzC,CAAC,UAAU;CAEd,MAAM,eAAe,aAClB,YAAoB;AACnB,wBAAsB,YAAY;GAChC,MAAM,OAAO,IAAI,IAAI,QAAQ;AAC7B,QAAK,IAAI,QAAQ;AAEjB,OAAI,WACF,qBAAoB,YAAY,KAAK;AAGvC,UAAO;IACP;IAEJ,CAAC,WAAW,CACb;CAED,MAAM,QAAQ,YACZ,OAAO,WAAW,eAAe;AAC/B,MAAI,mBAAmB,CAAC,aACtB,QAAO;GAAE,IAAI;GAAO,QAAQ;GAAa;AAG3C,MAAI,CAAC,KACH,QAAO;GAAE,IAAI;GAAO,QAAQ;GAAgB;EAG9C,MAAM,oBAAoB,aAAa,WAAW,SAAS,EAAE,EAC1D,QAAQ,SAAS,KAAK,cAAc,UAAU,UAAU,CACxD,KAAK,SAAS,KAAK,IAAI;AAE1B,MAAI,iBAAiB,SAAS,EAM5B,uBAAsB,YAAY,UAAU,SAAS,iBAAiB,CAAC;EAGzE,IAAI,YAAY;AAEhB,MAAI,cAAc,UAAU,cAC1B,KAAI;GACF,MAAM,WAAW,MAAM,OAAO,MAAM;IAClC;IACA;IACA,QAAQ;IACR;IACA,OAAO;IACP,MAAM,cAAc,KAAK;IACzB,WAAW,aAAa;IACzB,CAAC;AAGF,OAAI,SAAS,SACX,eACE,SAAS,UACT,sBACA,qBACD;UAEG;AACN,eAAY;;AAIhB,MAAI;AACF,SAAM,UAAU,WAAW,WAAW;UAChC;AACN,UAAO;IAAE,IAAI;IAAO,QAAQ;IAAiB;;AAG/C,MAAI,CAAC,UACH,QAAO;GAAE,IAAI;GAAO,QAAQ;GAAiB;AAG/C,SAAO,EAAE,IAAI,MAAM;IAErB;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CACF;CAED,MAAM,eAAe,YACnB,OAAO,YAAY;AACjB,MAAI,mBAAmB,CAAC,aACtB,QAAO;GAAE,IAAI;GAAO,QAAQ;GAAa;AAG3C,MAAI,CAAC,KACH,QAAO;GAAE,IAAI;GAAO,QAAQ;GAAgB;AAG9C,MAAI,aAAa,SAAS,YAAY,CAAC,aAAa,OAClD,QAAO;GAAE,IAAI;GAAO,QAAQ;GAAc;AAK5C,MAAI,CAAC,cAAc,CAAC,UAAU,CAAC,cAC7B,QAAO;GAAE,IAAI;GAAO,QAAQ;GAAa;AAG3C,MAAI;AACF,SAAM,OAAO,aAAa;IACxB;IACA;IACA,QAAQ;IACR,OAAO;IACP,MAAM,cAAc,KAAK;IACzB,WAAW,aAAa;IACzB,CAAC;UACI;AACN,UAAO;IAAE,IAAI;IAAO,QAAQ;IAAiB;;AAI/C,oBAAkB;AAClB,SAAO,EAAE,IAAI,MAAM;IAErB;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CACF;CAED,MAAM,iBAAiB,YACrB,OAAO,SAAuB;AAC5B,MAAI,CAAC,KAAK,MACR;AAGF,QAAM,qBAAqB,KAAK,OAAO,SAAS;IAElD,CAAC,SAAS,CACX;CAED,MAAM,YAAY,YAChB,OAAO,YAAY;AACjB,MAAI,mBAAmB,CAAC,aACtB,QAAO;GAAE,IAAI;GAAO,QAAQ;GAAa;AAG3C,MAAI,CAAC,KACH,QAAO;GAAE,IAAI;GAAO,QAAQ;GAAgB;AAG9C,MAAI,CAAC,aAAa,MAAM,SACtB,QAAO;GAAE,IAAI;GAAO,QAAQ;GAAgB;AAG9C,wBAAsB,UAAU;AAChC,qBAAmB;GAAE,KAAK;GAAS,WAAW;GAAG,CAAC;AAMlD,MAAI,cAAc,UAAU,iBAAiB,KAC3C,CAAK,OACF,UAAU;GACT;GACA,QAAQ;GACR,OAAO;GACP;GACA,MAAM,cAAc,KAAK;GACzB,WAAW,aAAa;GACzB,CAAC,CACD,YAAY,GAGX;AAGN,SAAO,EAAE,IAAI,MAAM;IAErB;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CACF;CAED,MAAM,cAAc,YAClB,OAAO,YAAY;EAGjB,MAAM,YAAY;EAClB,MAAM,mBAAmB,YACrB,cAAc,MAAM,UAAU,MAAM,MAAM,UAAU,YAAY,MAChE;AAEJ,qBAAmB,KAAK;AACxB,wBAAsB,UAAU;AAMhC,MACE,CAAC,SAAS,UACV,cACA,UACA,iBACA,QACA,aACA,iBAEA,CAAK,OACF,YAAY;GACX;GACA,QAAQ;GACR,OAAO;GACP,SAAS;GACT,SAAS,UAAU;GACnB,MAAM,cAAc,KAAK;GACzB,WAAW,aAAa;GACzB,CAAC,CACD,YAAY,GAEX;AAGN,SAAO,EAAE,IAAI,MAAM;IAErB;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CACF;CAED,MAAM,mBAAmB,YACvB,OAAO,YAAoB;AACzB,eAAa,QAAQ;AAErB,MAAI,CAAC,cAAc,CAAC,UAAU,CAAC,iBAAiB,CAAC,KAC/C;AAGF,MAAI;AASF,kBARiB,MAAM,OAAO,aAAa;IACrC;IACJ,QAAQ;IACR,OAAO;IACP;IACA,MAAM,cAAc,KAAK;IACzB,WAAW,aAAa;IACzB,CAAC,EAES,UACT,sBACA,qBACD;UACK;IAIV;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CACF;CAED,MAAM,cAAc,YAClB,YAAY;AACV,MAAI,mBAAmB,CAAC,gBAAgB,CAAC,gBACvC,QAAO;GAAE,IAAI;GAAO,QAAQ;GAAa;AAG3C,MAAI,CAAC,KACH,QAAO;GAAE,IAAI;GAAO,QAAQ;GAAgB;EAG9C,MAAM,OAAO,aAAa,MAAM,gBAAgB;AAEhD,MAAI,CAAC,KACH,QAAO;GAAE,IAAI;GAAO,QAAQ;GAAgB;AAG9C,wBAAsB,UAAU;AAEhC,MAAI,gBAAgB,aAAa,KAAK,MAAM,SAAS,GAAG;GACtD,MAAM,eAAe,gBAAgB;AACrC,sBAAmB,KAAK;AACxB,SAAM,iBAAiB,aAAa;AACpC,UAAO,EAAE,IAAI,MAAM;;AAGrB,qBAAmB;GACjB,KAAK,gBAAgB;GACrB,WAAW,gBAAgB,YAAY;GACxC,CAAC;AACF,SAAO,EAAE,IAAI,MAAM;IAErB;EAAC;EAAiB;EAAkB;EAAiB;EAAc;EAAK,CACzE;CAED,MAAM,qBAAqB,YAGzB,OAAO,SAAS;AACd,MAAI,KAAK,QAAQ,SAAS,YACxB,QAAO,UAAU,KAAK,OAAO,KAAK;AAGpC,QAAM,eAAe,KAAK;IAE5B,CAAC,gBAAgB,UAAU,CAC5B;CAED,MAAM,kBAAkB,cAAuD;AAC7E,MAAI,CAAC,gBAAgB,WACnB,QAAO,EAAE;AAGX,SAAO,OAAO,QAAQ,aAAa,SAAS,CACzC,QAAQ,GAAG,aAAa,aAAa,QAAQ,OAAO,aAAa,CAAC,CAClE,QACE,GAAG,aACF,QAAQ,cACR,CAAC,2BAA2B,IAAI,QAAQ,OAAO,KAAK,CACvD,CACA,KAAK,CAAC,KAAK,cAAc;GAAE;GAAK;GAAS,EAAE;IAC7C;EAAC;EAAY;EAA4B;EAAc;EAAa,CAAC;CAExE,MAAM,QAAQ,eACL;EACL,MAAM;EACN,MAAM;EACN,SAAS;EACT,OAAO;EACP,mBAAmB;EACnB,mBAAmB;EACnB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,GACD;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CACF;AAED,QACE,oBAAC,gBAAgB;EAAgB;EAC9B;GACwB;;AAI/B,SAAS,qBACP,QACA,SAOgB;CAChB,MAAM,EAAE,SAAS,OAAO,QAAQ,OAAO,SAAS;CAChD,MAAM,CAAC,OAAO,YAAY,SAAyB;EACjD,YAAY;EACZ,YAAY;EACZ,SAAS;EACT,QAAQ;EACT,CAAC;CACF,MAAM,oBAAoB,OAAO,MAAM;AAEvC,iBAAgB;AACd,MAAI,CAAC,UAAU,CAAC,WAAW,CAAC,MAAM,IAAI;AACpC,YAAS;IACP,YAAY;IACZ,YAAY;IACZ,SAAS;IACT,QAAQ;IACT,CAAC;AACF;;EASF,MAAM,aAAa,IAAI,iBAAiB;AACxC,WAAS;GACP,YAAY;GACZ,YAAY;GACZ,SAAS;GACT,QAAQ;GACT,CAAC;AAEF,SACG,UAAU;GACP;GACF;GACA,MAAM,cAAc,KAAK;GAC1B,CAAC,CACD,MAAM,aAAa;AAClB,OAAI,WAAW,OAAO,QACpB;AAGF,OACE,SAAS,YAAY,WAAW,KAChC,CAAC,SAAS,UACV,kBAAkB,CAElB,SAAQ,KACN,kEAAkE,MAAM,6HACzE;AAGH,OACE,SAAS,eACT,kBAAkB,IAClB,CAAC,kBAAkB,SACnB;AACA,sBAAkB,UAAU;AAC5B,YAAQ,KAAK,cAAc,SAAS,YAAY,UAAU;;AAG5D,YAAS;IACP,YAAY,SAAS,cAAc;IACnC,YAAY,SAAS,YAAY,MAAM;IACvC,SAAS;IACT,QAAQ,SAAS,UAAU;IAC5B,CAAC;IACF,CACD,OAAO,UAAmB;AACzB,OAAI,WAAW,OAAO,QACpB;AAGF,OAAI,kBAAkB,CACpB,SAAQ,KAAK,+BAA+B,MAAM;AAGpD,YAAS;IACP,YAAY;IACZ,YAAY;IACZ,SAAS;IACT,QAAQ;IACT,CAAC;IACF;AAEJ,eAAa;AACX,cAAW,OAAO;;IAUnB;EAAC;EAAQ;EAAS;EAAO;EAAQ,MAAM;EAAG,CAAC;AAE9C,QAAO;;AAGT,SAAS,cACP,UACA,UACA,UACA;AACA,WAAU,YAAY,UAAU,SAAS,SAAS,kBAAkB,CAAC;AACrE,WAAU,YAAY,UAAU,SAAS,SAAS,kBAAkB,CAAC;;AAGvE,SAAS,UAAU,SAAsB,UAAiC;AACxE,KAAI,SAAS,WAAW,EACtB,QAAO;CAGT,MAAM,OAAO,IAAI,IAAI,QAAQ;AAE7B,MAAK,MAAM,OAAO,SAChB,MAAK,IAAI,IAAI;AAGf,KAAI,KAAK,SAAS,QAAQ,KACxB,QAAO;AAGT,QAAO;;AAGT,SAAS,mBAA4B;AACnC,KAAI,OAAO,YAAY,eAAe,CAAC,QAAQ,IAC7C,QAAO;AAGT,QAAO,QAAQ,IAAI,aAAa;;AAGlC,SAAS,cAAc,MAAoB;AACzC,QAAO;EACL,OAAO,KAAK,SAAS;EACrB,OAAO,KAAK,SAAS;EACrB,IAAI,KAAK;EACT,YAAY,KAAK;EAClB;;AAGH,SAAS,yBAAyB,SAAiB,QAAwB;AACzE,QAAO,YAAY,QAAQ,GAAG,OAAO;;AAGvC,SAAS,kBAA0B;AACjC,KAAI,OAAO,WAAW,YACpB,QAAO;AAGT,QAAO,GAAG,OAAO,SAAS,WAAW,OAAO,SAAS;;AAGvD,SAAS,mBAAuC;AAC9C,KAAI,OAAO,WAAW,YACpB;AAGF,QAAO,OAAO,SAAS;;AAGzB,SAAS,mBAAmB,YAAiC;AAC3D,KAAI,OAAO,WAAW,YACpB,wBAAO,IAAI,KAAK;AAGlB,KAAI;EACF,MAAM,WAAW,OAAO,aAAa,QAAQ,WAAW;EACxD,MAAM,SAAS,WAAY,KAAK,MAAM,SAAS,GAAe,EAAE;AAEhE,MAAI,CAAC,MAAM,QAAQ,OAAO,CACxB,wBAAO,IAAI,KAAK;AAGlB,SAAO,IAAI,IAAI,OAAO,QAAQ,UAA2B,OAAO,UAAU,SAAS,CAAC;SAC9E;AACN,yBAAO,IAAI,KAAK;;;AAIpB,SAAS,oBAAoB,YAAoB,UAAuB;AACtE,KAAI,OAAO,WAAW,YACpB;AAGF,KAAI;AACF,SAAO,aAAa,QAAQ,YAAY,KAAK,UAAU,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;SACvE;;;;;AC34BV,MAAM,gBACJ;AAIF,SAAS,YAAY,MAAiD;AACpE,KAAI,CAAC,KAAM,QAAO,EAAE;CAEpB,MAAMC,OAAiB,EAAE,GADL,KAAK,cAAc,EAAE,EACD;AACxC,MAAK,KAAK,KAAK;AACf,KAAI,KAAK,UAAU,OAAW,MAAK,QAAQ,KAAK;AAChD,KAAI,KAAK,UAAU,OAAW,MAAK,QAAQ,KAAK;AAChD,QAAO;;AAGT,SAAS,WAAW,MAAgB,MAAuB;CACzD,MAAM,WAAW,KAAK,MAAM,IAAI;CAChC,IAAIC,UAAmB;AACvB,MAAK,MAAM,WAAW,UAAU;AAC9B,MAAI,YAAY,QAAQ,YAAY,OAAW,QAAO;AACtD,MAAI,OAAO,YAAY,SAAU,QAAO;AACxC,YAAW,QAAoC;;AAEjD,QAAO;;AAGT,SAAS,YAAY,OAA+B;AAClD,KAAI,UAAU,QAAQ,UAAU,OAAW,QAAO;AAClD,KAAI,OAAO,UAAU,SAAU,QAAO,MAAM,SAAS,IAAI,QAAQ;AACjE,KAAI,OAAO,UAAU,YAAY,OAAO,UAAU,UAChD,QAAO,OAAO,MAAM;AAEtB,QAAO;;AAGT,SAAgB,kBACd,UACA,MACQ;AACR,KAAI,CAAC,YAAY,CAAC,SAAS,SAAS,KAAK,CAAE,QAAO;CAClD,MAAM,OAAO,YAAY,KAAK;AAC9B,QAAO,SAAS,QACd,gBACC,QAAQ,MAAc,cAAuB,iBAA0B;EACtE,MAAM,WAAW,YAAY,WAAW,MAAM,KAAK,CAAC;AACpD,MAAI,aAAa,KAAM,QAAO;AAE9B,SADiB,gBAAgB,gBACd;GAEtB;;;;;AChDH,SAAgB,cAAoC;CAClD,MAAM,UAAU,WAAW,gBAAgB;AAE3C,KAAI,CAAC,QACH,OAAM,IAAI,MAAM,oDAAoD;AAGtE,QAAO;;;;;AC2BT,SAAS,eAAe,MAAwC;AAC9D,KAAI,KAAK,QAAQ,SAAS,YAAa,QAAO;AAC9C,KAAI,KAAK,UAAU,KAAK,MAAO,QAAO;;AAIxC,SAASC,sBACP,MACA,MACoB;AACpB,KAAI,CAAC,KAAM,QAAO;AAElB,QADqB,kBAAkB,MAAM,KAAK,IAC3B;;AAGzB,SAAgB,eAAmC;CACjD,MAAM,EACJ,MACA,MACA,SACA,mBACA,gBACA,YACA,uBACE,aAAa;CAEjB,MAAM,CAAC,cAAc,mBAAmB,SAAwB,KAAK;CAKrE,MAAM,eAAe,QAAQ,QAAQ,KAAK,WAAW,MAAM,OAAO;CAClE,MAAM,eAAe,aAAa,KAAK,kBAAkB;CACzD,MAAM,QAAQ,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,CAAC,QAAQ;CAU9C,MAAM,UAAU,MAAM,OAAO;CAC7B,MAAM,SAAS,MAAM,MAAM;CAC3B,MAAM,iBACJ,SAAS,WAAW,SAAS,GAAG,QAAQ,GAAG,WAAW;CACxD,MAAM,iBAAiB,OACrB,KACD;AACD,KAAI,kBAAkB,eAAe,SAAS,QAAQ,eACpD,gBAAe,UAAU;EACvB,KAAK;EACL,eAAe,CAAC;EACjB;UACQ,kBAAkB,eAAe,WAAW,CAAC,aACtD,gBAAe,QAAQ,gBAAgB;CAEzC,MAAM,qBAAqB,eAAe,SAAS,iBAAiB;CAEpE,MAAM,YAAY,iBAAiB,QAAQ,iBAAiB;CAE5D,MAAM,WAAW,cACT,MAAM,WAAW,SAAS,EAAE,EAClC,CAAC,KAAK,CACP;CAED,MAAM,QAAQ,cACNA,sBAAoB,MAAM,OAAO,QAAQ,KAAK,EACpD,CAAC,MAAM,OAAO,KAAK,CACpB;CAED,MAAM,WAAW,cACTA,sBAAoB,MAAM,UAAU,QAAQ,KAAK,EACvD,CAAC,MAAM,UAAU,KAAK,CACvB;CAED,MAAM,aAAa,cAA+C;EAChE,MAAM,MAAM,MAAM;AAClB,MAAI,CAAC,IAAK,QAAO;EACjB,MAAM,oBAAoBA,sBAAoB,IAAI,OAAO,QAAQ,KAAK;AACtE,MAAI,CAAC,kBAAmB,QAAO;AAC/B,SAAO;GACL,OAAO;GACP,UAAUA,sBAAoB,IAAI,UAAU,QAAQ,KAAK;GACzD,SAASA,sBAAoB,IAAI,SAAS,QAAQ,KAAK;GACvD,gBAAgB,IAAI;GACrB;IACA,CAAC,MAAM,YAAY,KAAK,CAAC;CAE5B,MAAM,oBACJ,SAAS,gBAAgB,CAAC,CAAC,cAAc,sBAAsB,CAAC;CAClE,MAAM,UAAW,SAAS,CAAC,gBAAiB;CAE5C,MAAM,QAAQ,cAAmC;AAC/C,SAAO,SAAS,KAAK,UAAU;GAC7B,KAAK,KAAK;GACV,OAAO,kBAAkB,KAAK,OAAO,QAAQ,KAAK;GAClD,aAAa,KAAK,cACd,kBAAkB,KAAK,aAAa,QAAQ,KAAK,GACjD;GACJ,OAAO,KAAK,QACR,kBAAkB,KAAK,OAAO,QAAQ,KAAK,GAC3C;GACJ,aAAa,eAAe,KAAK;GAClC,EAAE;IACF,CAAC,UAAU,KAAK,CAAC;CAEpB,MAAM,aAAa,cAAc;EAC/B,MAAM,sBAAM,IAAI,KAA2B;AAC3C,OAAK,MAAM,QAAQ,SACjB,KAAI,IAAI,KAAK,KAAK,KAAK;AAEzB,SAAO;IACN,CAAC,SAAS,CAAC;CAEd,MAAM,iBAAiB,KAAK,IAAI,aAAa,gBAAgB,EAAE;CAC/D,MAAM,WACJ,aAAa,IAAI,KAAK,MAAO,iBAAiB,aAAc,IAAI,GAAG;CAErE,MAAM,oBAAoB,kBAAkB;AAC1C,MAAI,eACF,iBAAgB,eAAe;IAEhC,CAAC,eAAe,CAAC;AAEpB,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,cAAc,QAAQ,kBAAkB,IAAI,IAAI;EAChD,YAAY,SAAS,KAAK,gBAAgB;EAC1C,YAAY,SAAS;GACnB,MAAM,MAAM,WAAW,IAAI,KAAK,IAAI;AACpC,OAAI,IACF,CAAK,mBAAmB,IAAI;;EAGhC,YAAY,oBAAoB,aAAa;EAC7C;EACD;;;;;AChIH,SAAS,oBACP,MACA,MACoB;AACpB,KAAI,CAAC,KAAM,QAAO;AAElB,QADqB,kBAAkB,MAAM,KAAK,IAC3B;;AAGzB,SAAS,SAAS,OAAyB;AACzC,KAAI,UAAU,UAAa,UAAU,KAAM,QAAO;AAClD,KAAI,OAAO,UAAU,SAAU,QAAO,MAAM,MAAM,CAAC,SAAS;AAC5D,KAAI,MAAM,QAAQ,MAAM,CAAE,QAAO,MAAM,SAAS;AAChD,QAAO;;AAGT,SAAgB,YAA6B;CAC3C,MAAM,EAAE,MAAM,MAAM,SAAS,iBAAiB,aAAa;CAE3D,MAAM,CAAC,SAAS,cAAc,SAAkC,EAAE,CAAC;CACnE,MAAM,CAAC,YAAY,iBAAiB,SAAS,MAAM;CACnD,MAAM,CAAC,WAAW,gBAAgB,SAAS,MAAM;CACjD,MAAM,CAAC,OAAO,YAAY,SAAwB,KAAK;CACvD,MAAM,CAAC,WAAW,gBAAgB,SAAS,EAAE;CAE7C,MAAM,WAAW,QAAQ,QAAQ,KAAK,SAAS,YAAY,KAAK,OAAO;CACvE,MAAM,QAAQ,CAAC,WAAW,CAAC,CAAC,QAAQ;CAEpC,MAAM,QAAQ,cACN,oBAAoB,MAAM,OAAO,QAAQ,KAAK,EACpD,CAAC,MAAM,OAAO,KAAK,CACpB;CACD,MAAM,WAAW,cACT,oBAAoB,MAAM,UAAU,QAAQ,KAAK,EACvD,CAAC,MAAM,UAAU,KAAK,CACvB;CAED,MAAM,QAAQ,cAAgC;AAE5C,UADiB,MAAM,QAAQ,SAAS,EAAE,EAC1B,KAAK,UAAU;GAC7B,OAAO,oBAAoB,KAAK,OAAO,QAAQ,KAAK;GACpD,UAAU,oBAAoB,KAAK,UAAU,QAAQ,KAAK;GAC1D,WAAW,KAAK,UAAU,KAAK,cAAc;IAC3C,KAAK,SAAS;IACd,QAAQ,kBAAkB,SAAS,QAAQ,QAAQ,KAAK;IACxD,MAAM,SAAS;IACf,UAAU,SAAS;IACnB,UAAU,SAAS,WAAW,EAAE,EAAE,KAAK,YAAY;KACjD,OAAO,OAAO;KACd,OAAO,kBAAkB,OAAO,OAAO,QAAQ,KAAK;KACrD,EAAE;IACJ,EAAE;GACJ,EAAE;IACF,CAAC,MAAM,QAAQ,KAAK,CAAC;CAExB,MAAM,YAAY,cACV,MAAM,SAAS,SAAS,KAAK,UAAU,EAC7C,CAAC,MAAM,CACR;CAGD,MAAM,YAAY,UAAU,KAAK,aAAa,SAAS,IAAI,CAAC,KAAK,IAAI;CACrE,MAAM,eAAe,OAAO,UAAU;AACtC,KAAI,aAAa,YAAY,WAAW;AACtC,eAAa,UAAU;AACvB,MAAI,cAAc,EAAG,cAAa,EAAE;;CAGtC,MAAM,aAAa,MAAM;CACzB,MAAM,YACJ,aAAa,IAAI,KAAK,IAAI,WAAW,aAAa,EAAE,GAAG;CACzD,MAAM,cAAc,MAAM;CAE1B,MAAM,YAAY,aAAa,KAAa,UAAkB;AAC5D,cAAY,aAAa;GAAE,GAAG;IAAU,MAAM;GAAO,EAAE;IACtD,EAAE,CAAC;CAEN,MAAM,eAAe,aAAa,KAAa,UAAkB;AAC/D,cAAY,YAAY;GACtB,MAAM,WAAW,MAAM,QAAQ,QAAQ,KAAK,GACvC,QAAQ,OACT,EAAE;GACN,MAAMC,SAAO,SAAS,SAAS,MAAM,GACjC,SAAS,QAAQ,SAAS,SAAS,MAAM,GACzC,CAAC,GAAG,UAAU,MAAM;AACxB,UAAO;IAAE,GAAG;KAAU,MAAMA;IAAM;IAClC;IACD,EAAE,CAAC;CAEN,MAAM,aAAa,aAChB,QAAgB,SAAS,QAAQ,KAAK,EACvC,CAAC,QAAQ,CACV;CAED,MAAM,YAAY,cAAc;AAC9B,MAAI,CAAC,SAAS,WAAY,QAAO;AACjC,SAAO,UACJ,QAAQ,aAAa,SAAS,SAAS,CACvC,OAAO,aAAa,SAAS,QAAQ,SAAS,KAAK,CAAC;IACtD;EAAC;EAAS;EAAW;EAAO;EAAW,CAAC;CAE3C,MAAM,aAAa,cAAc;AAC/B,MAAI,CAAC,YAAa,QAAO;AACzB,SAAO,YAAY,UAChB,QAAQ,aAAa,SAAS,SAAS,CACvC,OAAO,aAAa,SAAS,QAAQ,SAAS,KAAK,CAAC;IACtD,CAAC,SAAS,YAAY,CAAC;CAE1B,MAAM,SAAS,YAAY,YAAY;AACrC,MAAI,CAAC,UAAW;AAChB,gBAAc,KAAK;AACnB,WAAS,KAAK;EAEd,MAAM,SAAS,MAAM,aAAa,QAAQ;AAC1C,gBAAc,MAAM;AAEpB,MAAI,OAAO,IAAI;AACb,gBAAa,KAAK;AAClB;;AAGF,WACE,OAAO,WAAW,kBACd,4CACA,uBACL;IACA;EAAC;EAAS;EAAW;EAAa,CAAC;CAEtC,MAAM,aAAa,eAAe,KAAK,aAAa,aAAa;CACjE,MAAM,cAAc,cAAc;CAElC,MAAM,OAAO,kBAAkB;AAC7B,MAAI,WACF,CAAK,QAAQ;MAEb,cAAa,YAAY,EAAE;IAE5B;EAAC;EAAY;EAAW;EAAO,CAAC;CAEnC,MAAM,OAAO,kBAAkB;AAC7B,gBAAc,YAAY,KAAK,IAAI,GAAG,UAAU,EAAE,CAAC;IAClD,EAAE,CAAC;AAEN,QAAO;EACL,SAAS,SAAS,CAAC;EACnB;EACA;EACA;EACA;EACA,WAAW;EACX;EACA;EACA,UAAU,aAAa,KAAM,YAAY,KAAK,aAAc,MAAM;EAClE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;;;;;AC5NH,MAAa,kBAAkB;AAC3B,QAAO,oBAAC;EAAI,OAAM;EACd,OAAM;EAAK,QAAO;EAAK,SAAQ;EAAY,MAAK;EAChD,QAAO;EAAe,aAAY;EAAI,eAAc;EAAQ,gBAAe;EAC3E,WAAU;YAAwC,oBAAC,UAAK,GAAE,oBAAoB;GAAM;;;;;ACI5F,SAAgB,cAAc,EAC5B,WACA,SACA,QACqB;CACrB,MAAM,YAAY,KAAK,gBAAgB;CAEvC,MAAM,SACJ,oBAAC;EACC,WAAU;EACV,cAAY,YAAY,cAAc;YAErC,YAAY,oBAAC,cAAY,GAAG;GACxB;CAGT,MAAM,UACJ,qBAAC;EAAI,WAAU;aACb,qBAAC;GAAI,WAAU;cACb,oBAAC;IAAG,WAAU;cAAuB,KAAK;KAAW,EACpD,KAAK,QACJ,oBAAC;IAAK,WAAU;cAAuB,KAAK;KAAa,GACvD;IACA,EAEL,KAAK,cACJ,oBAAC;GAAE,WAAU;aAA6B,KAAK;IAAgB,GAC7D;GACA;AAGR,QACE,oBAAC;EAAG,WAAU;EAAgB,kBAAgB;YAC3C,YACC,qBAAC;GACC,WAAU;GACV,MAAK;GACL,SAAS;GACT,cAAY,GAAG,KAAK,YAAY,GAAG,KAAK;cAEvC,QACA;IACM,GAET,qBAAC;GAAI,WAAU;cACZ,QACA;IACG;GAEL;;;;;ACpDT,SAAgB,gBAAgB,MAA8C;CAC5E,MAAM,EAAE,SAAS,aAAa;AAC9B,QAAO,cAAc;AACnB,MAAI,SAAS,OAAW,QAAO;AAC/B,SAAO,kBAAkB,MAAM,KAAK;IACnC,CAAC,MAAM,KAAK,CAAC;;;;;ACJlB,SAAgB,iBACd,UACA,SACA,YAAY,KACQ;CACpB,MAAM,CAAC,OAAO,YAAY,SAA6B;EACrD,SAAS;EACT,UAAU;EACX,CAAC;AAEF,iBAAgB;AACd,MAAI,CAAC,WAAW,CAAC,UAAU;AACzB,YAAS;IAAE,SAAS;IAAM,UAAU;IAAO,CAAC;AAC5C;;EAGF,IAAI,YAAY;EAChB,MAAM,YAAY,KAAK,KAAK;EAE5B,MAAM,mBAAmB;AACvB,OAAI,UACF;GAGF,MAAM,UAAU,SAAS,cAAc,SAAS;AAEhD,OAAI,mBAAmB,aAAa;AAClC,aAAS;KAAE;KAAS,UAAU;KAAO,CAAC;AACtC;;AAGF,OAAI,KAAK,KAAK,GAAG,aAAa,WAAW;AACvC,aAAS;KAAE,SAAS;KAAM,UAAU;KAAM,CAAC;AAC3C;;AAGF,UAAO,WAAW,YAAY,IAAI;;AAGpC,WAAS;GAAE,SAAS;GAAM,UAAU;GAAO,CAAC;AAC5C,cAAY;AAEZ,eAAa;AACX,eAAY;;IAEb;EAAC;EAAS;EAAU;EAAU,CAAC;AAElC,QAAO;;;;;ACrCT,MAAM,oBAAoB;AAE1B,SAAgB,mBAAmB;CACjC,MAAM,EAAE,oBAAoB,aAAa;AAEzC,QACE,0CACG,gBAAgB,KAAK,EAAE,KAAK,cAC3B,oBAAC;EAAiB;EAAS,YAAY;IAAU,IAAO,CACxD,GACD;;AAIP,SAAS,QAAQ,EAAE,SAAS,cAA4B;CACtD,MAAM,EAAE,cAAc,aAAa;CACnC,MAAM,QAAQ,gBAAgB,QAAQ,MAAM;CAC5C,MAAM,SAAS,iBAAiB,QAAQ,QAAQ,KAAK;CACrD,MAAM,EAAE,MAAM,mBAAmB,YAAY;EAC3C,WAAW,QAAQ;EACnB,UAAU;EACV,YAAY,CAAC,OAAO,CAAC,kBAAkB,CAAC;EACxC,sBAAsB;EACvB,CAAC;AAEF,iBAAgB;AACd,MAAI,CAAC,OAAO,QACV;AAGF,OAAK,aAAa,OAAO,QAAQ;IAChC,CAAC,MAAM,OAAO,QAAQ,CAAC;AAE1B,KAAI,CAAC,OAAO,QACV,QAAO;AAGT,QACE,oBAAC,4BACC,oBAAC;EACC,cAAY,SAAS,SAAS,QAAQ,OAAO,KAAK;EAClD,WAAU;EACV,cAAY,QAAQ;EACpB,yBAAuB;EACvB,eAAe;AACb,GAAK,UAAU,QAAQ,OAAO,KAAK;;EAErC,KAAK,KAAK;EACV,OAAO;EACP,MAAK;YAEL,oBAAC,UAAK,WAAU,yBAAyB;GAClC,GACM;;;;;ACvDrB,SAAgB,eAAe;CAC7B,MAAM,EAAE,YAAY,aAAa,aAAa,UAAU,aAAa;CACrE,MAAM,WAAW,OAA6B,KAAK;CACnD,MAAM,CAAC,eAAe,oBAAoB,SAAyB,KAAK;CACxE,MAAM,aAAa,aAAa,YAAY,KAAK,OAAO,SAAS,IAAI;CACrE,MAAM,SAAS,iBACb,YAAY,KAAK,UAAU,MAC3B,QAAQ,WAAW,IAAI,YACvB,IACD;CACD,MAAM,EAAE,SAAS,MAAM,gBAAgB,WAAW,YAAY;EAC5D,WAAW,YAAY,KAAK,aAAa;EACzC,YAAY;GACV,OAAO,GAAG;GACV,MAAM;GACN,MAAM,EAAE,SAAS,IAAI,CAAC;GACtB,MAAM,EAAE,SAAS,UAAU,CAAC;GAC7B;EACF,CAAC;AAEF,iBAAgB;AACd,MAAI,CAAC,cAAc,CAAC,cAAc,CAAC,OAAO,SACxC;AAKF,EAAK,YAAY,EAAE,QAAQ,MAAM,CAAC;IACjC;EAAC;EAAY;EAAa;EAAY,OAAO;EAAS,CAAC;AAE1D,iBAAgB;AACd,MAAI,CAAC,cAAc,CAAC,OAAO,QACzB;AAGF,OAAK,aAAa,OAAO,QAAQ;AACjC,SAAO,QAAQ,eAAe;GAAE,OAAO;GAAU,QAAQ;GAAU,CAAC;AACpE,EAAK,QAAQ;IACZ;EAAC;EAAY;EAAM,OAAO;EAAS;EAAO,CAAC;AAE9C,iBAAgB;AACd,MAAI,CAAC,cAAc,CAAC,OAAO,SAAS;AAClC,oBAAiB,KAAK;AACtB;;EAGF,MAAM,wBAAwB;AAC5B,oBAAiB,OAAO,SAAS,uBAAuB,IAAI,KAAK;;AAGnE,mBAAiB;AACjB,SAAO,iBAAiB,UAAU,gBAAgB;AAClD,SAAO,iBAAiB,UAAU,iBAAiB,KAAK;AAExD,SAAO,QAAQ,aAAa,6BAA6B,OAAO;AAEhE,eAAa;AACX,UAAO,oBAAoB,UAAU,gBAAgB;AACrD,UAAO,oBAAoB,UAAU,iBAAiB,KAAK;AAC3D,UAAO,SAAS,gBAAgB,4BAA4B;AAC5D,oBAAiB,KAAK;;IAEvB,CAAC,YAAY,OAAO,QAAQ,CAAC;AAEhC,iBAAgB;AACd,MAAI,CAAC,cAAc,CAAC,OAAO,QACzB;EAGF,MAAM,6BAA6B;AACjC,UAAO,iBAAiB;AACtB,IAAK,aAAa;MACjB,EAAE;;AAGP,SAAO,QAAQ,iBAAiB,SAAS,qBAAqB;AAE9D,eAAa;AACX,UAAO,SAAS,oBAAoB,SAAS,qBAAqB;;IAEnE;EAAC;EAAY;EAAa,OAAO;EAAQ,CAAC;CAE7C,MAAM,WAAW,gBAAgB,YAAY,KAAK;CAClD,MAAM,YAAY,gBAAgB,YAAY,KAAK,MAAM;CACzD,MAAM,WAAW,gBAAgB,YAAY,KAAK,KAAK;AAEvD,KAAI,CAAC,WACH,QAAO;AAGT,KAAI,CAAC,cAAc,CAAC,OAAO,QACzB,QAAO;CAGT,MAAM,aAAa,WAAW,cAAc,WAAW,aAAa;AAEpE,QACE,qBAAC,6BACC,oBAAC,qBAAkB,MAAM,gBAAiB,EAC1C,qBAAC;EACC,cAAY,GAAG,SAAS;EACxB,WAAU;EACV,KAAK,KAAK;EACV,MAAK;EACL,OAAO;;GAEP,oBAAC;IACC,WAAU;IACD;IACT,MAAK;IACL,KAAK;IACL,QAAO;IACP,aAAa;IACb,WAAW;KACX;GACF,qBAAC;IAAI,WAAU;;KACZ,WAAW,YAAY;KAAE;KAAI,WAAW;;KACrC;GACN,oBAAC;IAAG,WAAU;cAAuB;KAAe;GACnD,WACC,oBAAC;IAAE,WAAU;cAAsB;KAAa,GAC9C;GACJ,qBAAC;IAAI,WAAU;eACb,oBAAC;KACC,WAAU;KACV,eAAe,KAAK,aAAa;KACjC,MAAK;eACN;MAEQ,EACT,oBAAC;KACC,WAAU;KACV,eAAe,KAAK,aAAa;KACjC,MAAK;eAEJ,aAAa,WAAW;MAClB;KACL;;GACE,IACK;;AAIrB,SAAS,kBAAkB,EAAE,QAAkC;AAC7D,KAAI,CAAC,KACH,QAAO,oBAAC,SAAI,WAAU,wBAAwB;CAGhD,MAAM,UAAU;CAChB,MAAM,SAAS;CACf,MAAM,OAAO,KAAK,IAAI,KAAK,OAAO,SAAS,EAAE;CAC7C,MAAM,MAAM,KAAK,IAAI,KAAK,MAAM,SAAS,EAAE;CAC3C,MAAM,QAAQ,KAAK,QAAQ,UAAU;CACrC,MAAM,SAAS,KAAK,SAAS,UAAU;CACvC,MAAM,SAAS;AAEf,QACE,qBAAC;EACC,eAAY;EACZ,WAAU;EACV,WAAU;aAEV,oBAAC,oBACC,qBAAC;GAAK,IAAI;cACR,oBAAC;IAAK,MAAK;IAAQ,QAAO;IAAO,OAAM;IAAO,GAAE;IAAI,GAAE;KAAM,EAC5D,oBAAC;IACC,MAAK;IACG;IACR,IAAI;IACJ,IAAI;IACG;IACP,GAAG;IACH,GAAG;KACH;IACG,GACF,EACP,oBAAC;GACC,MAAK;GACL,QAAO;GACP,MAAM,QAAQ,OAAO;GACrB,OAAM;GACN,GAAE;GACF,GAAE;IACF;GACE;;;;;AChMV,SAAgB,oBAAoB;CAClC,MAAM,CAAC,MAAM,WAAW,SAAS,KAAK;CACtC,MAAM,EACJ,SACA,OACA,UACA,OACA,gBACA,YACA,gBACA,UACA,aACA,WACA,YACA,sBACE,cAAc;AAKlB,iBAAgB;AACd,MAAI,CAAC,YAAY,eAAgB;EACjC,MAAM,SAAS,OAAO,WAAW,mBAAmB,WAAW,eAAe;AAC9E,eAAa,OAAO,aAAa,OAAO;IACvC,CAAC,YAAY,kBAAkB,CAAC;AAEnC,KAAI,CAAC,WAAW,CAAC,MACf,QAAO;AAGT,KAAI,WACF,QACE,qBAAC;EACC,WAAU;EACV,cAAY,GAAG,MAAM;EACrB,MAAK;EACL,aAAU;aAEV,oBAAC;GACC,WAAU;GACV,MAAK;GACL,SAAS;GACT,cAAW;aACZ;IAEQ,EACT,qBAAC;GAAI,WAAU;;IACb,oBAAC;KAAI,WAAU;KAA4B,eAAY;eACrD,oBAAC;MACC,SAAQ;MACR,MAAK;MACL,QAAO;MACP,aAAY;MACZ,eAAc;MACd,gBAAe;gBAEf,oBAAC,cAAS,QAAO,oBAAoB;OACjC;MACF;IACL,WAAW,WACV,oBAAC;KAAE,WAAU;eAAgC,WAAW;MAAa,GACnE;IACJ,oBAAC;KAAG,WAAU;eAA6B,WAAW;MAAW;IAChE,WAAW,UACV,oBAAC;KAAE,WAAU;eAA+B,WAAW;MAAY,GACjE;;IACA;GACA;AAIZ,KAAI,CAAC,KACH,QACE,oBAAC;EACC,WAAU;EACV,MAAK;EACL,eAAe,QAAQ,KAAK;EAC5B,cAAY,QAAQ,MAAM,cAAc,eAAe;YAEvD,oBAAC;GAAK,WAAU;aAA2B;IAAsB;GAC1D;AAIb,QACE,qBAAC;EAAM,WAAU;EAAgB,cAAY,GAAG,MAAM;;GACpD,qBAAC;IAAI,WAAU;eACb,qBAAC,oBACE,WAAW,oBAAC;KAAE,WAAU;eAAoB;MAAa,GAAG,MAC7D,oBAAC;KAAG,WAAU;eAAkB;MAAW,IACvC,EACN,qBAAC;KAAI,WAAU;gBACb,qBAAC;MACC,WAAU;MACV,cAAW;;OAEV;OAAe;OAAI;;OAClB,EACJ,oBAAC;MACC,WAAU;MACV,MAAK;MACL,eAAe,QAAQ,MAAM;MAC7B,cAAY,SAAS,MAAM;gBAC5B;OAEQ;MACL;KACF;GAEN,oBAAC;IACC,WAAU;IACV,MAAK;IACL,cAAY,GAAG,MAAM;IACrB,iBAAe;IACf,iBAAe;IACf,iBAAe;cAEf,oBAAC;KACC,WAAU;KACV,OAAO,EAAE,OAAO,GAAG,SAAS,IAAI;MAChC;KACE;GAEN,oBAAC;IAAG,WAAU;cACX,MAAM,KAAK,SACV,oBAAC;KACC,WAAW,YAAY,KAAK,IAAI;KAEhC,eAAe,UAAU,KAAK;KACxB;OAFD,KAAK,IAGV,CACF;KACC;;GACC;;AAIZ,SAAgB,iBAAiB;CAC/B,MAAM,EACJ,SACA,OACA,UACA,aACA,WACA,YACA,UACA,aACA,YACA,YACA,WACA,MACA,MACA,SACA,WACA,cACA,YACA,UACE,WAAW;AAEf,KAAI,CAAC,WAAW,CAAC,SAAS,CAAC,YACzB,QAAO;CAGT,MAAM,eAAe,aAAa;AAElC,QACE,oBAAC;EAAI,WAAU;YACb,qBAAC;GACC,WAAU;GACV,MAAK;GACL,cAAW;GACX,cAAY,GAAG,MAAM;;IAErB,qBAAC;KAAI,WAAU;gBACZ,WAAW,oBAAC;MAAE,WAAU;gBAAoB;OAAa,GAAG,MAC7D,oBAAC;MAAG,WAAU;gBAAkB;OAAW;MACvC;IAEL,eACC,qBAAC;KAAI,WAAU;gBACb,qBAAC;MAAE,WAAU;;OAA4B;OACjC,YAAY;OAAE;OAAK;;OACvB,EACJ,oBAAC;MACC,WAAU;MACV,MAAK;MACL,cAAY,GAAG,MAAM;MACrB,iBAAe;MACf,iBAAe;MACf,iBAAe,YAAY;gBAE3B,oBAAC;OACC,WAAU;OACV,OAAO,EAAE,OAAO,GAAG,SAAS,IAAI;QAChC;OACE;MACF,GACJ;IAEJ,qBAAC;KACC,WAAU;KACV,WAAW,UAAU;AACnB,YAAM,gBAAgB;AACtB,YAAM;;;MAGP,YAAY,QACX,oBAAC;OAAE,WAAU;iBAA8B,YAAY;QAAU,GAC/D;MAEJ,oBAAC;OAAG,WAAU;iBACX,YAAY,UAAU,KAAK,aAC1B,qBAAC;QAAG,WAAU;mBACZ,qBAAC;SAAE,WAAU;oBACV,SAAS,QACT,SAAS,WACR,oBAAC;UAAK,eAAY;UAAO,WAAU;oBAChC;WACI,GACL;UACF,EAEH,SAAS,SAAS,SACjB,oBAAC;SACC,WAAU;SACV,MAAK;SACL,OACE,OAAO,QAAQ,SAAS,SAAS,WAC5B,QAAQ,SAAS,OAClB;SAEN,UAAU,SAAS;SACnB,cAAY,SAAS;SACrB,WAAW,UACT,UAAU,SAAS,KAAK,MAAM,OAAO,MAAM;UAE7C,GAEF,oBAAC;SACC,WAAU;SACV,MAAK;SACL,cAAY,SAAS;mBAEpB,SAAS,QAAQ,KAAK,WAAW;UAChC,MAAM,WACJ,SAAS,SAAS,iBACd,MAAM,QAAQ,QAAQ,SAAS,KAAK,IACnC,QAAQ,SAAS,KAAkB,SAClC,OAAO,MACR,GACD,QAAQ,SAAS,SAAS,OAAO;AAEvC,iBACE,oBAAC;WACC,WAAU;WACV,MAAK;WAEL,gBAAc;WACd,iBAAe,WAAW,SAAS;WACnC,eACE,SAAS,SAAS,iBACd,aAAa,SAAS,KAAK,OAAO,MAAM,GACxC,UAAU,SAAS,KAAK,OAAO,MAAM;qBAG1C,OAAO;aATH,OAAO,MAUL;WAEX;UACE;UAzDoC,SAAS,IA2DlD,CACL;QACC;MAEJ,QACC,oBAAC;OAAE,WAAU;OAAwB,MAAK;iBACvC;QACC,GACF;MAEJ,qBAAC;OAAI,WAAU;kBACZ,CAAC,cACA,oBAAC;QACC,WAAU;QACV,MAAK;QACL,SAAS;kBACV;SAEQ,GAET,oBAAC,WAAO,EAEV,oBAAC;QACC,WAAU;QACV,MAAK;QACL,UAAU,aAAa,CAAC,YAAY,CAAC;kBAEpC,aAAc,aAAa,gBAAgB,WAAY;SACjD;QACL;;MACD;;IACC;GACN;;AAIV,MAAa,WAAW,OAAO,OAAO,mBAAmB;CACvD,WAAW;CACX,QAAQ;CACR,UAAU;CACV,MAAM;CACP,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sunboard/react",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "React SDK for the Sunboard onboarding runtime.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -26,7 +26,7 @@
26
26
  },
27
27
  "dependencies": {
28
28
  "@floating-ui/react": "^0.27.19",
29
- "@sunboard/core": "0.2.0"
29
+ "@sunboard/core": "0.4.0"
30
30
  },
31
31
  "peerDependencies": {
32
32
  "react": ">=18.3.0 || >=19.0.0",