@wireai/activation 0.2.0 → 0.4.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 (63) hide show
  1. package/dist/analytics/index.d.mts +69 -2
  2. package/dist/analytics/index.d.ts +69 -2
  3. package/dist/analytics/index.js +123 -2
  4. package/dist/analytics/index.js.map +1 -1
  5. package/dist/analytics/index.mjs +122 -3
  6. package/dist/analytics/index.mjs.map +1 -1
  7. package/dist/coachmarks/index.js +6 -1
  8. package/dist/coachmarks/index.js.map +1 -1
  9. package/dist/coachmarks/index.mjs +6 -1
  10. package/dist/coachmarks/index.mjs.map +1 -1
  11. package/dist/{eventQueue-CxKi7Qd5.d.mts → eventQueue-CA1d8Fmn.d.mts} +7 -3
  12. package/dist/{eventQueue-rV1dtJJR.d.ts → eventQueue-CrNB9gzH.d.ts} +7 -3
  13. package/dist/index.d.mts +5 -4
  14. package/dist/index.d.ts +5 -4
  15. package/dist/index.js +220 -151
  16. package/dist/index.js.map +1 -1
  17. package/dist/index.mjs +221 -152
  18. package/dist/index.mjs.map +1 -1
  19. package/dist/questionnaire/index.d.mts +1 -1
  20. package/dist/questionnaire/index.d.ts +1 -1
  21. package/dist/questionnaire/index.js +29 -5
  22. package/dist/questionnaire/index.js.map +1 -1
  23. package/dist/questionnaire/index.mjs +30 -6
  24. package/dist/questionnaire/index.mjs.map +1 -1
  25. package/dist/reviews/index.d.mts +1 -1
  26. package/dist/reviews/index.d.ts +1 -1
  27. package/dist/reviews/index.js +40 -9
  28. package/dist/reviews/index.js.map +1 -1
  29. package/dist/reviews/index.mjs +42 -11
  30. package/dist/reviews/index.mjs.map +1 -1
  31. package/dist/showcase/index.d.mts +1 -14
  32. package/dist/showcase/index.d.ts +1 -14
  33. package/dist/showcase/index.js +8 -2
  34. package/dist/showcase/index.js.map +1 -1
  35. package/dist/showcase/index.mjs +8 -2
  36. package/dist/showcase/index.mjs.map +1 -1
  37. package/package.json +3 -2
  38. package/src/OnboardingFlow.tsx +8 -4
  39. package/src/WireOnboarding.tsx +8 -3
  40. package/src/analytics/analyticsFacade.ts +184 -0
  41. package/src/analytics/eventQueue.ts +5 -0
  42. package/src/analytics/index.ts +12 -0
  43. package/src/analytics/reportClientEvent.ts +11 -2
  44. package/src/analytics/useAnalytics.ts +42 -0
  45. package/src/analytics/useScreenTracking.ts +23 -1
  46. package/src/cards/InterstitialCard.tsx +1 -1
  47. package/src/cards/NumberStepperCard.tsx +12 -7
  48. package/src/cards/StatusCard.tsx +13 -8
  49. package/src/cards/TextInputCard.tsx +10 -5
  50. package/src/components/AnimatedSparkle.tsx +20 -3
  51. package/src/components/Button.tsx +10 -1
  52. package/src/components/CardHandoff.tsx +2 -6
  53. package/src/components/CardLayout.tsx +16 -10
  54. package/src/components/Illustration.tsx +9 -5
  55. package/src/components/LoadingBlock.tsx +44 -29
  56. package/src/components/LoadingScreen.tsx +3 -2
  57. package/src/components/OnboardingScaffold.tsx +21 -23
  58. package/src/features/WireFeaturesProvider.tsx +7 -2
  59. package/src/questionnaire/QuestionnaireGate.tsx +15 -3
  60. package/src/reviews/ReviewGate.tsx +27 -7
  61. package/src/reviews/ReviewModal.tsx +4 -0
  62. package/src/showcase/FeatureShowcase.tsx +2 -1
  63. 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';
@@ -79,11 +79,15 @@ var useOnboardingTheme = () => {
79
79
  const ctx = useContext(OnboardingThemeContext);
80
80
  return ctx != null ? ctx : mergeTheme();
81
81
  };
82
- var IllustrationContext = createContext({});
82
+ var EMPTY_REGISTRY = {};
83
+ var IllustrationContext = createContext(EMPTY_REGISTRY);
83
84
  var IllustrationProvider = ({
84
85
  registry,
85
86
  children
86
- }) => /* @__PURE__ */ jsx(IllustrationContext.Provider, { value: registry != null ? registry : {}, children });
87
+ }) => {
88
+ const value = useMemo(() => registry != null ? registry : EMPTY_REGISTRY, [registry]);
89
+ return /* @__PURE__ */ jsx(IllustrationContext.Provider, { value, children });
90
+ };
87
91
  var useIllustration = (name) => {
88
92
  const registry = useContext(IllustrationContext);
89
93
  if (!name) return void 0;
@@ -374,23 +378,33 @@ var _AnimatedSparkle = ({ size = 30, color, variant = "spin" }) => {
374
378
  outputRange: [1, LOADER_PULSE_MIN_OPACITY]
375
379
  });
376
380
  const animatedStyle = reduced ? void 0 : variant === "pulse" ? { opacity: pulse } : { transform: [{ rotate }] };
377
- return /* @__PURE__ */ jsx(Animated.View, { style: animatedStyle, children: /* @__PURE__ */ jsx(
381
+ return /* @__PURE__ */ jsx(Animated.View, { style: [styles2.box, { width: size, height: size }, animatedStyle], children: /* @__PURE__ */ jsx(
378
382
  Text,
379
383
  {
380
384
  accessibilityElementsHidden: true,
381
385
  importantForAccessibility: "no",
382
- style: [styles2.glyph, { fontSize: size, lineHeight: size * 1.1, color: color != null ? color : t.colors.primary }],
386
+ style: [
387
+ styles2.glyph,
388
+ {
389
+ width: size,
390
+ fontSize: size,
391
+ lineHeight: size,
392
+ color: color != null ? color : t.colors.primary
393
+ }
394
+ ],
383
395
  children: "\u2726"
384
396
  }
385
397
  ) });
386
398
  };
387
399
  var AnimatedSparkle = React14.memo(_AnimatedSparkle);
388
400
  var styles2 = StyleSheet.create({
389
- glyph: { textAlign: "center" }
401
+ box: { alignItems: "center", justifyContent: "center" },
402
+ glyph: {
403
+ textAlign: "center",
404
+ textAlignVertical: "center",
405
+ includeFontPadding: false
406
+ }
390
407
  });
391
- var LoaderChromeContext = createContext({ offsetY: 0 });
392
- var LoaderChromeProvider = LoaderChromeContext.Provider;
393
- var useLoaderChrome = () => useContext(LoaderChromeContext);
394
408
  var STAGE_SIZE = 96;
395
409
  var ORBIT_DOT = 9;
396
410
  var OrbitDot = ({ color, reduced }) => {
@@ -454,39 +468,45 @@ var _LoadingBlock = ({
454
468
  }) => {
455
469
  const t = useOnboardingTheme();
456
470
  const reduced = useReducedMotion();
457
- const { offsetY } = useLoaderChrome();
458
471
  const [showSkeleton, setShowSkeleton] = useState(false);
459
472
  useEffect(() => {
460
473
  const timer = setTimeout(() => setShowSkeleton(true), SKELETON_DELAY_MS);
461
474
  return () => clearTimeout(timer);
462
475
  }, []);
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
- );
476
+ return /* @__PURE__ */ jsxs(View, { style: [styles3.center, { padding: t.spacing.lg }], children: [
477
+ /* @__PURE__ */ jsxs(View, { style: styles3.stage, children: [
478
+ /* @__PURE__ */ jsx(View, { style: [styles3.ring, { borderColor: t.colors.border, borderRadius: t.radius.full }] }),
479
+ /* @__PURE__ */ jsx(OrbitDot, { color: t.colors.primary, reduced }),
480
+ /* @__PURE__ */ jsx(AnimatedSparkle, { size: 40, variant: "pulse" })
481
+ ] }),
482
+ /* @__PURE__ */ jsxs(
483
+ View,
484
+ {
485
+ style: [
486
+ styles3.labels,
487
+ { marginTop: STAGE_SIZE / 2 + t.spacing.lg, gap: t.spacing.md, paddingHorizontal: t.spacing.lg }
488
+ ],
489
+ pointerEvents: "none",
490
+ children: [
491
+ title ? /* @__PURE__ */ jsx(Text, { style: [headingStyle(t.fonts), { color: t.colors.text, textAlign: "center" }], children: title }) : null,
492
+ hint ? /* @__PURE__ */ jsx(Text, { style: [bodyStyle(t.fonts), { color: t.colors.textMuted, textAlign: "center" }], children: hint }) : null,
493
+ showSkeleton ? /* @__PURE__ */ jsxs(View, { style: [styles3.skeleton, { gap: t.spacing.sm, marginTop: t.spacing.sm }], children: [
494
+ /* @__PURE__ */ jsx(SkeletonLine, { width: "80%", delay: 0, color: t.colors.surface, reduced }),
495
+ /* @__PURE__ */ jsx(SkeletonLine, { width: "60%", delay: 200, color: t.colors.surface, reduced })
496
+ ] }) : null
497
+ ]
498
+ }
499
+ )
500
+ ] });
486
501
  };
487
502
  var LoadingBlock = React14.memo(_LoadingBlock);
488
503
  var styles3 = StyleSheet.create({
489
504
  center: { flex: 1, alignItems: "center", justifyContent: "center" },
505
+ // Absolutely-positioned label column, pinned just under the centered stage: its top
506
+ // edge starts at the flow center (top: 50%) and the inline marginTop (STAGE_SIZE/2 +
507
+ // a gap) drops it below the stage. Out of flow → it never moves the stage, so the
508
+ // 300ms skeleton reveal grows DOWNWARD only and the icon stays centered.
509
+ labels: { position: "absolute", top: "50%", left: 0, right: 0, alignItems: "center" },
490
510
  stage: {
491
511
  width: STAGE_SIZE,
492
512
  height: STAGE_SIZE,
@@ -569,7 +589,6 @@ var styles5 = StyleSheet.create({
569
589
  track: { height: 6, width: "100%", overflow: "hidden" },
570
590
  fill: { height: 6 }
571
591
  });
572
- var PROGRESS_TRACK = 6;
573
592
  var _OnboardingScaffold = ({
574
593
  step,
575
594
  complete,
@@ -581,33 +600,41 @@ var _OnboardingScaffold = ({
581
600
  children
582
601
  }) => {
583
602
  const t = useOnboardingTheme();
603
+ const styles16 = useMemo(() => makeStyles(t), [t]);
584
604
  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
605
  return /* @__PURE__ */ jsxs(
588
606
  SafeAreaView,
589
607
  {
590
608
  edges: ["top", "bottom"],
591
- style: [styles6.flex, { backgroundColor: t.colors.background }],
609
+ style: [styles16.flex, { backgroundColor: t.colors.background }],
592
610
  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
611
+ /* @__PURE__ */ jsxs(View, { style: [styles16.header, { paddingHorizontal: t.spacing.md, paddingTop: t.spacing.md }], children: [
612
+ /* @__PURE__ */ jsx(View, { style: styles16.progressWrap, children: /* @__PURE__ */ jsx(StepProgress, { step, complete, approxScreens }) }),
613
+ 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
614
  ] }),
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
615
+ /* @__PURE__ */ jsx(
616
+ View,
617
+ {
618
+ style: [styles16.body, { paddingHorizontal: t.spacing.md, paddingTop: t.spacing.md }],
619
+ children
620
+ }
621
+ ),
622
+ 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
623
  ]
600
624
  }
601
625
  );
602
626
  };
603
627
  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
- });
628
+ function makeStyles(t) {
629
+ return StyleSheet.create({
630
+ flex: { flex: 1 },
631
+ header: { flexDirection: "row", alignItems: "center" },
632
+ progressWrap: { flex: 1 },
633
+ body: { flex: 1 },
634
+ footer: { flexDirection: "row", justifyContent: "flex-start" },
635
+ skip: { marginLeft: t.spacing.md }
636
+ });
637
+ }
611
638
  var _Button = ({
612
639
  title,
613
640
  onPress,
@@ -638,6 +665,14 @@ var _Button = ({
638
665
  },
639
666
  [pressT, reduced]
640
667
  );
668
+ const lastPress = useRef(0);
669
+ const handlePress = useCallback(() => {
670
+ if (!onPress) return;
671
+ const now = Date.now();
672
+ if (now - lastPress.current < 500) return;
673
+ lastPress.current = now;
674
+ onPress();
675
+ }, [onPress]);
641
676
  const handlePressIn = useCallback(() => pressTo(1), [pressTo]);
642
677
  const handlePressOut = useCallback(() => pressTo(0), [pressTo]);
643
678
  const scale = pressT.interpolate({
@@ -651,19 +686,19 @@ var _Button = ({
651
686
  return /* @__PURE__ */ jsx(
652
687
  Pressable,
653
688
  {
654
- onPress: disabled ? void 0 : onPress,
689
+ onPress: disabled ? void 0 : handlePress,
655
690
  onPressIn: disabled ? void 0 : handlePressIn,
656
691
  onPressOut: disabled ? void 0 : handlePressOut,
657
692
  disabled,
658
693
  accessibilityRole: "button",
659
694
  accessibilityState: { disabled },
660
- style: fullWidth ? styles7.fill : void 0,
695
+ style: fullWidth ? styles6.fill : void 0,
661
696
  children: /* @__PURE__ */ jsx(
662
697
  Animated.View,
663
698
  {
664
699
  style: [
665
- styles7.base,
666
- fullWidth && styles7.fill,
700
+ styles6.base,
701
+ fullWidth && styles6.fill,
667
702
  {
668
703
  borderRadius: t.button.radius,
669
704
  minHeight: t.button.height,
@@ -691,7 +726,7 @@ var _Button = ({
691
726
  );
692
727
  };
693
728
  var Button = React14.memo(_Button);
694
- var styles7 = StyleSheet.create({
729
+ var styles6 = StyleSheet.create({
695
730
  base: { alignItems: "center", justifyContent: "center" },
696
731
  fill: { width: "100%" }
697
732
  });
@@ -702,14 +737,14 @@ var _ErrorBlock = ({
702
737
  onRetry
703
738
  }) => {
704
739
  const t = useOnboardingTheme();
705
- return /* @__PURE__ */ jsxs(View, { style: [styles8.center, { gap: t.spacing.md, padding: t.spacing.lg }], children: [
740
+ return /* @__PURE__ */ jsxs(View, { style: [styles7.center, { gap: t.spacing.md, padding: t.spacing.lg }], children: [
706
741
  /* @__PURE__ */ jsx(Text, { style: [headingStyle(t.fonts), { color: t.colors.text, textAlign: "center" }], children: title }),
707
742
  /* @__PURE__ */ jsx(Text, { style: [bodyStyle(t.fonts), { color: t.colors.textMuted, textAlign: "center" }], children: message }),
708
743
  onRetry ? /* @__PURE__ */ jsx(Button, { title: retryLabel, onPress: onRetry, variant: "outline" }) : null
709
744
  ] });
710
745
  };
711
746
  var ErrorBlock = React14.memo(_ErrorBlock);
712
- var styles8 = StyleSheet.create({
747
+ var styles7 = StyleSheet.create({
713
748
  center: { flex: 1, alignItems: "center", justifyContent: "center" }
714
749
  });
715
750
  var _CardLayout = ({
@@ -721,9 +756,10 @@ var _CardLayout = ({
721
756
  titleNode
722
757
  }) => {
723
758
  const t = useOnboardingTheme();
759
+ const styles16 = useMemo(() => makeStyles2(t), [t]);
724
760
  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: [
761
+ return /* @__PURE__ */ jsxs(KeyboardAvoidingView, { style: styles16.fill, behavior: "padding", children: [
762
+ hasHeader ? /* @__PURE__ */ jsxs(View, { style: styles16.header, children: [
727
763
  titleNode != null ? titleNode : title ? /* @__PURE__ */ jsx(Text, { style: [labelStyle(t.fonts), { color: t.colors.text }], children: title }) : null,
728
764
  subtitle ? /* @__PURE__ */ jsx(
729
765
  Text,
@@ -739,10 +775,10 @@ var _CardLayout = ({
739
775
  /* @__PURE__ */ jsx(
740
776
  ScrollView,
741
777
  {
742
- style: styles9.fill,
778
+ style: styles16.fill,
743
779
  contentContainerStyle: [
744
- styles9.scrollContent,
745
- align === "center" && styles9.center
780
+ styles16.scrollContent,
781
+ align === "center" && styles16.center
746
782
  ],
747
783
  keyboardShouldPersistTaps: "handled",
748
784
  keyboardDismissMode: "on-drag",
@@ -750,17 +786,21 @@ var _CardLayout = ({
750
786
  children
751
787
  }
752
788
  ),
753
- footer ? /* @__PURE__ */ jsx(View, { style: { paddingTop: t.spacing.lg }, children: footer }) : null
789
+ footer ? /* @__PURE__ */ jsx(View, { style: styles16.footer, children: footer }) : null
754
790
  ] });
755
791
  };
756
792
  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
- });
793
+ function makeStyles2(t) {
794
+ return StyleSheet.create({
795
+ fill: { flex: 1 },
796
+ header: { marginBottom: t.spacing.lg },
797
+ footer: { paddingTop: t.spacing.lg },
798
+ // flexGrow lets a short content block grow to fill (pinning the footer) while a
799
+ // tall one scrolls past the visible area.
800
+ scrollContent: { flexGrow: 1 },
801
+ center: { justifyContent: "center" }
802
+ });
803
+ }
764
804
  var easeWire2 = Easing.bezier(...WIRE_BEZIER);
765
805
  var BADGE_SIZE = 64;
766
806
  var BURST_SIZE = 104;
@@ -835,14 +875,14 @@ var _CompletionView = ({
835
875
  {
836
876
  align: "center",
837
877
  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: [
878
+ children: /* @__PURE__ */ jsxs(View, { style: [styles8.center, { gap: t.spacing.md }], children: [
879
+ /* @__PURE__ */ jsxs(View, { style: styles8.badgeStage, children: [
840
880
  /* @__PURE__ */ jsx(
841
881
  Animated.View,
842
882
  {
843
883
  pointerEvents: "none",
844
884
  style: [
845
- styles10.burst,
885
+ styles8.burst,
846
886
  {
847
887
  backgroundColor: t.colors.primarySoft,
848
888
  borderRadius: t.radius.full,
@@ -856,7 +896,7 @@ var _CompletionView = ({
856
896
  Animated.View,
857
897
  {
858
898
  style: [
859
- styles10.badge,
899
+ styles8.badge,
860
900
  {
861
901
  backgroundColor: t.colors.primarySoft,
862
902
  borderRadius: t.radius.full,
@@ -864,7 +904,7 @@ var _CompletionView = ({
864
904
  opacity: pop
865
905
  }
866
906
  ],
867
- children: /* @__PURE__ */ jsx(Text, { style: [styles10.check, { color: t.colors.primary }], children: "\u2713" })
907
+ children: /* @__PURE__ */ jsx(Text, { style: [styles8.check, { color: t.colors.primary }], children: "\u2713" })
868
908
  }
869
909
  )
870
910
  ] }),
@@ -875,7 +915,7 @@ var _CompletionView = ({
875
915
  );
876
916
  };
877
917
  var CompletionView = React14.memo(_CompletionView);
878
- var styles10 = StyleSheet.create({
918
+ var styles8 = StyleSheet.create({
879
919
  center: { width: "100%", alignItems: "center", justifyContent: "center" },
880
920
  badgeStage: {
881
921
  width: BURST_SIZE,
@@ -898,7 +938,6 @@ var _CardHandoff = ({
898
938
  const enterT = useRef(new Animated.Value(0)).current;
899
939
  const exitT = useRef(new Animated.Value(0)).current;
900
940
  const prev = useRef({ key: transitionKey, node: children, variant });
901
- const [enterVariant, setEnterVariant] = useState(variant);
902
941
  const running = useRef({});
903
942
  const mounted = useRef(false);
904
943
  useEffect(() => {
@@ -907,7 +946,6 @@ var _CardHandoff = ({
907
946
  prev.current = { key: transitionKey, node: children, variant };
908
947
  if (!mounted.current) {
909
948
  mounted.current = true;
910
- setEnterVariant(variant);
911
949
  running.current.enter = startEnter(enterT, variant, reduced, 0);
912
950
  return;
913
951
  }
@@ -915,7 +953,6 @@ var _CardHandoff = ({
915
953
  (_a = running.current.enter) == null ? void 0 : _a.stop();
916
954
  (_b = running.current.exit) == null ? void 0 : _b.stop();
917
955
  setLeaving(last);
918
- setEnterVariant(variant);
919
956
  enterT.setValue(0);
920
957
  exitT.setValue(0);
921
958
  const exitDuration = reduced ? REDUCED_FADE_MS : last.variant === "fade" ? LOADER_HANDOFF_FADE_MS : CARD_EXIT_MS;
@@ -946,7 +983,7 @@ var _CardHandoff = ({
946
983
  );
947
984
  const enterStyle = {
948
985
  opacity: enterT,
949
- transform: transformFor(enterVariant, enterT, reduced, "enter")
986
+ transform: transformFor(variant, enterT, reduced, "enter")
950
987
  };
951
988
  return /* @__PURE__ */ jsxs(Fragment, { children: [
952
989
  leaving ? /* @__PURE__ */ jsx(
@@ -963,7 +1000,7 @@ var _CardHandoff = ({
963
1000
  children: leaving.node
964
1001
  }
965
1002
  ) : null,
966
- /* @__PURE__ */ jsx(Animated.View, { style: [styles11.fill, enterStyle], children })
1003
+ /* @__PURE__ */ jsx(Animated.View, { style: [styles9.fill, enterStyle], children }, variant)
967
1004
  ] });
968
1005
  };
969
1006
  var startEnter = (value, variant, reduced, delay, onDone) => {
@@ -1004,7 +1041,7 @@ var transformFor = (variant, t, reduced, direction) => {
1004
1041
  ];
1005
1042
  };
1006
1043
  var CardHandoff = React14.memo(_CardHandoff);
1007
- var styles11 = StyleSheet.create({
1044
+ var styles9 = StyleSheet.create({
1008
1045
  fill: { flex: 1 }
1009
1046
  });
1010
1047
 
@@ -1134,6 +1171,10 @@ var OnboardingFlow = ({
1134
1171
  onEventRef.current = onEvent;
1135
1172
  const onErrorRef = useRef(onError);
1136
1173
  onErrorRef.current = onError;
1174
+ const onCompleteRef = useRef(onComplete);
1175
+ onCompleteRef.current = onComplete;
1176
+ const validatorsRef = useRef(validators);
1177
+ validatorsRef.current = validators;
1137
1178
  const reportTargetRef = useRef(reportTarget);
1138
1179
  reportTargetRef.current = reportTarget;
1139
1180
  const sessionIdRef = useRef(sessionId);
@@ -1249,8 +1290,8 @@ var OnboardingFlow = ({
1249
1290
  const handleFinish = useCallback(() => {
1250
1291
  if (finished.current) return;
1251
1292
  finished.current = true;
1252
- onComplete({ answers: deriveAnswers(messages), raw: messages });
1253
- }, [messages, onComplete]);
1293
+ onCompleteRef.current({ answers: deriveAnswers(messages), raw: messages });
1294
+ }, [messages]);
1254
1295
  const handleRetry = useCallback(() => {
1255
1296
  attempts.current = 0;
1256
1297
  degradedRef.current = false;
@@ -1278,9 +1319,10 @@ var OnboardingFlow = ({
1278
1319
  }, []);
1279
1320
  const wrappedActions = useCallback(
1280
1321
  (messageId) => {
1322
+ var _a2;
1281
1323
  const base = makeActions(messageId);
1282
1324
  const key = progress.key;
1283
- const validator = key ? validators == null ? void 0 : validators[key] : void 0;
1325
+ const validator = key ? (_a2 = validatorsRef.current) == null ? void 0 : _a2[key] : void 0;
1284
1326
  if (!validator) return base;
1285
1327
  const out = { ...base };
1286
1328
  for (const name of ANSWER_CALLBACKS) {
@@ -1291,12 +1333,12 @@ var OnboardingFlow = ({
1291
1333
  if (typeof value !== "string") return original(...args);
1292
1334
  setValidating(true);
1293
1335
  validator(value).then((res) => {
1294
- var _a2;
1336
+ var _a3;
1295
1337
  if (res.ok) {
1296
1338
  setValidationError(void 0);
1297
1339
  original(...args);
1298
1340
  } else {
1299
- setValidationError((_a2 = res.error) != null ? _a2 : "That value isn't available.");
1341
+ setValidationError((_a3 = res.error) != null ? _a3 : "That value isn't available.");
1300
1342
  }
1301
1343
  }).catch(() => {
1302
1344
  original(...args);
@@ -1305,7 +1347,7 @@ var OnboardingFlow = ({
1305
1347
  }
1306
1348
  return out;
1307
1349
  },
1308
- [makeActions, progress.key, validators]
1350
+ [makeActions, progress.key]
1309
1351
  );
1310
1352
  if (degraded) {
1311
1353
  if (fallbackFlow !== void 0) return /* @__PURE__ */ jsx(Fragment, { children: fallbackFlow });
@@ -1460,7 +1502,7 @@ var _ChipItem = ({ chip, isSelected, multiSelect, onToggle }) => {
1460
1502
  Animated.View,
1461
1503
  {
1462
1504
  style: [
1463
- styles12.chip,
1505
+ styles10.chip,
1464
1506
  {
1465
1507
  borderRadius: t.radius.full,
1466
1508
  paddingHorizontal: t.spacing.md,
@@ -1477,7 +1519,7 @@ var _ChipItem = ({ chip, isSelected, multiSelect, onToggle }) => {
1477
1519
  pointerEvents: "none",
1478
1520
  style: [
1479
1521
  StyleSheet.absoluteFill,
1480
- styles12.fillOverlay,
1522
+ styles10.fillOverlay,
1481
1523
  {
1482
1524
  borderRadius: t.radius.full,
1483
1525
  borderColor: t.colors.primary,
@@ -1491,7 +1533,7 @@ var _ChipItem = ({ chip, isSelected, multiSelect, onToggle }) => {
1491
1533
  Animated.Text,
1492
1534
  {
1493
1535
  style: [
1494
- styles12.check,
1536
+ styles10.check,
1495
1537
  { color: t.colors.onPrimary, opacity: checkT, transform: [{ scale: checkScale }] }
1496
1538
  ],
1497
1539
  children: "\u2713"
@@ -1502,7 +1544,7 @@ var _ChipItem = ({ chip, isSelected, multiSelect, onToggle }) => {
1502
1544
  {
1503
1545
  style: [
1504
1546
  captionStyle(t.fonts),
1505
- styles12.chipLabel,
1547
+ styles10.chipLabel,
1506
1548
  { color: isSelected ? t.colors.onPrimary : t.colors.textMuted }
1507
1549
  ],
1508
1550
  children: chip
@@ -1564,7 +1606,7 @@ var _ChipSelectCard = ({
1564
1606
  disabled: selected.length === 0 || submitted
1565
1607
  }
1566
1608
  ),
1567
- children: /* @__PURE__ */ jsx(View, { style: [styles12.chipsRow, { gap: t.spacing.sm + t.spacing.xs }], children: chips.map((chip) => /* @__PURE__ */ jsx(
1609
+ children: /* @__PURE__ */ jsx(View, { style: [styles10.chipsRow, { gap: t.spacing.sm + t.spacing.xs }], children: chips.map((chip) => /* @__PURE__ */ jsx(
1568
1610
  ChipItem,
1569
1611
  {
1570
1612
  chip,
@@ -1584,7 +1626,7 @@ var ChipSelectCard = {
1584
1626
  propsSchema: schema,
1585
1627
  defaultProps: { multiSelect: true, submitLabel: "Continue" }
1586
1628
  };
1587
- var styles12 = StyleSheet.create({
1629
+ var styles10 = StyleSheet.create({
1588
1630
  chipsRow: { flexDirection: "row", flexWrap: "wrap" },
1589
1631
  chip: { borderWidth: 1.5, flexDirection: "row", alignItems: "center", gap: 6, overflow: "hidden" },
1590
1632
  fillOverlay: { borderWidth: 1.5 },
@@ -1616,6 +1658,7 @@ var _TextInputCard = ({
1616
1658
  lines = 6
1617
1659
  }) => {
1618
1660
  const t = useOnboardingTheme();
1661
+ const styles16 = useMemo(() => makeStyles3(t), [t]);
1619
1662
  const { height: screenHeight } = useWindowDimensions();
1620
1663
  const [value, setValue] = useState("");
1621
1664
  const [submitted, setSubmitted] = useState(false);
@@ -1642,7 +1685,7 @@ var _TextInputCard = ({
1642
1685
  disabled: !value.trim() || locked
1643
1686
  }
1644
1687
  ),
1645
- children: /* @__PURE__ */ jsxs(View, { style: { gap: t.spacing.sm }, children: [
1688
+ children: /* @__PURE__ */ jsxs(View, { style: styles16.row, children: [
1646
1689
  /* @__PURE__ */ jsx(
1647
1690
  TextInput,
1648
1691
  {
@@ -1656,7 +1699,7 @@ var _TextInputCard = ({
1656
1699
  textAlignVertical: multiline ? "top" : "center",
1657
1700
  style: [
1658
1701
  bodyStyle(t.fonts),
1659
- styles13.input,
1702
+ styles16.input,
1660
1703
  {
1661
1704
  color: t.colors.text,
1662
1705
  backgroundColor: t.colors.surface,
@@ -1680,9 +1723,12 @@ var TextInputCard = {
1680
1723
  propsSchema: schema2,
1681
1724
  defaultProps: { submitLabel: "Submit", multiline: true, lines: 6 }
1682
1725
  };
1683
- var styles13 = StyleSheet.create({
1684
- input: { borderWidth: 1 }
1685
- });
1726
+ function makeStyles3(t) {
1727
+ return StyleSheet.create({
1728
+ row: { gap: t.spacing.sm },
1729
+ input: { borderWidth: 1 }
1730
+ });
1731
+ }
1686
1732
  var easeWire5 = Easing.bezier(...WIRE_BEZIER);
1687
1733
  var optionObjectSchema = z.object({
1688
1734
  value: z.string().describe("Option value"),
@@ -1761,7 +1807,7 @@ var _OptionRow = ({ opt, isSelected, multiSelect, onToggle }) => {
1761
1807
  Animated.View,
1762
1808
  {
1763
1809
  style: [
1764
- styles14.option,
1810
+ styles11.option,
1765
1811
  {
1766
1812
  padding: t.spacing.md,
1767
1813
  borderRadius: t.radius.md,
@@ -1778,7 +1824,7 @@ var _OptionRow = ({ opt, isSelected, multiSelect, onToggle }) => {
1778
1824
  pointerEvents: "none",
1779
1825
  style: [
1780
1826
  StyleSheet.absoluteFill,
1781
- styles14.selectedOverlay,
1827
+ styles11.selectedOverlay,
1782
1828
  {
1783
1829
  borderRadius: t.radius.md,
1784
1830
  borderColor: t.colors.primary,
@@ -1792,7 +1838,7 @@ var _OptionRow = ({ opt, isSelected, multiSelect, onToggle }) => {
1792
1838
  View,
1793
1839
  {
1794
1840
  style: [
1795
- styles14.indicator,
1841
+ styles11.indicator,
1796
1842
  { borderRadius: t.radius.full },
1797
1843
  isSelected ? { borderColor: t.colors.primary, backgroundColor: t.colors.primary } : { borderColor: t.colors.borderStrong, backgroundColor: "transparent" }
1798
1844
  ],
@@ -1800,7 +1846,7 @@ var _OptionRow = ({ opt, isSelected, multiSelect, onToggle }) => {
1800
1846
  Animated.Text,
1801
1847
  {
1802
1848
  style: [
1803
- styles14.check,
1849
+ styles11.check,
1804
1850
  { color: t.colors.onPrimary, opacity: checkT, transform: [{ scale: checkScale }] }
1805
1851
  ],
1806
1852
  children: "\u2713"
@@ -1879,7 +1925,7 @@ var SelectionCard = {
1879
1925
  propsSchema: schema3,
1880
1926
  defaultProps: { submitLabel: "Continue", multiSelect: false }
1881
1927
  };
1882
- var styles14 = StyleSheet.create({
1928
+ var styles11 = StyleSheet.create({
1883
1929
  option: { borderWidth: 1.5, flexDirection: "row", alignItems: "center", overflow: "hidden" },
1884
1930
  selectedOverlay: { borderWidth: 1.5 },
1885
1931
  indicator: { width: 24, height: 24, borderWidth: 2, alignItems: "center", justifyContent: "center" },
@@ -1901,17 +1947,22 @@ var STATUS_ICONS = {
1901
1947
  info: "\u2139"
1902
1948
  };
1903
1949
  var _StatusCard = ({ status, title, message, ctaLabel, onContinue, onPress, onSubmit }) => {
1904
- var _a, _b;
1950
+ var _a;
1905
1951
  const t = useOnboardingTheme();
1906
1952
  const reduced = useReducedMotion();
1907
1953
  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;
1954
+ const color = useMemo(() => {
1955
+ switch (status) {
1956
+ case "success":
1957
+ return t.colors.success;
1958
+ case "warning":
1959
+ return t.colors.warning;
1960
+ case "error":
1961
+ return t.colors.error;
1962
+ default:
1963
+ return t.colors.info;
1964
+ }
1965
+ }, [status, t.colors]);
1915
1966
  const popT = useRef(new Animated.Value(reduced ? 1 : 0)).current;
1916
1967
  const copyT = useRef(new Animated.Value(reduced ? 1 : 0)).current;
1917
1968
  useEffect(() => {
@@ -1953,8 +2004,8 @@ var _StatusCard = ({ status, title, message, ctaLabel, onContinue, onPress, onSu
1953
2004
  {
1954
2005
  align: "center",
1955
2006
  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: [
2007
+ children: /* @__PURE__ */ jsxs(View, { style: [styles12.center, { gap: t.spacing.sm }], children: [
2008
+ /* @__PURE__ */ jsxs(View, { style: [styles12.titleRow, { gap: t.spacing.sm }], children: [
1958
2009
  /* @__PURE__ */ jsx(
1959
2010
  Animated.Text,
1960
2011
  {
@@ -1962,7 +2013,7 @@ var _StatusCard = ({ status, title, message, ctaLabel, onContinue, onPress, onSu
1962
2013
  headingStyle(t.fonts),
1963
2014
  { color, opacity: popT, transform: [{ scale: popScale }] }
1964
2015
  ],
1965
- children: (_b = STATUS_ICONS[status]) != null ? _b : "\xB7"
2016
+ children: (_a = STATUS_ICONS[status]) != null ? _a : "\xB7"
1966
2017
  }
1967
2018
  ),
1968
2019
  /* @__PURE__ */ jsx(
@@ -2001,7 +2052,7 @@ var StatusCard = {
2001
2052
  component: React14.memo(_StatusCard),
2002
2053
  propsSchema: schema4
2003
2054
  };
2004
- var styles15 = StyleSheet.create({
2055
+ var styles12 = StyleSheet.create({
2005
2056
  center: { width: "100%", alignItems: "center", justifyContent: "center" },
2006
2057
  titleRow: { flexDirection: "row", alignItems: "center", justifyContent: "center" }
2007
2058
  });
@@ -2026,6 +2077,7 @@ var _NumberStepperCard = ({
2026
2077
  onSubmit
2027
2078
  }) => {
2028
2079
  const t = useOnboardingTheme();
2080
+ const styles16 = useMemo(() => makeStyles4(t), [t]);
2029
2081
  const safeMin = safeNum(min, 1);
2030
2082
  const safeMaxRaw = safeNum(max, 30);
2031
2083
  const safeMax = safeMaxRaw < safeMin ? safeMin : safeMaxRaw;
@@ -2058,7 +2110,7 @@ var _NumberStepperCard = ({
2058
2110
  title: label,
2059
2111
  align: "center",
2060
2112
  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: [
2113
+ children: /* @__PURE__ */ jsxs(View, { style: styles16.wrap, children: [
2062
2114
  /* @__PURE__ */ jsxs(View, { style: [styles16.stepper, { gap: t.spacing.lg }], children: [
2063
2115
  /* @__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
2116
  /* @__PURE__ */ jsxs(View, { style: styles16.valueBox, children: [
@@ -2079,11 +2131,14 @@ var NumberStepperCard = {
2079
2131
  propsSchema: schema5,
2080
2132
  defaultProps: { min: 1, max: 30, defaultValue: 1, step: 1, submitLabel: "Confirm" }
2081
2133
  };
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
- });
2134
+ function makeStyles4(t) {
2135
+ return StyleSheet.create({
2136
+ wrap: { gap: t.spacing.md, alignItems: "center" },
2137
+ stepper: { flexDirection: "row", alignItems: "center", justifyContent: "center" },
2138
+ stepBtn: { width: 44, height: 44, borderWidth: 2, alignItems: "center", justifyContent: "center" },
2139
+ valueBox: { alignItems: "center", minWidth: 80 }
2140
+ });
2141
+ }
2087
2142
  var easeWire7 = Easing.bezier(...WIRE_BEZIER);
2088
2143
  var RISE_PX = 10;
2089
2144
  var schema6 = z.object({
@@ -2145,20 +2200,20 @@ var _WorkItem = ({
2145
2200
  return /* @__PURE__ */ jsxs(
2146
2201
  Animated.View,
2147
2202
  {
2148
- style: [styles17.itemRow, { gap: t.spacing.sm + t.spacing.xs, opacity: rowT, transform: [{ translateY: rise }] }],
2203
+ style: [styles13.itemRow, { gap: t.spacing.sm + t.spacing.xs, opacity: rowT, transform: [{ translateY: rise }] }],
2149
2204
  children: [
2150
2205
  /* @__PURE__ */ jsx(
2151
2206
  View,
2152
2207
  {
2153
2208
  style: [
2154
- styles17.itemBadge,
2209
+ styles13.itemBadge,
2155
2210
  { backgroundColor: t.colors.primarySoft, borderRadius: t.radius.full }
2156
2211
  ],
2157
2212
  children: /* @__PURE__ */ jsx(
2158
2213
  Animated.Text,
2159
2214
  {
2160
2215
  style: [
2161
- styles17.itemCheck,
2216
+ styles13.itemCheck,
2162
2217
  { color: t.colors.primary, opacity: checkT, transform: [{ scale: checkScale }] }
2163
2218
  ],
2164
2219
  children: "\u2713"
@@ -2166,7 +2221,7 @@ var _WorkItem = ({
2166
2221
  )
2167
2222
  }
2168
2223
  ),
2169
- /* @__PURE__ */ jsx(Text, { style: [bodyStyle(t.fonts), styles17.itemText, { color: t.colors.text }], children: text })
2224
+ /* @__PURE__ */ jsx(Text, { style: [bodyStyle(t.fonts), styles13.itemText, { color: t.colors.text }], children: text })
2170
2225
  ]
2171
2226
  }
2172
2227
  );
@@ -2244,18 +2299,18 @@ var _InterstitialCard = ({
2244
2299
  disabled: submitted || !canAdvance
2245
2300
  }
2246
2301
  ),
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(
2302
+ children: /* @__PURE__ */ jsxs(View, { style: [styles13.center, { gap: t.spacing.md }], children: [
2303
+ /* @__PURE__ */ jsx(Animated.View, { style: [styles13.fullWidth, riseStyle(artT)], children: showImage ? /* @__PURE__ */ jsx(
2249
2304
  Image,
2250
2305
  {
2251
2306
  source: { uri: imageUrl },
2252
- style: styles17.image,
2307
+ style: styles13.image,
2253
2308
  resizeMode: "contain",
2254
2309
  onError: () => setImgFailed(true)
2255
2310
  }
2256
- ) : art ? /* @__PURE__ */ jsx(View, { style: styles17.art, children: art }) : null }),
2311
+ ) : art ? /* @__PURE__ */ jsx(View, { style: styles13.art, children: art }) : null }),
2257
2312
  /* @__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
2313
+ (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
2314
  ] })
2260
2315
  }
2261
2316
  );
@@ -2267,7 +2322,7 @@ var InterstitialCard = {
2267
2322
  propsSchema: schema6,
2268
2323
  defaultProps: { ctaLabel: "Continue", illustrationFallback: "momentum" }
2269
2324
  };
2270
- var styles17 = StyleSheet.create({
2325
+ var styles13 = StyleSheet.create({
2271
2326
  center: { width: "100%", alignItems: "center", justifyContent: "center" },
2272
2327
  fullWidth: { width: "100%", alignItems: "center" },
2273
2328
  art: { alignItems: "center", justifyContent: "center" },
@@ -2505,6 +2560,10 @@ var WireOnboarding = ({
2505
2560
  );
2506
2561
  const sessionId = (_a = session == null ? void 0 : session.id) != null ? _a : "";
2507
2562
  const startupUserIdRef = useRef(boundUserId);
2563
+ const metadataKey = config.metadata ? JSON.stringify(config.metadata) : "";
2564
+ const metadataStable = useMemo(() => metadataKey ? JSON.parse(metadataKey) : void 0, [metadataKey]);
2565
+ const componentNamesKey = (components != null ? components : onboardingComponents).map((c) => c.name).join(",");
2566
+ const componentsStable = useMemo(() => components != null ? components : onboardingComponents, [componentNamesKey]);
2508
2567
  const llm = useMemo(() => {
2509
2568
  const baseUrl = config.serverUrl.replace(/\/$/, "");
2510
2569
  return {
@@ -2517,9 +2576,9 @@ var WireOnboarding = ({
2517
2576
  // names of the components this device can actually render — so the backend only
2518
2577
  // emits cards the kit knows how to draw.
2519
2578
  metadata: {
2520
- ...config.metadata,
2579
+ ...metadataStable,
2521
2580
  sessionId,
2522
- supportedComponents: (components != null ? components : onboardingComponents).map((c) => c.name),
2581
+ supportedComponents: componentsStable.map((c) => c.name),
2523
2582
  // Device snapshot + host-injected user context ride the session-start metadata so the
2524
2583
  // backend can segment the funnel. Old servers ignore these unknown keys (backward compat).
2525
2584
  device,
@@ -2532,7 +2591,7 @@ var WireOnboarding = ({
2532
2591
  },
2533
2592
  timeoutMs: 6e4
2534
2593
  };
2535
- }, [config.serverUrl, config.appId, config.apiKey, config.metadata, sessionId, components, device, userContextStable]);
2594
+ }, [config.serverUrl, config.appId, config.apiKey, metadataStable, sessionId, componentsStable, device, userContextStable]);
2536
2595
  const reportTarget = useMemo(
2537
2596
  () => ({ serverUrl: config.serverUrl, apiKey: config.apiKey }),
2538
2597
  [config.serverUrl, config.apiKey]
@@ -2597,7 +2656,7 @@ function DemoOnboarding({
2597
2656
  const [open, setOpen] = useState(false);
2598
2657
  const [runId, setRunId] = useState(0);
2599
2658
  const t = useMemo(() => mergeTheme(theme != null ? theme : {}), [theme]);
2600
- const styles20 = useMemo(() => makeStyles(t), [t]);
2659
+ const styles16 = useMemo(() => makeStyles5(t), [t]);
2601
2660
  const openDemo = useCallback(() => {
2602
2661
  setRunId((n) => n + 1);
2603
2662
  setOpen(true);
@@ -2611,7 +2670,7 @@ function DemoOnboarding({
2611
2670
  [onComplete]
2612
2671
  );
2613
2672
  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 }) }),
2673
+ renderTrigger ? renderTrigger(openDemo) : /* @__PURE__ */ jsx(Pressable, { onPress: openDemo, style: styles16.trigger, accessibilityRole: "button", children: /* @__PURE__ */ jsx(Text, { style: styles16.triggerText, children: label }) }),
2615
2674
  /* @__PURE__ */ jsx(
2616
2675
  Modal,
2617
2676
  {
@@ -2619,7 +2678,7 @@ function DemoOnboarding({
2619
2678
  animationType: "slide",
2620
2679
  presentationStyle: "fullScreen",
2621
2680
  onRequestClose: close,
2622
- children: /* @__PURE__ */ jsx(View, { style: styles20.modal, children: config ? /* @__PURE__ */ jsx(
2681
+ children: /* @__PURE__ */ jsx(View, { style: styles16.modal, children: config ? /* @__PURE__ */ jsx(
2623
2682
  WireOnboarding,
2624
2683
  {
2625
2684
  config,
@@ -2632,16 +2691,16 @@ function DemoOnboarding({
2632
2691
  onError: close
2633
2692
  },
2634
2693
  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" }) })
2694
+ ) : /* @__PURE__ */ jsxs(View, { style: styles16.notice, children: [
2695
+ /* @__PURE__ */ jsx(Text, { style: styles16.noticeTitle, children: "Wire AI not configured" }),
2696
+ /* @__PURE__ */ jsx(Text, { style: styles16.noticeBody, children: "Set the Wire AI key + server URL (e.g. via wireConfigFromEnv) to demo the AI onboarding here." }),
2697
+ /* @__PURE__ */ jsx(Pressable, { onPress: close, style: styles16.trigger, accessibilityRole: "button", children: /* @__PURE__ */ jsx(Text, { style: styles16.triggerText, children: "Close" }) })
2639
2698
  ] }) })
2640
2699
  }
2641
2700
  )
2642
2701
  ] });
2643
2702
  }
2644
- function makeStyles(t) {
2703
+ function makeStyles5(t) {
2645
2704
  return StyleSheet.create({
2646
2705
  trigger: {
2647
2706
  backgroundColor: t.colors.primary,
@@ -2714,13 +2773,13 @@ var _DoneBlock = ({
2714
2773
  message = "Personalizing your experience\u2026"
2715
2774
  }) => {
2716
2775
  const t = useOnboardingTheme();
2717
- return /* @__PURE__ */ jsxs(View, { style: [styles18.center, { gap: t.spacing.sm, padding: t.spacing.lg }], children: [
2776
+ return /* @__PURE__ */ jsxs(View, { style: [styles14.center, { gap: t.spacing.sm, padding: t.spacing.lg }], children: [
2718
2777
  /* @__PURE__ */ jsx(Text, { style: [headingStyle(t.fonts), { color: t.colors.success, textAlign: "center" }], children: title }),
2719
2778
  /* @__PURE__ */ jsx(Text, { style: [bodyStyle(t.fonts), { color: t.colors.textMuted, textAlign: "center" }], children: message })
2720
2779
  ] });
2721
2780
  };
2722
2781
  var DoneBlock = React14.memo(_DoneBlock);
2723
- var styles18 = StyleSheet.create({
2782
+ var styles14 = StyleSheet.create({
2724
2783
  center: { flex: 1, alignItems: "center", justifyContent: "center" }
2725
2784
  });
2726
2785
 
@@ -2799,25 +2858,25 @@ var _CenteredModal = forwardRef(
2799
2858
  /* @__PURE__ */ jsx(
2800
2859
  Animated.View,
2801
2860
  {
2802
- style: [styles19.backdrop, { opacity: backdropOpacity }],
2861
+ style: [styles15.backdrop, { opacity: backdropOpacity }],
2803
2862
  pointerEvents: "none"
2804
2863
  }
2805
2864
  ),
2806
2865
  /* @__PURE__ */ jsx(
2807
2866
  Pressable,
2808
2867
  {
2809
- style: styles19.fill,
2868
+ style: styles15.fill,
2810
2869
  onPress: onBackdrop,
2811
2870
  accessibilityRole: "button",
2812
2871
  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(
2872
+ children: /* @__PURE__ */ jsx(SafeAreaView, { style: styles15.center, pointerEvents: "box-none", children: /* @__PURE__ */ jsx(Pressable, { onPress: () => {
2873
+ }, style: styles15.cardWrap, children: /* @__PURE__ */ jsx(
2815
2874
  Animated.View,
2816
2875
  {
2817
2876
  accessibilityViewIsModal: true,
2818
2877
  accessibilityRole: "alert",
2819
2878
  style: [
2820
- styles19.card,
2879
+ styles15.card,
2821
2880
  {
2822
2881
  backgroundColor: theme.colors.surface,
2823
2882
  borderRadius: theme.radius.lg,
@@ -2839,7 +2898,7 @@ var _CenteredModal = forwardRef(
2839
2898
  );
2840
2899
  _CenteredModal.displayName = "CenteredModal";
2841
2900
  var CenteredModal = React14.memo(_CenteredModal);
2842
- var styles19 = StyleSheet.create({
2901
+ var styles15 = StyleSheet.create({
2843
2902
  fill: { flex: 1 },
2844
2903
  backdrop: { position: "absolute", top: 0, left: 0, right: 0, bottom: 0, backgroundColor: "#000" },
2845
2904
  center: { flex: 1, alignItems: "center", justifyContent: "center", padding: 24 },
@@ -2993,7 +3052,12 @@ var useWireFeatures = (config) => {
2993
3052
  }, [canFetch, stableConfig]);
2994
3053
  return flags;
2995
3054
  };
2996
- var WireFeaturesContext = createContext(null);
3055
+ var CONTEXT_SYMBOL = /* @__PURE__ */ Symbol.for("wireai.features.context");
3056
+ var globalObj = global;
3057
+ if (!globalObj[CONTEXT_SYMBOL]) {
3058
+ globalObj[CONTEXT_SYMBOL] = createContext(null);
3059
+ }
3060
+ var WireFeaturesContext = globalObj[CONTEXT_SYMBOL];
2997
3061
  var WireFeaturesProvider = ({
2998
3062
  config,
2999
3063
  flags,
@@ -3406,6 +3470,8 @@ var createEventQueue = (options) => {
3406
3470
  })();
3407
3471
  const postBatch = async (events) => {
3408
3472
  if (!(target == null ? void 0 : target.serverUrl) || events.length === 0) return false;
3473
+ const controller = typeof AbortController !== "undefined" ? new AbortController() : void 0;
3474
+ const timer = setTimeout(() => controller == null ? void 0 : controller.abort(), 15e3);
3409
3475
  try {
3410
3476
  const url = `${target.serverUrl.replace(/\/$/, "")}/v1/events`;
3411
3477
  const headers = { "Content-Type": "application/json" };
@@ -3413,11 +3479,14 @@ var createEventQueue = (options) => {
3413
3479
  const res = await fetch(url, {
3414
3480
  method: "POST",
3415
3481
  headers,
3416
- body: JSON.stringify({ events })
3482
+ body: JSON.stringify({ events }),
3483
+ signal: controller == null ? void 0 : controller.signal
3417
3484
  });
3418
3485
  return !!(res && res.ok);
3419
3486
  } catch {
3420
3487
  return false;
3488
+ } finally {
3489
+ clearTimeout(timer);
3421
3490
  }
3422
3491
  };
3423
3492
  const clearRetry = () => {