@wireai/activation 0.3.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +57 -0
  3. package/dist/analytics/index.d.mts +15 -2
  4. package/dist/analytics/index.d.ts +15 -2
  5. package/dist/analytics/index.js +224 -14
  6. package/dist/analytics/index.js.map +1 -1
  7. package/dist/analytics/index.mjs +222 -15
  8. package/dist/analytics/index.mjs.map +1 -1
  9. package/dist/coachmarks/index.js +6 -1
  10. package/dist/coachmarks/index.js.map +1 -1
  11. package/dist/coachmarks/index.mjs +6 -1
  12. package/dist/coachmarks/index.mjs.map +1 -1
  13. package/dist/{eventQueue-CA1d8Fmn.d.mts → currentSession-BJBB7i4-.d.mts} +143 -15
  14. package/dist/{eventQueue-CrNB9gzH.d.ts → currentSession-CxnP7gAa.d.ts} +143 -15
  15. package/dist/index.d.mts +40 -39
  16. package/dist/index.d.ts +40 -39
  17. package/dist/index.js +378 -154
  18. package/dist/index.js.map +1 -1
  19. package/dist/index.mjs +370 -156
  20. package/dist/index.mjs.map +1 -1
  21. package/dist/questionnaire/index.d.mts +1 -1
  22. package/dist/questionnaire/index.d.ts +1 -1
  23. package/dist/questionnaire/index.js +29 -5
  24. package/dist/questionnaire/index.js.map +1 -1
  25. package/dist/questionnaire/index.mjs +30 -6
  26. package/dist/questionnaire/index.mjs.map +1 -1
  27. package/dist/reviews/index.d.mts +1 -1
  28. package/dist/reviews/index.d.ts +1 -1
  29. package/dist/reviews/index.js +40 -9
  30. package/dist/reviews/index.js.map +1 -1
  31. package/dist/reviews/index.mjs +42 -11
  32. package/dist/reviews/index.mjs.map +1 -1
  33. package/dist/showcase/index.d.mts +1 -14
  34. package/dist/showcase/index.d.ts +1 -14
  35. package/dist/showcase/index.js +8 -2
  36. package/dist/showcase/index.js.map +1 -1
  37. package/dist/showcase/index.mjs +8 -2
  38. package/dist/showcase/index.mjs.map +1 -1
  39. package/package.json +3 -2
  40. package/src/OnboardingFlow.tsx +8 -4
  41. package/src/WireOnboarding.tsx +12 -6
  42. package/src/analytics/analyticsFacade.ts +86 -10
  43. package/src/analytics/contextEnvelope.ts +13 -7
  44. package/src/analytics/currentSession.ts +35 -0
  45. package/src/analytics/eventQueue.ts +5 -0
  46. package/src/analytics/index.ts +3 -0
  47. package/src/analytics/useAnalytics.ts +7 -1
  48. package/src/analytics/useScreenTracking.ts +23 -1
  49. package/src/cards/InterstitialCard.tsx +1 -1
  50. package/src/cards/NumberStepperCard.tsx +12 -7
  51. package/src/cards/StatusCard.tsx +13 -8
  52. package/src/cards/TextInputCard.tsx +10 -5
  53. package/src/components/AnimatedSparkle.tsx +20 -3
  54. package/src/components/Button.tsx +10 -1
  55. package/src/components/CardHandoff.tsx +2 -6
  56. package/src/components/CardLayout.tsx +16 -10
  57. package/src/components/Illustration.tsx +9 -5
  58. package/src/components/LoadingBlock.tsx +44 -29
  59. package/src/components/LoadingScreen.tsx +3 -2
  60. package/src/components/OnboardingScaffold.tsx +21 -23
  61. package/src/context/userContext.ts +210 -0
  62. package/src/device/appVersion.ts +103 -0
  63. package/src/device/deviceContext.ts +17 -5
  64. package/src/features/WireFeaturesProvider.tsx +7 -2
  65. package/src/identity/userIdentity.ts +5 -0
  66. package/src/index.ts +23 -0
  67. package/src/questionnaire/QuestionnaireGate.tsx +15 -3
  68. package/src/reviews/ReviewGate.tsx +27 -7
  69. package/src/reviews/ReviewModal.tsx +4 -0
  70. package/src/session-analytics/reportSessionStart.ts +7 -0
  71. package/src/session-analytics/useLifecycleEvents.ts +4 -2
  72. package/src/session-analytics/useSessionStart.ts +2 -1
  73. package/src/showcase/FeatureShowcase.tsx +2 -1
  74. package/src/types.ts +6 -5
  75. package/src/components/loaderChrome.ts +0 -28
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import React14, { createContext, forwardRef, useRef, useState, useEffect, useCallback, useImperativeHandle, useContext, useMemo } from 'react';
1
+ import React14, { createContext, forwardRef, useRef, useState, useEffect, useCallback, useImperativeHandle, useMemo, useContext } from 'react';
2
2
  import { useWireAIThread, useWireAIAction, ComponentRenderer, WireAIProvider } from 'wireai-rn';
3
3
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
4
4
  import { StyleSheet, Easing, Animated, BackHandler, Modal, Pressable, Text, View, TouchableOpacity, KeyboardAvoidingView, ScrollView, useWindowDimensions, TextInput, Image, AccessibilityInfo, Platform, Dimensions, I18nManager, AppState } from 'react-native';
@@ -6,6 +6,12 @@ import { SafeAreaView } from 'react-native-safe-area-context';
6
6
  import { z } from 'zod';
7
7
 
8
8
  var __defProp = Object.defineProperty;
9
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
10
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
11
+ }) : x)(function(x) {
12
+ if (typeof require !== "undefined") return require.apply(this, arguments);
13
+ throw Error('Dynamic require of "' + x + '" is not supported');
14
+ });
9
15
  var __export = (target, all) => {
10
16
  for (var name in all)
11
17
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -79,11 +85,15 @@ var useOnboardingTheme = () => {
79
85
  const ctx = useContext(OnboardingThemeContext);
80
86
  return ctx != null ? ctx : mergeTheme();
81
87
  };
82
- var IllustrationContext = createContext({});
88
+ var EMPTY_REGISTRY = {};
89
+ var IllustrationContext = createContext(EMPTY_REGISTRY);
83
90
  var IllustrationProvider = ({
84
91
  registry,
85
92
  children
86
- }) => /* @__PURE__ */ jsx(IllustrationContext.Provider, { value: registry != null ? registry : {}, children });
93
+ }) => {
94
+ const value = useMemo(() => registry != null ? registry : EMPTY_REGISTRY, [registry]);
95
+ return /* @__PURE__ */ jsx(IllustrationContext.Provider, { value, children });
96
+ };
87
97
  var useIllustration = (name) => {
88
98
  const registry = useContext(IllustrationContext);
89
99
  if (!name) return void 0;
@@ -374,23 +384,33 @@ var _AnimatedSparkle = ({ size = 30, color, variant = "spin" }) => {
374
384
  outputRange: [1, LOADER_PULSE_MIN_OPACITY]
375
385
  });
376
386
  const animatedStyle = reduced ? void 0 : variant === "pulse" ? { opacity: pulse } : { transform: [{ rotate }] };
377
- return /* @__PURE__ */ jsx(Animated.View, { style: animatedStyle, children: /* @__PURE__ */ jsx(
387
+ return /* @__PURE__ */ jsx(Animated.View, { style: [styles2.box, { width: size, height: size }, animatedStyle], children: /* @__PURE__ */ jsx(
378
388
  Text,
379
389
  {
380
390
  accessibilityElementsHidden: true,
381
391
  importantForAccessibility: "no",
382
- style: [styles2.glyph, { fontSize: size, lineHeight: size * 1.1, color: color != null ? color : t.colors.primary }],
392
+ style: [
393
+ styles2.glyph,
394
+ {
395
+ width: size,
396
+ fontSize: size,
397
+ lineHeight: size,
398
+ color: color != null ? color : t.colors.primary
399
+ }
400
+ ],
383
401
  children: "\u2726"
384
402
  }
385
403
  ) });
386
404
  };
387
405
  var AnimatedSparkle = React14.memo(_AnimatedSparkle);
388
406
  var styles2 = StyleSheet.create({
389
- glyph: { textAlign: "center" }
407
+ box: { alignItems: "center", justifyContent: "center" },
408
+ glyph: {
409
+ textAlign: "center",
410
+ textAlignVertical: "center",
411
+ includeFontPadding: false
412
+ }
390
413
  });
391
- var LoaderChromeContext = createContext({ offsetY: 0 });
392
- var LoaderChromeProvider = LoaderChromeContext.Provider;
393
- var useLoaderChrome = () => useContext(LoaderChromeContext);
394
414
  var STAGE_SIZE = 96;
395
415
  var ORBIT_DOT = 9;
396
416
  var OrbitDot = ({ color, reduced }) => {
@@ -454,39 +474,45 @@ var _LoadingBlock = ({
454
474
  }) => {
455
475
  const t = useOnboardingTheme();
456
476
  const reduced = useReducedMotion();
457
- const { offsetY } = useLoaderChrome();
458
477
  const [showSkeleton, setShowSkeleton] = useState(false);
459
478
  useEffect(() => {
460
479
  const timer = setTimeout(() => setShowSkeleton(true), SKELETON_DELAY_MS);
461
480
  return () => clearTimeout(timer);
462
481
  }, []);
463
- return /* @__PURE__ */ jsxs(
464
- View,
465
- {
466
- style: [
467
- styles3.center,
468
- { gap: t.spacing.md, padding: t.spacing.lg },
469
- offsetY ? { transform: [{ translateY: offsetY }] } : null
470
- ],
471
- children: [
472
- /* @__PURE__ */ jsxs(View, { style: styles3.stage, children: [
473
- /* @__PURE__ */ jsx(View, { style: [styles3.ring, { borderColor: t.colors.border, borderRadius: t.radius.full }] }),
474
- /* @__PURE__ */ jsx(OrbitDot, { color: t.colors.primary, reduced }),
475
- /* @__PURE__ */ jsx(AnimatedSparkle, { size: 40, variant: "pulse" })
476
- ] }),
477
- title ? /* @__PURE__ */ jsx(Text, { style: [headingStyle(t.fonts), { color: t.colors.text, textAlign: "center" }], children: title }) : null,
478
- hint ? /* @__PURE__ */ jsx(Text, { style: [bodyStyle(t.fonts), { color: t.colors.textMuted, textAlign: "center" }], children: hint }) : null,
479
- showSkeleton ? /* @__PURE__ */ jsxs(View, { style: [styles3.skeleton, { gap: t.spacing.sm, marginTop: t.spacing.sm }], children: [
480
- /* @__PURE__ */ jsx(SkeletonLine, { width: "80%", delay: 0, color: t.colors.surface, reduced }),
481
- /* @__PURE__ */ jsx(SkeletonLine, { width: "60%", delay: 200, color: t.colors.surface, reduced })
482
- ] }) : null
483
- ]
484
- }
485
- );
482
+ return /* @__PURE__ */ jsxs(View, { style: [styles3.center, { padding: t.spacing.lg }], children: [
483
+ /* @__PURE__ */ jsxs(View, { style: styles3.stage, children: [
484
+ /* @__PURE__ */ jsx(View, { style: [styles3.ring, { borderColor: t.colors.border, borderRadius: t.radius.full }] }),
485
+ /* @__PURE__ */ jsx(OrbitDot, { color: t.colors.primary, reduced }),
486
+ /* @__PURE__ */ jsx(AnimatedSparkle, { size: 40, variant: "pulse" })
487
+ ] }),
488
+ /* @__PURE__ */ jsxs(
489
+ View,
490
+ {
491
+ style: [
492
+ styles3.labels,
493
+ { marginTop: STAGE_SIZE / 2 + t.spacing.lg, gap: t.spacing.md, paddingHorizontal: t.spacing.lg }
494
+ ],
495
+ pointerEvents: "none",
496
+ children: [
497
+ title ? /* @__PURE__ */ jsx(Text, { style: [headingStyle(t.fonts), { color: t.colors.text, textAlign: "center" }], children: title }) : null,
498
+ hint ? /* @__PURE__ */ jsx(Text, { style: [bodyStyle(t.fonts), { color: t.colors.textMuted, textAlign: "center" }], children: hint }) : null,
499
+ showSkeleton ? /* @__PURE__ */ jsxs(View, { style: [styles3.skeleton, { gap: t.spacing.sm, marginTop: t.spacing.sm }], children: [
500
+ /* @__PURE__ */ jsx(SkeletonLine, { width: "80%", delay: 0, color: t.colors.surface, reduced }),
501
+ /* @__PURE__ */ jsx(SkeletonLine, { width: "60%", delay: 200, color: t.colors.surface, reduced })
502
+ ] }) : null
503
+ ]
504
+ }
505
+ )
506
+ ] });
486
507
  };
487
508
  var LoadingBlock = React14.memo(_LoadingBlock);
488
509
  var styles3 = StyleSheet.create({
489
510
  center: { flex: 1, alignItems: "center", justifyContent: "center" },
511
+ // Absolutely-positioned label column, pinned just under the centered stage: its top
512
+ // edge starts at the flow center (top: 50%) and the inline marginTop (STAGE_SIZE/2 +
513
+ // a gap) drops it below the stage. Out of flow → it never moves the stage, so the
514
+ // 300ms skeleton reveal grows DOWNWARD only and the icon stays centered.
515
+ labels: { position: "absolute", top: "50%", left: 0, right: 0, alignItems: "center" },
490
516
  stage: {
491
517
  width: STAGE_SIZE,
492
518
  height: STAGE_SIZE,
@@ -569,7 +595,6 @@ var styles5 = StyleSheet.create({
569
595
  track: { height: 6, width: "100%", overflow: "hidden" },
570
596
  fill: { height: 6 }
571
597
  });
572
- var PROGRESS_TRACK = 6;
573
598
  var _OnboardingScaffold = ({
574
599
  step,
575
600
  complete,
@@ -581,33 +606,41 @@ var _OnboardingScaffold = ({
581
606
  children
582
607
  }) => {
583
608
  const t = useOnboardingTheme();
609
+ const styles16 = useMemo(() => makeStyles(t), [t]);
584
610
  const hit = { top: 12, bottom: 12, left: 12, right: 12 };
585
- const topChrome = t.spacing.md + PROGRESS_TRACK + t.spacing.md;
586
- const loaderOffsetY = -Math.round(topChrome / 2);
587
611
  return /* @__PURE__ */ jsxs(
588
612
  SafeAreaView,
589
613
  {
590
614
  edges: ["top", "bottom"],
591
- style: [styles6.flex, { backgroundColor: t.colors.background }],
615
+ style: [styles16.flex, { backgroundColor: t.colors.background }],
592
616
  children: [
593
- /* @__PURE__ */ jsxs(View, { style: [styles6.header, { paddingHorizontal: t.spacing.md, paddingTop: t.spacing.md }], children: [
594
- /* @__PURE__ */ jsx(View, { style: styles6.progressWrap, children: /* @__PURE__ */ jsx(StepProgress, { step, complete, approxScreens }) }),
595
- onSkip ? /* @__PURE__ */ jsx(TouchableOpacity, { onPress: onSkip, hitSlop: hit, style: { marginLeft: t.spacing.md }, children: /* @__PURE__ */ jsx(Text, { style: [bodyStyle(t.fonts), { color: t.colors.textMuted }], children: skipLabel }) }) : null
617
+ /* @__PURE__ */ jsxs(View, { style: [styles16.header, { paddingHorizontal: t.spacing.md, paddingTop: t.spacing.md }], children: [
618
+ /* @__PURE__ */ jsx(View, { style: styles16.progressWrap, children: /* @__PURE__ */ jsx(StepProgress, { step, complete, approxScreens }) }),
619
+ onSkip ? /* @__PURE__ */ jsx(TouchableOpacity, { onPress: onSkip, hitSlop: hit, style: styles16.skip, children: /* @__PURE__ */ jsx(Text, { style: [bodyStyle(t.fonts), { color: t.colors.textMuted }], children: skipLabel }) }) : null
596
620
  ] }),
597
- /* @__PURE__ */ jsx(View, { style: [styles6.body, { paddingHorizontal: t.spacing.md, paddingTop: t.spacing.md }], children: /* @__PURE__ */ jsx(LoaderChromeProvider, { value: { offsetY: loaderOffsetY }, children }) }),
598
- onBack ? /* @__PURE__ */ jsx(View, { style: [styles6.footer, { paddingHorizontal: t.spacing.md, paddingBottom: t.spacing.sm }], children: /* @__PURE__ */ jsx(TouchableOpacity, { onPress: onBack, hitSlop: hit, children: /* @__PURE__ */ jsx(Text, { style: [bodyStyle(t.fonts), { color: t.colors.textMuted }], children: backLabel }) }) }) : null
621
+ /* @__PURE__ */ jsx(
622
+ View,
623
+ {
624
+ style: [styles16.body, { paddingHorizontal: t.spacing.md, paddingTop: t.spacing.md }],
625
+ children
626
+ }
627
+ ),
628
+ onBack ? /* @__PURE__ */ jsx(View, { style: [styles16.footer, { paddingHorizontal: t.spacing.md, paddingBottom: t.spacing.sm }], children: /* @__PURE__ */ jsx(TouchableOpacity, { onPress: onBack, hitSlop: hit, children: /* @__PURE__ */ jsx(Text, { style: [bodyStyle(t.fonts), { color: t.colors.textMuted }], children: backLabel }) }) }) : null
599
629
  ]
600
630
  }
601
631
  );
602
632
  };
603
633
  var OnboardingScaffold = React14.memo(_OnboardingScaffold);
604
- var styles6 = StyleSheet.create({
605
- flex: { flex: 1 },
606
- header: { flexDirection: "row", alignItems: "center" },
607
- progressWrap: { flex: 1 },
608
- body: { flex: 1 },
609
- footer: { flexDirection: "row", justifyContent: "flex-start" }
610
- });
634
+ function makeStyles(t) {
635
+ return StyleSheet.create({
636
+ flex: { flex: 1 },
637
+ header: { flexDirection: "row", alignItems: "center" },
638
+ progressWrap: { flex: 1 },
639
+ body: { flex: 1 },
640
+ footer: { flexDirection: "row", justifyContent: "flex-start" },
641
+ skip: { marginLeft: t.spacing.md }
642
+ });
643
+ }
611
644
  var _Button = ({
612
645
  title,
613
646
  onPress,
@@ -638,6 +671,14 @@ var _Button = ({
638
671
  },
639
672
  [pressT, reduced]
640
673
  );
674
+ const lastPress = useRef(0);
675
+ const handlePress = useCallback(() => {
676
+ if (!onPress) return;
677
+ const now = Date.now();
678
+ if (now - lastPress.current < 500) return;
679
+ lastPress.current = now;
680
+ onPress();
681
+ }, [onPress]);
641
682
  const handlePressIn = useCallback(() => pressTo(1), [pressTo]);
642
683
  const handlePressOut = useCallback(() => pressTo(0), [pressTo]);
643
684
  const scale = pressT.interpolate({
@@ -651,19 +692,19 @@ var _Button = ({
651
692
  return /* @__PURE__ */ jsx(
652
693
  Pressable,
653
694
  {
654
- onPress: disabled ? void 0 : onPress,
695
+ onPress: disabled ? void 0 : handlePress,
655
696
  onPressIn: disabled ? void 0 : handlePressIn,
656
697
  onPressOut: disabled ? void 0 : handlePressOut,
657
698
  disabled,
658
699
  accessibilityRole: "button",
659
700
  accessibilityState: { disabled },
660
- style: fullWidth ? styles7.fill : void 0,
701
+ style: fullWidth ? styles6.fill : void 0,
661
702
  children: /* @__PURE__ */ jsx(
662
703
  Animated.View,
663
704
  {
664
705
  style: [
665
- styles7.base,
666
- fullWidth && styles7.fill,
706
+ styles6.base,
707
+ fullWidth && styles6.fill,
667
708
  {
668
709
  borderRadius: t.button.radius,
669
710
  minHeight: t.button.height,
@@ -691,7 +732,7 @@ var _Button = ({
691
732
  );
692
733
  };
693
734
  var Button = React14.memo(_Button);
694
- var styles7 = StyleSheet.create({
735
+ var styles6 = StyleSheet.create({
695
736
  base: { alignItems: "center", justifyContent: "center" },
696
737
  fill: { width: "100%" }
697
738
  });
@@ -702,14 +743,14 @@ var _ErrorBlock = ({
702
743
  onRetry
703
744
  }) => {
704
745
  const t = useOnboardingTheme();
705
- return /* @__PURE__ */ jsxs(View, { style: [styles8.center, { gap: t.spacing.md, padding: t.spacing.lg }], children: [
746
+ return /* @__PURE__ */ jsxs(View, { style: [styles7.center, { gap: t.spacing.md, padding: t.spacing.lg }], children: [
706
747
  /* @__PURE__ */ jsx(Text, { style: [headingStyle(t.fonts), { color: t.colors.text, textAlign: "center" }], children: title }),
707
748
  /* @__PURE__ */ jsx(Text, { style: [bodyStyle(t.fonts), { color: t.colors.textMuted, textAlign: "center" }], children: message }),
708
749
  onRetry ? /* @__PURE__ */ jsx(Button, { title: retryLabel, onPress: onRetry, variant: "outline" }) : null
709
750
  ] });
710
751
  };
711
752
  var ErrorBlock = React14.memo(_ErrorBlock);
712
- var styles8 = StyleSheet.create({
753
+ var styles7 = StyleSheet.create({
713
754
  center: { flex: 1, alignItems: "center", justifyContent: "center" }
714
755
  });
715
756
  var _CardLayout = ({
@@ -721,9 +762,10 @@ var _CardLayout = ({
721
762
  titleNode
722
763
  }) => {
723
764
  const t = useOnboardingTheme();
765
+ const styles16 = useMemo(() => makeStyles2(t), [t]);
724
766
  const hasHeader = !!title || !!subtitle || !!titleNode;
725
- return /* @__PURE__ */ jsxs(KeyboardAvoidingView, { style: styles9.fill, behavior: "padding", children: [
726
- hasHeader ? /* @__PURE__ */ jsxs(View, { style: { marginBottom: t.spacing.lg }, children: [
767
+ return /* @__PURE__ */ jsxs(KeyboardAvoidingView, { style: styles16.fill, behavior: "padding", children: [
768
+ hasHeader ? /* @__PURE__ */ jsxs(View, { style: styles16.header, children: [
727
769
  titleNode != null ? titleNode : title ? /* @__PURE__ */ jsx(Text, { style: [labelStyle(t.fonts), { color: t.colors.text }], children: title }) : null,
728
770
  subtitle ? /* @__PURE__ */ jsx(
729
771
  Text,
@@ -739,10 +781,10 @@ var _CardLayout = ({
739
781
  /* @__PURE__ */ jsx(
740
782
  ScrollView,
741
783
  {
742
- style: styles9.fill,
784
+ style: styles16.fill,
743
785
  contentContainerStyle: [
744
- styles9.scrollContent,
745
- align === "center" && styles9.center
786
+ styles16.scrollContent,
787
+ align === "center" && styles16.center
746
788
  ],
747
789
  keyboardShouldPersistTaps: "handled",
748
790
  keyboardDismissMode: "on-drag",
@@ -750,17 +792,21 @@ var _CardLayout = ({
750
792
  children
751
793
  }
752
794
  ),
753
- footer ? /* @__PURE__ */ jsx(View, { style: { paddingTop: t.spacing.lg }, children: footer }) : null
795
+ footer ? /* @__PURE__ */ jsx(View, { style: styles16.footer, children: footer }) : null
754
796
  ] });
755
797
  };
756
798
  var CardLayout = React14.memo(_CardLayout);
757
- var styles9 = StyleSheet.create({
758
- fill: { flex: 1 },
759
- // flexGrow lets a short content block grow to fill (pinning the footer) while a
760
- // tall one scrolls past the visible area.
761
- scrollContent: { flexGrow: 1 },
762
- center: { justifyContent: "center" }
763
- });
799
+ function makeStyles2(t) {
800
+ return StyleSheet.create({
801
+ fill: { flex: 1 },
802
+ header: { marginBottom: t.spacing.lg },
803
+ footer: { paddingTop: t.spacing.lg },
804
+ // flexGrow lets a short content block grow to fill (pinning the footer) while a
805
+ // tall one scrolls past the visible area.
806
+ scrollContent: { flexGrow: 1 },
807
+ center: { justifyContent: "center" }
808
+ });
809
+ }
764
810
  var easeWire2 = Easing.bezier(...WIRE_BEZIER);
765
811
  var BADGE_SIZE = 64;
766
812
  var BURST_SIZE = 104;
@@ -835,14 +881,14 @@ var _CompletionView = ({
835
881
  {
836
882
  align: "center",
837
883
  footer: /* @__PURE__ */ jsx(Button, { title: ctaLabel, onPress: handlePress, variant: "primary", disabled: submitted }),
838
- children: /* @__PURE__ */ jsxs(View, { style: [styles10.center, { gap: t.spacing.md }], children: [
839
- /* @__PURE__ */ jsxs(View, { style: styles10.badgeStage, children: [
884
+ children: /* @__PURE__ */ jsxs(View, { style: [styles8.center, { gap: t.spacing.md }], children: [
885
+ /* @__PURE__ */ jsxs(View, { style: styles8.badgeStage, children: [
840
886
  /* @__PURE__ */ jsx(
841
887
  Animated.View,
842
888
  {
843
889
  pointerEvents: "none",
844
890
  style: [
845
- styles10.burst,
891
+ styles8.burst,
846
892
  {
847
893
  backgroundColor: t.colors.primarySoft,
848
894
  borderRadius: t.radius.full,
@@ -856,7 +902,7 @@ var _CompletionView = ({
856
902
  Animated.View,
857
903
  {
858
904
  style: [
859
- styles10.badge,
905
+ styles8.badge,
860
906
  {
861
907
  backgroundColor: t.colors.primarySoft,
862
908
  borderRadius: t.radius.full,
@@ -864,7 +910,7 @@ var _CompletionView = ({
864
910
  opacity: pop
865
911
  }
866
912
  ],
867
- children: /* @__PURE__ */ jsx(Text, { style: [styles10.check, { color: t.colors.primary }], children: "\u2713" })
913
+ children: /* @__PURE__ */ jsx(Text, { style: [styles8.check, { color: t.colors.primary }], children: "\u2713" })
868
914
  }
869
915
  )
870
916
  ] }),
@@ -875,7 +921,7 @@ var _CompletionView = ({
875
921
  );
876
922
  };
877
923
  var CompletionView = React14.memo(_CompletionView);
878
- var styles10 = StyleSheet.create({
924
+ var styles8 = StyleSheet.create({
879
925
  center: { width: "100%", alignItems: "center", justifyContent: "center" },
880
926
  badgeStage: {
881
927
  width: BURST_SIZE,
@@ -898,7 +944,6 @@ var _CardHandoff = ({
898
944
  const enterT = useRef(new Animated.Value(0)).current;
899
945
  const exitT = useRef(new Animated.Value(0)).current;
900
946
  const prev = useRef({ key: transitionKey, node: children, variant });
901
- const [enterVariant, setEnterVariant] = useState(variant);
902
947
  const running = useRef({});
903
948
  const mounted = useRef(false);
904
949
  useEffect(() => {
@@ -907,7 +952,6 @@ var _CardHandoff = ({
907
952
  prev.current = { key: transitionKey, node: children, variant };
908
953
  if (!mounted.current) {
909
954
  mounted.current = true;
910
- setEnterVariant(variant);
911
955
  running.current.enter = startEnter(enterT, variant, reduced, 0);
912
956
  return;
913
957
  }
@@ -915,7 +959,6 @@ var _CardHandoff = ({
915
959
  (_a = running.current.enter) == null ? void 0 : _a.stop();
916
960
  (_b = running.current.exit) == null ? void 0 : _b.stop();
917
961
  setLeaving(last);
918
- setEnterVariant(variant);
919
962
  enterT.setValue(0);
920
963
  exitT.setValue(0);
921
964
  const exitDuration = reduced ? REDUCED_FADE_MS : last.variant === "fade" ? LOADER_HANDOFF_FADE_MS : CARD_EXIT_MS;
@@ -946,7 +989,7 @@ var _CardHandoff = ({
946
989
  );
947
990
  const enterStyle = {
948
991
  opacity: enterT,
949
- transform: transformFor(enterVariant, enterT, reduced, "enter")
992
+ transform: transformFor(variant, enterT, reduced, "enter")
950
993
  };
951
994
  return /* @__PURE__ */ jsxs(Fragment, { children: [
952
995
  leaving ? /* @__PURE__ */ jsx(
@@ -963,7 +1006,7 @@ var _CardHandoff = ({
963
1006
  children: leaving.node
964
1007
  }
965
1008
  ) : null,
966
- /* @__PURE__ */ jsx(Animated.View, { style: [styles11.fill, enterStyle], children })
1009
+ /* @__PURE__ */ jsx(Animated.View, { style: [styles9.fill, enterStyle], children }, variant)
967
1010
  ] });
968
1011
  };
969
1012
  var startEnter = (value, variant, reduced, delay, onDone) => {
@@ -1004,7 +1047,7 @@ var transformFor = (variant, t, reduced, direction) => {
1004
1047
  ];
1005
1048
  };
1006
1049
  var CardHandoff = React14.memo(_CardHandoff);
1007
- var styles11 = StyleSheet.create({
1050
+ var styles9 = StyleSheet.create({
1008
1051
  fill: { flex: 1 }
1009
1052
  });
1010
1053
 
@@ -1134,6 +1177,10 @@ var OnboardingFlow = ({
1134
1177
  onEventRef.current = onEvent;
1135
1178
  const onErrorRef = useRef(onError);
1136
1179
  onErrorRef.current = onError;
1180
+ const onCompleteRef = useRef(onComplete);
1181
+ onCompleteRef.current = onComplete;
1182
+ const validatorsRef = useRef(validators);
1183
+ validatorsRef.current = validators;
1137
1184
  const reportTargetRef = useRef(reportTarget);
1138
1185
  reportTargetRef.current = reportTarget;
1139
1186
  const sessionIdRef = useRef(sessionId);
@@ -1249,8 +1296,8 @@ var OnboardingFlow = ({
1249
1296
  const handleFinish = useCallback(() => {
1250
1297
  if (finished.current) return;
1251
1298
  finished.current = true;
1252
- onComplete({ answers: deriveAnswers(messages), raw: messages });
1253
- }, [messages, onComplete]);
1299
+ onCompleteRef.current({ answers: deriveAnswers(messages), raw: messages });
1300
+ }, [messages]);
1254
1301
  const handleRetry = useCallback(() => {
1255
1302
  attempts.current = 0;
1256
1303
  degradedRef.current = false;
@@ -1278,9 +1325,10 @@ var OnboardingFlow = ({
1278
1325
  }, []);
1279
1326
  const wrappedActions = useCallback(
1280
1327
  (messageId) => {
1328
+ var _a2;
1281
1329
  const base = makeActions(messageId);
1282
1330
  const key = progress.key;
1283
- const validator = key ? validators == null ? void 0 : validators[key] : void 0;
1331
+ const validator = key ? (_a2 = validatorsRef.current) == null ? void 0 : _a2[key] : void 0;
1284
1332
  if (!validator) return base;
1285
1333
  const out = { ...base };
1286
1334
  for (const name of ANSWER_CALLBACKS) {
@@ -1291,12 +1339,12 @@ var OnboardingFlow = ({
1291
1339
  if (typeof value !== "string") return original(...args);
1292
1340
  setValidating(true);
1293
1341
  validator(value).then((res) => {
1294
- var _a2;
1342
+ var _a3;
1295
1343
  if (res.ok) {
1296
1344
  setValidationError(void 0);
1297
1345
  original(...args);
1298
1346
  } else {
1299
- setValidationError((_a2 = res.error) != null ? _a2 : "That value isn't available.");
1347
+ setValidationError((_a3 = res.error) != null ? _a3 : "That value isn't available.");
1300
1348
  }
1301
1349
  }).catch(() => {
1302
1350
  original(...args);
@@ -1305,7 +1353,7 @@ var OnboardingFlow = ({
1305
1353
  }
1306
1354
  return out;
1307
1355
  },
1308
- [makeActions, progress.key, validators]
1356
+ [makeActions, progress.key]
1309
1357
  );
1310
1358
  if (degraded) {
1311
1359
  if (fallbackFlow !== void 0) return /* @__PURE__ */ jsx(Fragment, { children: fallbackFlow });
@@ -1460,7 +1508,7 @@ var _ChipItem = ({ chip, isSelected, multiSelect, onToggle }) => {
1460
1508
  Animated.View,
1461
1509
  {
1462
1510
  style: [
1463
- styles12.chip,
1511
+ styles10.chip,
1464
1512
  {
1465
1513
  borderRadius: t.radius.full,
1466
1514
  paddingHorizontal: t.spacing.md,
@@ -1477,7 +1525,7 @@ var _ChipItem = ({ chip, isSelected, multiSelect, onToggle }) => {
1477
1525
  pointerEvents: "none",
1478
1526
  style: [
1479
1527
  StyleSheet.absoluteFill,
1480
- styles12.fillOverlay,
1528
+ styles10.fillOverlay,
1481
1529
  {
1482
1530
  borderRadius: t.radius.full,
1483
1531
  borderColor: t.colors.primary,
@@ -1491,7 +1539,7 @@ var _ChipItem = ({ chip, isSelected, multiSelect, onToggle }) => {
1491
1539
  Animated.Text,
1492
1540
  {
1493
1541
  style: [
1494
- styles12.check,
1542
+ styles10.check,
1495
1543
  { color: t.colors.onPrimary, opacity: checkT, transform: [{ scale: checkScale }] }
1496
1544
  ],
1497
1545
  children: "\u2713"
@@ -1502,7 +1550,7 @@ var _ChipItem = ({ chip, isSelected, multiSelect, onToggle }) => {
1502
1550
  {
1503
1551
  style: [
1504
1552
  captionStyle(t.fonts),
1505
- styles12.chipLabel,
1553
+ styles10.chipLabel,
1506
1554
  { color: isSelected ? t.colors.onPrimary : t.colors.textMuted }
1507
1555
  ],
1508
1556
  children: chip
@@ -1564,7 +1612,7 @@ var _ChipSelectCard = ({
1564
1612
  disabled: selected.length === 0 || submitted
1565
1613
  }
1566
1614
  ),
1567
- children: /* @__PURE__ */ jsx(View, { style: [styles12.chipsRow, { gap: t.spacing.sm + t.spacing.xs }], children: chips.map((chip) => /* @__PURE__ */ jsx(
1615
+ children: /* @__PURE__ */ jsx(View, { style: [styles10.chipsRow, { gap: t.spacing.sm + t.spacing.xs }], children: chips.map((chip) => /* @__PURE__ */ jsx(
1568
1616
  ChipItem,
1569
1617
  {
1570
1618
  chip,
@@ -1584,7 +1632,7 @@ var ChipSelectCard = {
1584
1632
  propsSchema: schema,
1585
1633
  defaultProps: { multiSelect: true, submitLabel: "Continue" }
1586
1634
  };
1587
- var styles12 = StyleSheet.create({
1635
+ var styles10 = StyleSheet.create({
1588
1636
  chipsRow: { flexDirection: "row", flexWrap: "wrap" },
1589
1637
  chip: { borderWidth: 1.5, flexDirection: "row", alignItems: "center", gap: 6, overflow: "hidden" },
1590
1638
  fillOverlay: { borderWidth: 1.5 },
@@ -1616,6 +1664,7 @@ var _TextInputCard = ({
1616
1664
  lines = 6
1617
1665
  }) => {
1618
1666
  const t = useOnboardingTheme();
1667
+ const styles16 = useMemo(() => makeStyles3(t), [t]);
1619
1668
  const { height: screenHeight } = useWindowDimensions();
1620
1669
  const [value, setValue] = useState("");
1621
1670
  const [submitted, setSubmitted] = useState(false);
@@ -1642,7 +1691,7 @@ var _TextInputCard = ({
1642
1691
  disabled: !value.trim() || locked
1643
1692
  }
1644
1693
  ),
1645
- children: /* @__PURE__ */ jsxs(View, { style: { gap: t.spacing.sm }, children: [
1694
+ children: /* @__PURE__ */ jsxs(View, { style: styles16.row, children: [
1646
1695
  /* @__PURE__ */ jsx(
1647
1696
  TextInput,
1648
1697
  {
@@ -1656,7 +1705,7 @@ var _TextInputCard = ({
1656
1705
  textAlignVertical: multiline ? "top" : "center",
1657
1706
  style: [
1658
1707
  bodyStyle(t.fonts),
1659
- styles13.input,
1708
+ styles16.input,
1660
1709
  {
1661
1710
  color: t.colors.text,
1662
1711
  backgroundColor: t.colors.surface,
@@ -1680,9 +1729,12 @@ var TextInputCard = {
1680
1729
  propsSchema: schema2,
1681
1730
  defaultProps: { submitLabel: "Submit", multiline: true, lines: 6 }
1682
1731
  };
1683
- var styles13 = StyleSheet.create({
1684
- input: { borderWidth: 1 }
1685
- });
1732
+ function makeStyles3(t) {
1733
+ return StyleSheet.create({
1734
+ row: { gap: t.spacing.sm },
1735
+ input: { borderWidth: 1 }
1736
+ });
1737
+ }
1686
1738
  var easeWire5 = Easing.bezier(...WIRE_BEZIER);
1687
1739
  var optionObjectSchema = z.object({
1688
1740
  value: z.string().describe("Option value"),
@@ -1761,7 +1813,7 @@ var _OptionRow = ({ opt, isSelected, multiSelect, onToggle }) => {
1761
1813
  Animated.View,
1762
1814
  {
1763
1815
  style: [
1764
- styles14.option,
1816
+ styles11.option,
1765
1817
  {
1766
1818
  padding: t.spacing.md,
1767
1819
  borderRadius: t.radius.md,
@@ -1778,7 +1830,7 @@ var _OptionRow = ({ opt, isSelected, multiSelect, onToggle }) => {
1778
1830
  pointerEvents: "none",
1779
1831
  style: [
1780
1832
  StyleSheet.absoluteFill,
1781
- styles14.selectedOverlay,
1833
+ styles11.selectedOverlay,
1782
1834
  {
1783
1835
  borderRadius: t.radius.md,
1784
1836
  borderColor: t.colors.primary,
@@ -1792,7 +1844,7 @@ var _OptionRow = ({ opt, isSelected, multiSelect, onToggle }) => {
1792
1844
  View,
1793
1845
  {
1794
1846
  style: [
1795
- styles14.indicator,
1847
+ styles11.indicator,
1796
1848
  { borderRadius: t.radius.full },
1797
1849
  isSelected ? { borderColor: t.colors.primary, backgroundColor: t.colors.primary } : { borderColor: t.colors.borderStrong, backgroundColor: "transparent" }
1798
1850
  ],
@@ -1800,7 +1852,7 @@ var _OptionRow = ({ opt, isSelected, multiSelect, onToggle }) => {
1800
1852
  Animated.Text,
1801
1853
  {
1802
1854
  style: [
1803
- styles14.check,
1855
+ styles11.check,
1804
1856
  { color: t.colors.onPrimary, opacity: checkT, transform: [{ scale: checkScale }] }
1805
1857
  ],
1806
1858
  children: "\u2713"
@@ -1879,7 +1931,7 @@ var SelectionCard = {
1879
1931
  propsSchema: schema3,
1880
1932
  defaultProps: { submitLabel: "Continue", multiSelect: false }
1881
1933
  };
1882
- var styles14 = StyleSheet.create({
1934
+ var styles11 = StyleSheet.create({
1883
1935
  option: { borderWidth: 1.5, flexDirection: "row", alignItems: "center", overflow: "hidden" },
1884
1936
  selectedOverlay: { borderWidth: 1.5 },
1885
1937
  indicator: { width: 24, height: 24, borderWidth: 2, alignItems: "center", justifyContent: "center" },
@@ -1901,17 +1953,22 @@ var STATUS_ICONS = {
1901
1953
  info: "\u2139"
1902
1954
  };
1903
1955
  var _StatusCard = ({ status, title, message, ctaLabel, onContinue, onPress, onSubmit }) => {
1904
- var _a, _b;
1956
+ var _a;
1905
1957
  const t = useOnboardingTheme();
1906
1958
  const reduced = useReducedMotion();
1907
1959
  const [submitted, setSubmitted] = useState(false);
1908
- const colorFor = {
1909
- success: t.colors.success,
1910
- warning: t.colors.warning,
1911
- error: t.colors.error,
1912
- info: t.colors.info
1913
- };
1914
- const color = (_a = colorFor[status]) != null ? _a : t.colors.info;
1960
+ const color = useMemo(() => {
1961
+ switch (status) {
1962
+ case "success":
1963
+ return t.colors.success;
1964
+ case "warning":
1965
+ return t.colors.warning;
1966
+ case "error":
1967
+ return t.colors.error;
1968
+ default:
1969
+ return t.colors.info;
1970
+ }
1971
+ }, [status, t.colors]);
1915
1972
  const popT = useRef(new Animated.Value(reduced ? 1 : 0)).current;
1916
1973
  const copyT = useRef(new Animated.Value(reduced ? 1 : 0)).current;
1917
1974
  useEffect(() => {
@@ -1953,8 +2010,8 @@ var _StatusCard = ({ status, title, message, ctaLabel, onContinue, onPress, onSu
1953
2010
  {
1954
2011
  align: "center",
1955
2012
  footer: ctaLabel ? /* @__PURE__ */ jsx(Button, { title: ctaLabel, onPress: handleContinue, variant: "primary", disabled: submitted }) : void 0,
1956
- children: /* @__PURE__ */ jsxs(View, { style: [styles15.center, { gap: t.spacing.sm }], children: [
1957
- /* @__PURE__ */ jsxs(View, { style: [styles15.titleRow, { gap: t.spacing.sm }], children: [
2013
+ children: /* @__PURE__ */ jsxs(View, { style: [styles12.center, { gap: t.spacing.sm }], children: [
2014
+ /* @__PURE__ */ jsxs(View, { style: [styles12.titleRow, { gap: t.spacing.sm }], children: [
1958
2015
  /* @__PURE__ */ jsx(
1959
2016
  Animated.Text,
1960
2017
  {
@@ -1962,7 +2019,7 @@ var _StatusCard = ({ status, title, message, ctaLabel, onContinue, onPress, onSu
1962
2019
  headingStyle(t.fonts),
1963
2020
  { color, opacity: popT, transform: [{ scale: popScale }] }
1964
2021
  ],
1965
- children: (_b = STATUS_ICONS[status]) != null ? _b : "\xB7"
2022
+ children: (_a = STATUS_ICONS[status]) != null ? _a : "\xB7"
1966
2023
  }
1967
2024
  ),
1968
2025
  /* @__PURE__ */ jsx(
@@ -2001,7 +2058,7 @@ var StatusCard = {
2001
2058
  component: React14.memo(_StatusCard),
2002
2059
  propsSchema: schema4
2003
2060
  };
2004
- var styles15 = StyleSheet.create({
2061
+ var styles12 = StyleSheet.create({
2005
2062
  center: { width: "100%", alignItems: "center", justifyContent: "center" },
2006
2063
  titleRow: { flexDirection: "row", alignItems: "center", justifyContent: "center" }
2007
2064
  });
@@ -2026,6 +2083,7 @@ var _NumberStepperCard = ({
2026
2083
  onSubmit
2027
2084
  }) => {
2028
2085
  const t = useOnboardingTheme();
2086
+ const styles16 = useMemo(() => makeStyles4(t), [t]);
2029
2087
  const safeMin = safeNum(min, 1);
2030
2088
  const safeMaxRaw = safeNum(max, 30);
2031
2089
  const safeMax = safeMaxRaw < safeMin ? safeMin : safeMaxRaw;
@@ -2058,7 +2116,7 @@ var _NumberStepperCard = ({
2058
2116
  title: label,
2059
2117
  align: "center",
2060
2118
  footer: /* @__PURE__ */ jsx(Button, { title: submitLabel, onPress: handleSubmit, variant: "primary", disabled: submitted }),
2061
- children: /* @__PURE__ */ jsxs(View, { style: { gap: t.spacing.md, alignItems: "center" }, children: [
2119
+ children: /* @__PURE__ */ jsxs(View, { style: styles16.wrap, children: [
2062
2120
  /* @__PURE__ */ jsxs(View, { style: [styles16.stepper, { gap: t.spacing.lg }], children: [
2063
2121
  /* @__PURE__ */ jsx(Pressable, { onPress: atMin || submitted ? void 0 : handleDecrement, disabled: atMin || submitted, style: stepBtnStyle(atMin), children: /* @__PURE__ */ jsx(Text, { style: [headingStyle(t.fonts), { color: atMin ? t.colors.textMuted : t.colors.primary }], children: "\u2212" }) }),
2064
2122
  /* @__PURE__ */ jsxs(View, { style: styles16.valueBox, children: [
@@ -2079,11 +2137,14 @@ var NumberStepperCard = {
2079
2137
  propsSchema: schema5,
2080
2138
  defaultProps: { min: 1, max: 30, defaultValue: 1, step: 1, submitLabel: "Confirm" }
2081
2139
  };
2082
- var styles16 = StyleSheet.create({
2083
- stepper: { flexDirection: "row", alignItems: "center", justifyContent: "center" },
2084
- stepBtn: { width: 44, height: 44, borderWidth: 2, alignItems: "center", justifyContent: "center" },
2085
- valueBox: { alignItems: "center", minWidth: 80 }
2086
- });
2140
+ function makeStyles4(t) {
2141
+ return StyleSheet.create({
2142
+ wrap: { gap: t.spacing.md, alignItems: "center" },
2143
+ stepper: { flexDirection: "row", alignItems: "center", justifyContent: "center" },
2144
+ stepBtn: { width: 44, height: 44, borderWidth: 2, alignItems: "center", justifyContent: "center" },
2145
+ valueBox: { alignItems: "center", minWidth: 80 }
2146
+ });
2147
+ }
2087
2148
  var easeWire7 = Easing.bezier(...WIRE_BEZIER);
2088
2149
  var RISE_PX = 10;
2089
2150
  var schema6 = z.object({
@@ -2145,20 +2206,20 @@ var _WorkItem = ({
2145
2206
  return /* @__PURE__ */ jsxs(
2146
2207
  Animated.View,
2147
2208
  {
2148
- style: [styles17.itemRow, { gap: t.spacing.sm + t.spacing.xs, opacity: rowT, transform: [{ translateY: rise }] }],
2209
+ style: [styles13.itemRow, { gap: t.spacing.sm + t.spacing.xs, opacity: rowT, transform: [{ translateY: rise }] }],
2149
2210
  children: [
2150
2211
  /* @__PURE__ */ jsx(
2151
2212
  View,
2152
2213
  {
2153
2214
  style: [
2154
- styles17.itemBadge,
2215
+ styles13.itemBadge,
2155
2216
  { backgroundColor: t.colors.primarySoft, borderRadius: t.radius.full }
2156
2217
  ],
2157
2218
  children: /* @__PURE__ */ jsx(
2158
2219
  Animated.Text,
2159
2220
  {
2160
2221
  style: [
2161
- styles17.itemCheck,
2222
+ styles13.itemCheck,
2162
2223
  { color: t.colors.primary, opacity: checkT, transform: [{ scale: checkScale }] }
2163
2224
  ],
2164
2225
  children: "\u2713"
@@ -2166,7 +2227,7 @@ var _WorkItem = ({
2166
2227
  )
2167
2228
  }
2168
2229
  ),
2169
- /* @__PURE__ */ jsx(Text, { style: [bodyStyle(t.fonts), styles17.itemText, { color: t.colors.text }], children: text })
2230
+ /* @__PURE__ */ jsx(Text, { style: [bodyStyle(t.fonts), styles13.itemText, { color: t.colors.text }], children: text })
2170
2231
  ]
2171
2232
  }
2172
2233
  );
@@ -2244,18 +2305,18 @@ var _InterstitialCard = ({
2244
2305
  disabled: submitted || !canAdvance
2245
2306
  }
2246
2307
  ),
2247
- children: /* @__PURE__ */ jsxs(View, { style: [styles17.center, { gap: t.spacing.md }], children: [
2248
- /* @__PURE__ */ jsx(Animated.View, { style: [styles17.fullWidth, riseStyle(artT)], children: showImage ? /* @__PURE__ */ jsx(
2308
+ children: /* @__PURE__ */ jsxs(View, { style: [styles13.center, { gap: t.spacing.md }], children: [
2309
+ /* @__PURE__ */ jsx(Animated.View, { style: [styles13.fullWidth, riseStyle(artT)], children: showImage ? /* @__PURE__ */ jsx(
2249
2310
  Image,
2250
2311
  {
2251
2312
  source: { uri: imageUrl },
2252
- style: styles17.image,
2313
+ style: styles13.image,
2253
2314
  resizeMode: "contain",
2254
2315
  onError: () => setImgFailed(true)
2255
2316
  }
2256
- ) : art ? /* @__PURE__ */ jsx(View, { style: styles17.art, children: art }) : null }),
2317
+ ) : art ? /* @__PURE__ */ jsx(View, { style: styles13.art, children: art }) : null }),
2257
2318
  /* @__PURE__ */ jsx(Animated.View, { style: riseStyle(titleT), children: /* @__PURE__ */ jsx(Text, { style: [headingStyle(t.fonts), { color: t.colors.text, textAlign: "center" }], children: title }) }),
2258
- (items == null ? void 0 : items.length) ? /* @__PURE__ */ jsx(View, { style: [styles17.items, { gap: t.spacing.md, marginTop: t.spacing.sm }], children: items.map((item, i) => /* @__PURE__ */ jsx(WorkItem, { text: item, index: i, reduced }, item)) }) : body ? /* @__PURE__ */ jsx(Animated.View, { style: riseStyle(bodyT), children: /* @__PURE__ */ jsx(Text, { style: [bodyStyle(t.fonts), { color: t.colors.textMuted, textAlign: "center" }], children: body }) }) : null
2319
+ (items == null ? void 0 : items.length) ? /* @__PURE__ */ jsx(View, { style: [styles13.items, { gap: t.spacing.md, marginTop: t.spacing.sm }], children: items.map((item, i) => /* @__PURE__ */ jsx(WorkItem, { text: item, index: i, reduced }, `${item}-${i}`)) }) : body ? /* @__PURE__ */ jsx(Animated.View, { style: riseStyle(bodyT), children: /* @__PURE__ */ jsx(Text, { style: [bodyStyle(t.fonts), { color: t.colors.textMuted, textAlign: "center" }], children: body }) }) : null
2259
2320
  ] })
2260
2321
  }
2261
2322
  );
@@ -2267,7 +2328,7 @@ var InterstitialCard = {
2267
2328
  propsSchema: schema6,
2268
2329
  defaultProps: { ctaLabel: "Continue", illustrationFallback: "momentum" }
2269
2330
  };
2270
- var styles17 = StyleSheet.create({
2331
+ var styles13 = StyleSheet.create({
2271
2332
  center: { width: "100%", alignItems: "center", justifyContent: "center" },
2272
2333
  fullWidth: { width: "100%", alignItems: "center" },
2273
2334
  art: { alignItems: "center", justifyContent: "center" },
@@ -2288,6 +2349,57 @@ var onboardingComponents = [
2288
2349
  NumberStepperCard,
2289
2350
  InterstitialCard
2290
2351
  ];
2352
+
2353
+ // src/device/appVersion.ts
2354
+ var coerceVersion = (value) => {
2355
+ if (typeof value !== "string") return void 0;
2356
+ const trimmed = value.trim();
2357
+ return trimmed.length > 0 ? trimmed : void 0;
2358
+ };
2359
+ var runtimeRequire = (moduleName) => {
2360
+ try {
2361
+ if (typeof __require !== "function") return void 0;
2362
+ return __require(moduleName);
2363
+ } catch {
2364
+ return void 0;
2365
+ }
2366
+ };
2367
+ var interop = (mod) => {
2368
+ if (!mod || typeof mod !== "object") return void 0;
2369
+ const def = mod.default;
2370
+ if (def && typeof def === "object") return def;
2371
+ return mod;
2372
+ };
2373
+ var safeInterop = (requireModule, moduleName) => {
2374
+ try {
2375
+ return interop(requireModule(moduleName));
2376
+ } catch {
2377
+ return void 0;
2378
+ }
2379
+ };
2380
+ var detectAppVersion = (requireModule = runtimeRequire) => {
2381
+ try {
2382
+ const constants = safeInterop(requireModule, "expo-constants");
2383
+ if (constants) {
2384
+ const expoConfig = constants.expoConfig;
2385
+ if (expoConfig && typeof expoConfig === "object") {
2386
+ const fromExpoConfig = coerceVersion(expoConfig.version);
2387
+ if (fromExpoConfig) return fromExpoConfig;
2388
+ }
2389
+ const fromNative = coerceVersion(constants.nativeAppVersion);
2390
+ if (fromNative) return fromNative;
2391
+ }
2392
+ const application = safeInterop(requireModule, "expo-application");
2393
+ if (application) {
2394
+ const fromApplication = coerceVersion(application.nativeApplicationVersion);
2395
+ if (fromApplication) return fromApplication;
2396
+ }
2397
+ } catch {
2398
+ }
2399
+ return void 0;
2400
+ };
2401
+
2402
+ // src/device/deviceContext.ts
2291
2403
  var deriveFormFactor = (iosIdiom, width, height) => {
2292
2404
  if (iosIdiom === "pad") return "tablet";
2293
2405
  if (iosIdiom === "phone") return "phone";
@@ -2357,9 +2469,21 @@ var collectDeviceContext = () => {
2357
2469
  if (resolved.timeZone) ctx.timeZone = resolved.timeZone;
2358
2470
  } catch {
2359
2471
  }
2472
+ const appVersion = detectAppVersion();
2473
+ if (appVersion) ctx.appVersion = appVersion;
2360
2474
  return ctx;
2361
2475
  };
2362
2476
 
2477
+ // src/analytics/currentSession.ts
2478
+ var _currentSessionId;
2479
+ var setCurrentSessionId = (id) => {
2480
+ if (typeof id === "string" && id.length > 0) _currentSessionId = id;
2481
+ };
2482
+ var getCurrentSessionId = () => _currentSessionId;
2483
+ var resetCurrentSessionId = () => {
2484
+ _currentSessionId = void 0;
2485
+ };
2486
+
2363
2487
  // src/session/persistedSession.ts
2364
2488
  var DEFAULT_SESSION_TTL_MS = 36e5;
2365
2489
  var READ_TIMEOUT_MS = 1500;
@@ -2438,6 +2562,7 @@ var identifyOnboarding = async (opts) => {
2438
2562
  contextId = stored == null ? void 0 : stored.id;
2439
2563
  }
2440
2564
  }
2565
+ if (!contextId) contextId = getCurrentSessionId();
2441
2566
  if (!contextId) return false;
2442
2567
  reportClientEvent(
2443
2568
  { serverUrl: opts.config.serverUrl, apiKey: opts.config.apiKey },
@@ -2505,6 +2630,10 @@ var WireOnboarding = ({
2505
2630
  );
2506
2631
  const sessionId = (_a = session == null ? void 0 : session.id) != null ? _a : "";
2507
2632
  const startupUserIdRef = useRef(boundUserId);
2633
+ const metadataKey = config.metadata ? JSON.stringify(config.metadata) : "";
2634
+ const metadataStable = useMemo(() => metadataKey ? JSON.parse(metadataKey) : void 0, [metadataKey]);
2635
+ const componentNamesKey = (components != null ? components : onboardingComponents).map((c) => c.name).join(",");
2636
+ const componentsStable = useMemo(() => components != null ? components : onboardingComponents, [componentNamesKey]);
2508
2637
  const llm = useMemo(() => {
2509
2638
  const baseUrl = config.serverUrl.replace(/\/$/, "");
2510
2639
  return {
@@ -2517,9 +2646,9 @@ var WireOnboarding = ({
2517
2646
  // names of the components this device can actually render — so the backend only
2518
2647
  // emits cards the kit knows how to draw.
2519
2648
  metadata: {
2520
- ...config.metadata,
2649
+ ...metadataStable,
2521
2650
  sessionId,
2522
- supportedComponents: (components != null ? components : onboardingComponents).map((c) => c.name),
2651
+ supportedComponents: componentsStable.map((c) => c.name),
2523
2652
  // Device snapshot + host-injected user context ride the session-start metadata so the
2524
2653
  // backend can segment the funnel. Old servers ignore these unknown keys (backward compat).
2525
2654
  device,
@@ -2532,7 +2661,7 @@ var WireOnboarding = ({
2532
2661
  },
2533
2662
  timeoutMs: 6e4
2534
2663
  };
2535
- }, [config.serverUrl, config.appId, config.apiKey, config.metadata, sessionId, components, device, userContextStable]);
2664
+ }, [config.serverUrl, config.appId, config.apiKey, metadataStable, sessionId, componentsStable, device, userContextStable]);
2536
2665
  const reportTarget = useMemo(
2537
2666
  () => ({ serverUrl: config.serverUrl, apiKey: config.apiKey }),
2538
2667
  [config.serverUrl, config.apiKey]
@@ -2597,7 +2726,7 @@ function DemoOnboarding({
2597
2726
  const [open, setOpen] = useState(false);
2598
2727
  const [runId, setRunId] = useState(0);
2599
2728
  const t = useMemo(() => mergeTheme(theme != null ? theme : {}), [theme]);
2600
- const styles20 = useMemo(() => makeStyles(t), [t]);
2729
+ const styles16 = useMemo(() => makeStyles5(t), [t]);
2601
2730
  const openDemo = useCallback(() => {
2602
2731
  setRunId((n) => n + 1);
2603
2732
  setOpen(true);
@@ -2611,7 +2740,7 @@ function DemoOnboarding({
2611
2740
  [onComplete]
2612
2741
  );
2613
2742
  return /* @__PURE__ */ jsxs(Fragment, { children: [
2614
- renderTrigger ? renderTrigger(openDemo) : /* @__PURE__ */ jsx(Pressable, { onPress: openDemo, style: styles20.trigger, accessibilityRole: "button", children: /* @__PURE__ */ jsx(Text, { style: styles20.triggerText, children: label }) }),
2743
+ renderTrigger ? renderTrigger(openDemo) : /* @__PURE__ */ jsx(Pressable, { onPress: openDemo, style: styles16.trigger, accessibilityRole: "button", children: /* @__PURE__ */ jsx(Text, { style: styles16.triggerText, children: label }) }),
2615
2744
  /* @__PURE__ */ jsx(
2616
2745
  Modal,
2617
2746
  {
@@ -2619,7 +2748,7 @@ function DemoOnboarding({
2619
2748
  animationType: "slide",
2620
2749
  presentationStyle: "fullScreen",
2621
2750
  onRequestClose: close,
2622
- children: /* @__PURE__ */ jsx(View, { style: styles20.modal, children: config ? /* @__PURE__ */ jsx(
2751
+ children: /* @__PURE__ */ jsx(View, { style: styles16.modal, children: config ? /* @__PURE__ */ jsx(
2623
2752
  WireOnboarding,
2624
2753
  {
2625
2754
  config,
@@ -2632,16 +2761,16 @@ function DemoOnboarding({
2632
2761
  onError: close
2633
2762
  },
2634
2763
  runId
2635
- ) : /* @__PURE__ */ jsxs(View, { style: styles20.notice, children: [
2636
- /* @__PURE__ */ jsx(Text, { style: styles20.noticeTitle, children: "Wire AI not configured" }),
2637
- /* @__PURE__ */ jsx(Text, { style: styles20.noticeBody, children: "Set the Wire AI key + server URL (e.g. via wireConfigFromEnv) to demo the AI onboarding here." }),
2638
- /* @__PURE__ */ jsx(Pressable, { onPress: close, style: styles20.trigger, accessibilityRole: "button", children: /* @__PURE__ */ jsx(Text, { style: styles20.triggerText, children: "Close" }) })
2764
+ ) : /* @__PURE__ */ jsxs(View, { style: styles16.notice, children: [
2765
+ /* @__PURE__ */ jsx(Text, { style: styles16.noticeTitle, children: "Wire AI not configured" }),
2766
+ /* @__PURE__ */ jsx(Text, { style: styles16.noticeBody, children: "Set the Wire AI key + server URL (e.g. via wireConfigFromEnv) to demo the AI onboarding here." }),
2767
+ /* @__PURE__ */ jsx(Pressable, { onPress: close, style: styles16.trigger, accessibilityRole: "button", children: /* @__PURE__ */ jsx(Text, { style: styles16.triggerText, children: "Close" }) })
2639
2768
  ] }) })
2640
2769
  }
2641
2770
  )
2642
2771
  ] });
2643
2772
  }
2644
- function makeStyles(t) {
2773
+ function makeStyles5(t) {
2645
2774
  return StyleSheet.create({
2646
2775
  trigger: {
2647
2776
  backgroundColor: t.colors.primary,
@@ -2714,13 +2843,13 @@ var _DoneBlock = ({
2714
2843
  message = "Personalizing your experience\u2026"
2715
2844
  }) => {
2716
2845
  const t = useOnboardingTheme();
2717
- return /* @__PURE__ */ jsxs(View, { style: [styles18.center, { gap: t.spacing.sm, padding: t.spacing.lg }], children: [
2846
+ return /* @__PURE__ */ jsxs(View, { style: [styles14.center, { gap: t.spacing.sm, padding: t.spacing.lg }], children: [
2718
2847
  /* @__PURE__ */ jsx(Text, { style: [headingStyle(t.fonts), { color: t.colors.success, textAlign: "center" }], children: title }),
2719
2848
  /* @__PURE__ */ jsx(Text, { style: [bodyStyle(t.fonts), { color: t.colors.textMuted, textAlign: "center" }], children: message })
2720
2849
  ] });
2721
2850
  };
2722
2851
  var DoneBlock = React14.memo(_DoneBlock);
2723
- var styles18 = StyleSheet.create({
2852
+ var styles14 = StyleSheet.create({
2724
2853
  center: { flex: 1, alignItems: "center", justifyContent: "center" }
2725
2854
  });
2726
2855
 
@@ -2799,25 +2928,25 @@ var _CenteredModal = forwardRef(
2799
2928
  /* @__PURE__ */ jsx(
2800
2929
  Animated.View,
2801
2930
  {
2802
- style: [styles19.backdrop, { opacity: backdropOpacity }],
2931
+ style: [styles15.backdrop, { opacity: backdropOpacity }],
2803
2932
  pointerEvents: "none"
2804
2933
  }
2805
2934
  ),
2806
2935
  /* @__PURE__ */ jsx(
2807
2936
  Pressable,
2808
2937
  {
2809
- style: styles19.fill,
2938
+ style: styles15.fill,
2810
2939
  onPress: onBackdrop,
2811
2940
  accessibilityRole: "button",
2812
2941
  accessibilityLabel: "Dismiss",
2813
- children: /* @__PURE__ */ jsx(SafeAreaView, { style: styles19.center, pointerEvents: "box-none", children: /* @__PURE__ */ jsx(Pressable, { onPress: () => {
2814
- }, style: styles19.cardWrap, children: /* @__PURE__ */ jsx(
2942
+ children: /* @__PURE__ */ jsx(SafeAreaView, { style: styles15.center, pointerEvents: "box-none", children: /* @__PURE__ */ jsx(Pressable, { onPress: () => {
2943
+ }, style: styles15.cardWrap, children: /* @__PURE__ */ jsx(
2815
2944
  Animated.View,
2816
2945
  {
2817
2946
  accessibilityViewIsModal: true,
2818
2947
  accessibilityRole: "alert",
2819
2948
  style: [
2820
- styles19.card,
2949
+ styles15.card,
2821
2950
  {
2822
2951
  backgroundColor: theme.colors.surface,
2823
2952
  borderRadius: theme.radius.lg,
@@ -2839,7 +2968,7 @@ var _CenteredModal = forwardRef(
2839
2968
  );
2840
2969
  _CenteredModal.displayName = "CenteredModal";
2841
2970
  var CenteredModal = React14.memo(_CenteredModal);
2842
- var styles19 = StyleSheet.create({
2971
+ var styles15 = StyleSheet.create({
2843
2972
  fill: { flex: 1 },
2844
2973
  backdrop: { position: "absolute", top: 0, left: 0, right: 0, bottom: 0, backgroundColor: "#000" },
2845
2974
  center: { flex: 1, alignItems: "center", justifyContent: "center", padding: 24 },
@@ -2993,7 +3122,12 @@ var useWireFeatures = (config) => {
2993
3122
  }, [canFetch, stableConfig]);
2994
3123
  return flags;
2995
3124
  };
2996
- var WireFeaturesContext = createContext(null);
3125
+ var CONTEXT_SYMBOL = /* @__PURE__ */ Symbol.for("wireai.features.context");
3126
+ var globalObj = global;
3127
+ if (!globalObj[CONTEXT_SYMBOL]) {
3128
+ globalObj[CONTEXT_SYMBOL] = createContext(null);
3129
+ }
3130
+ var WireFeaturesContext = globalObj[CONTEXT_SYMBOL];
2997
3131
  var WireFeaturesProvider = ({
2998
3132
  config,
2999
3133
  flags,
@@ -3085,6 +3219,80 @@ var attributionMetadata = (a) => {
3085
3219
  return { attribution };
3086
3220
  };
3087
3221
 
3222
+ // src/context/userContext.ts
3223
+ var RESERVED_USER_CONTEXT_KEYS = [
3224
+ "device_key",
3225
+ "app_version",
3226
+ "app_build",
3227
+ "network_type",
3228
+ "session_count",
3229
+ "returning",
3230
+ "platform",
3231
+ "user_email",
3232
+ "user_email_hashed"
3233
+ ];
3234
+ var EXTRA_KEY_PREFIX = "custom.";
3235
+ var isWireScalar = (value) => {
3236
+ const t = typeof value;
3237
+ if (t === "string" || t === "boolean") return true;
3238
+ if (t === "number") return Number.isFinite(value);
3239
+ return false;
3240
+ };
3241
+ var hashEmailFnv1a = (email) => {
3242
+ const normalized = email.trim().toLowerCase();
3243
+ let hash = 2166136261;
3244
+ for (let i = 0; i < normalized.length; i++) {
3245
+ hash ^= normalized.charCodeAt(i);
3246
+ hash = Math.imul(hash, 16777619);
3247
+ }
3248
+ return (hash >>> 0).toString(16).padStart(8, "0");
3249
+ };
3250
+ var cleanString = (value) => {
3251
+ if (typeof value !== "string") return void 0;
3252
+ const trimmed = value.trim();
3253
+ return trimmed.length > 0 ? trimmed : void 0;
3254
+ };
3255
+ var namespaceExtra = (extra) => {
3256
+ const out = {};
3257
+ if (!extra || typeof extra !== "object") return out;
3258
+ for (const [key, value] of Object.entries(extra)) {
3259
+ const cleanKey = cleanString(key);
3260
+ if (!cleanKey) continue;
3261
+ if (!isWireScalar(value)) continue;
3262
+ out[`${EXTRA_KEY_PREFIX}${cleanKey}`] = value;
3263
+ }
3264
+ return out;
3265
+ };
3266
+ var resolveUserContext = (ctx = {}, opts = {}) => {
3267
+ var _a;
3268
+ const result = {};
3269
+ const bucket = {};
3270
+ const userId = sanitizeUserId(ctx.userId);
3271
+ if (userId) result.userId = userId;
3272
+ const deviceKey = cleanString(ctx.deviceKey);
3273
+ if (deviceKey) {
3274
+ result.deviceKey = deviceKey;
3275
+ bucket.device_key = deviceKey;
3276
+ }
3277
+ const appVersion = (_a = cleanString(ctx.appVersion)) != null ? _a : cleanString(opts.autoAppVersion);
3278
+ if (appVersion) {
3279
+ result.appVersion = appVersion;
3280
+ bucket.app_version = appVersion;
3281
+ }
3282
+ const email = cleanString(ctx.userEmail);
3283
+ if (email) {
3284
+ if (ctx.hashEmail) {
3285
+ bucket.user_email = hashEmailFnv1a(email);
3286
+ bucket.user_email_hashed = true;
3287
+ } else {
3288
+ bucket.user_email = email;
3289
+ }
3290
+ }
3291
+ Object.assign(bucket, namespaceExtra(ctx.extra));
3292
+ if (Object.keys(bucket).length > 0) result.userContext = bucket;
3293
+ return result;
3294
+ };
3295
+
3088
3296
  // src/session-analytics/reportSessionStart.ts
3089
3297
  var SESSION_STARTED_EVENT = "app.session_started";
3090
3298
  var _emitted = /* @__PURE__ */ new Set();
@@ -3097,6 +3305,7 @@ var reportSessionStart = (opts) => {
3097
3305
  const target = opts.target;
3098
3306
  if (!opts.sink && !(target == null ? void 0 : target.serverUrl)) return;
3099
3307
  const sessionId = (_a = opts.sessionId) != null ? _a : makeSessionId();
3308
+ setCurrentSessionId(sessionId);
3100
3309
  if (opts.once !== false) {
3101
3310
  if (_emitted.has(sessionId)) return;
3102
3311
  if (_emitted.size >= _GUARD_MAX) {
@@ -3153,7 +3362,7 @@ var useSessionStart = (config, options = {}) => {
3153
3362
  if (opts.enabled === false) return;
3154
3363
  const target = { serverUrl: cfg.serverUrl, apiKey: (_a = cfg.apiKey) != null ? _a : "" };
3155
3364
  const device = collectDeviceContext();
3156
- if (cfg.appVersion && !device.appVersion) device.appVersion = cfg.appVersion;
3365
+ if (cfg.appVersion) device.appVersion = cfg.appVersion;
3157
3366
  reportSessionStart({
3158
3367
  target,
3159
3368
  // A fresh per-open id each fire; the emitter's once-guard dedupes within the open.
@@ -3406,6 +3615,8 @@ var createEventQueue = (options) => {
3406
3615
  })();
3407
3616
  const postBatch = async (events) => {
3408
3617
  if (!(target == null ? void 0 : target.serverUrl) || events.length === 0) return false;
3618
+ const controller = typeof AbortController !== "undefined" ? new AbortController() : void 0;
3619
+ const timer = setTimeout(() => controller == null ? void 0 : controller.abort(), 15e3);
3409
3620
  try {
3410
3621
  const url = `${target.serverUrl.replace(/\/$/, "")}/v1/events`;
3411
3622
  const headers = { "Content-Type": "application/json" };
@@ -3413,11 +3624,14 @@ var createEventQueue = (options) => {
3413
3624
  const res = await fetch(url, {
3414
3625
  method: "POST",
3415
3626
  headers,
3416
- body: JSON.stringify({ events })
3627
+ body: JSON.stringify({ events }),
3628
+ signal: controller == null ? void 0 : controller.signal
3417
3629
  });
3418
3630
  return !!(res && res.ok);
3419
3631
  } catch {
3420
3632
  return false;
3633
+ } finally {
3634
+ clearTimeout(timer);
3421
3635
  }
3422
3636
  };
3423
3637
  const clearRetry = () => {
@@ -3522,7 +3736,7 @@ var useLifecycleEvents = (config, options = {}) => {
3522
3736
  const { config: cfg, options: opts } = latest.current;
3523
3737
  if (opts.enabled !== false) {
3524
3738
  const device = collectDeviceContext();
3525
- if ((cfg == null ? void 0 : cfg.appVersion) && !device.appVersion) device.appVersion = cfg.appVersion;
3739
+ if (cfg == null ? void 0 : cfg.appVersion) device.appVersion = cfg.appVersion;
3526
3740
  reportFirstOpen({
3527
3741
  target: targetOf(cfg),
3528
3742
  sink: resolveSink(),
@@ -3544,7 +3758,7 @@ var useLifecycleEvents = (config, options = {}) => {
3544
3758
  if (opts.enabled === false) return;
3545
3759
  if (!(cfg == null ? void 0 : cfg.serverUrl) && !opts.sink) return;
3546
3760
  const device = collectDeviceContext();
3547
- if ((cfg == null ? void 0 : cfg.appVersion) && !device.appVersion) device.appVersion = cfg.appVersion;
3761
+ if (cfg == null ? void 0 : cfg.appVersion) device.appVersion = cfg.appVersion;
3548
3762
  reportSessionStart({
3549
3763
  target: targetOf(cfg),
3550
3764
  sink: resolveSink(),
@@ -3578,6 +3792,6 @@ var useLifecycleEvents = (config, options = {}) => {
3578
3792
  }, []);
3579
3793
  };
3580
3794
 
3581
- export { AnimatedSparkle, BACKGROUND_SESSION_MS, CardHandoff, CenteredModal, ChipSelectCard, CompletionView, DEFAULT_FEATURES_TTL_MS, DEFAULT_SESSION_TTL_MS, DemoOnboarding, DoneBlock, ErrorBlock, FIRST_OPEN_EVENT, IllustrationProvider, InterstitialCard, LoadingBlock, LoadingScreen, NumberStepperCard, Button as OnboardingButton, OnboardingFlow, OnboardingScaffold, OnboardingThemeProvider, SESSION_STARTED_EVENT, SelectionCard, StatusCard, StepProgress, TextInputCard, USER_ID_MAX_LENGTH, WIRE_ONBOARDING_EVENTS, WireFeaturesProvider, WireOnboarding, attributionMetadata, clearPersistedSession, collectDeviceContext, defaultIllustrations, defaultOnboardingTheme, defaultWireFeatures, deriveAnswers, featuresCacheKey, featuresEqual, fetchWireFeatures, firstOpenStorageKey, identifyOnboarding, isFeaturesFresh, isOnboardingEnabled, loadPersistedSession, makeSessionId, mergeTheme, motionSpec_exports as motionSpec, onboardingComponents, parseWireFeatures, peekPersistedSession, readCachedFeatures, readProgress, reportClientEvent, reportClientEvents, reportFirstOpen, reportSessionStart, resetFirstOpenLatch, resetSessionStartGuard, sanitizeUserId, savePersistedSession, sessionStorageKey, themeFromBrand, toAnalyticsEvent, useIllustration, useLifecycleEvents, useOnboardingTheme, useReducedMotion, useResolvedFeatures, useSessionStart, useWireFeatures, useWireFeaturesContext, wireConfigFromEnv, wireLifecycleEvents, writeCachedFeatures };
3795
+ export { AnimatedSparkle, BACKGROUND_SESSION_MS, CardHandoff, CenteredModal, ChipSelectCard, CompletionView, DEFAULT_FEATURES_TTL_MS, DEFAULT_SESSION_TTL_MS, DemoOnboarding, DoneBlock, EXTRA_KEY_PREFIX, ErrorBlock, FIRST_OPEN_EVENT, IllustrationProvider, InterstitialCard, LoadingBlock, LoadingScreen, NumberStepperCard, Button as OnboardingButton, OnboardingFlow, OnboardingScaffold, OnboardingThemeProvider, RESERVED_USER_CONTEXT_KEYS, SESSION_STARTED_EVENT, SelectionCard, StatusCard, StepProgress, TextInputCard, USER_ID_MAX_LENGTH, WIRE_ONBOARDING_EVENTS, WireFeaturesProvider, WireOnboarding, attributionMetadata, clearPersistedSession, collectDeviceContext, defaultIllustrations, defaultOnboardingTheme, defaultWireFeatures, deriveAnswers, detectAppVersion, featuresCacheKey, featuresEqual, fetchWireFeatures, firstOpenStorageKey, getCurrentSessionId, hashEmailFnv1a, identifyOnboarding, isFeaturesFresh, isOnboardingEnabled, isWireScalar, loadPersistedSession, makeSessionId, mergeTheme, motionSpec_exports as motionSpec, namespaceExtra, onboardingComponents, parseWireFeatures, peekPersistedSession, readCachedFeatures, readProgress, reportClientEvent, reportClientEvents, reportFirstOpen, reportSessionStart, resetCurrentSessionId, resetFirstOpenLatch, resetSessionStartGuard, resolveUserContext, sanitizeUserId, savePersistedSession, sessionStorageKey, setCurrentSessionId, themeFromBrand, toAnalyticsEvent, useIllustration, useLifecycleEvents, useOnboardingTheme, useReducedMotion, useResolvedFeatures, useSessionStart, useWireFeatures, useWireFeaturesContext, wireConfigFromEnv, wireLifecycleEvents, writeCachedFeatures };
3582
3796
  //# sourceMappingURL=index.mjs.map
3583
3797
  //# sourceMappingURL=index.mjs.map