@wireai/activation 0.13.0 → 0.13.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/AGENTS.md +3 -1
  2. package/CHANGELOG.md +116 -3
  3. package/README.md +82 -0
  4. package/dist/analytics/index.d.mts +2 -2
  5. package/dist/analytics/index.d.ts +2 -2
  6. package/dist/analytics/index.js +61 -2
  7. package/dist/analytics/index.js.map +1 -1
  8. package/dist/analytics/index.mjs +61 -2
  9. package/dist/analytics/index.mjs.map +1 -1
  10. package/dist/{currentSession-_GynvhzT.d.mts → currentSession-ClkLjcJ0.d.mts} +298 -13
  11. package/dist/{currentSession-D7zabMXK.d.ts → currentSession-DOVZEWJl.d.ts} +298 -13
  12. package/dist/index.d.mts +195 -4
  13. package/dist/index.d.ts +195 -4
  14. package/dist/index.js +909 -340
  15. package/dist/index.js.map +1 -1
  16. package/dist/index.mjs +693 -145
  17. package/dist/index.mjs.map +1 -1
  18. package/dist/questionnaire/index.js.map +1 -1
  19. package/dist/questionnaire/index.mjs.map +1 -1
  20. package/dist/reviews/index.js +12 -1
  21. package/dist/reviews/index.js.map +1 -1
  22. package/dist/reviews/index.mjs +12 -1
  23. package/dist/reviews/index.mjs.map +1 -1
  24. package/package.json +1 -1
  25. package/src/OnboardingFlow.tsx +131 -0
  26. package/src/WireOnboarding.tsx +63 -2
  27. package/src/analytics/analyticsEvent.ts +16 -1
  28. package/src/analytics/eventQueue.ts +2 -0
  29. package/src/analytics/reportClientEvent.ts +68 -12
  30. package/src/cards/PermissionCard.tsx +438 -0
  31. package/src/cards/index.ts +7 -0
  32. package/src/illustrations/defaultIllustrations.tsx +44 -3
  33. package/src/index.ts +38 -0
  34. package/src/permissions/index.ts +64 -0
  35. package/src/permissions/permissionCopy.ts +87 -0
  36. package/src/permissions/permissionEvents.ts +76 -0
  37. package/src/permissions/permissionMemory.ts +88 -0
  38. package/src/permissions/placement.ts +88 -0
  39. package/src/permissions/types.ts +131 -0
  40. package/src/session/persistedSession.ts +10 -3
  41. package/src/types.ts +66 -4
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { StyleSheet, Easing, Animated, BackHandler, Modal, Pressable, Text, View, TouchableOpacity, KeyboardAvoidingView, ScrollView, useWindowDimensions, TextInput, Image, AccessibilityInfo, Platform, Dimensions, I18nManager, AppState, NativeModules, TurboModuleRegistry } from 'react-native';
2
- import React18, { createContext, forwardRef, useRef, useState, useEffect, useCallback, useImperativeHandle, useMemo, useContext, useSyncExternalStore } from 'react';
2
+ import React19, { createContext, forwardRef, useRef, useState, useEffect, useCallback, useImperativeHandle, useMemo, useContext, useSyncExternalStore } from 'react';
3
3
  import { useWireAIThread, useWireAIAction, ComponentRenderer, WireAIProvider } from 'wireai-rn';
4
4
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
5
5
  import { SafeAreaView } from 'react-native-safe-area-context';
@@ -484,7 +484,7 @@ var useIllustration = (name2) => {
484
484
  if (!name2) return void 0;
485
485
  return registry[name2];
486
486
  };
487
- var Glyph = ({ children }) => {
487
+ var Disc = ({ children }) => {
488
488
  const t = useOnboardingTheme();
489
489
  return /* @__PURE__ */ jsx(
490
490
  View,
@@ -493,11 +493,23 @@ var Glyph = ({ children }) => {
493
493
  styles.disc,
494
494
  { backgroundColor: t.colors.primarySoft, borderRadius: t.radius.full }
495
495
  ],
496
- children: /* @__PURE__ */ jsx(Text, { style: [styles.glyphText, { color: t.colors.primary }], allowFontScaling: false, children })
496
+ children
497
497
  }
498
498
  );
499
499
  };
500
+ var Glyph = ({ children }) => {
501
+ const t = useOnboardingTheme();
502
+ return /* @__PURE__ */ jsx(Disc, { children: /* @__PURE__ */ jsx(Text, { style: [styles.glyphText, { color: t.colors.primary }], allowFontScaling: false, children }) });
503
+ };
500
504
  var MomentumGlyph = () => /* @__PURE__ */ jsx(Glyph, { children: "\u2197" });
505
+ var NotificationsGlyph = () => {
506
+ const t = useOnboardingTheme();
507
+ return /* @__PURE__ */ jsx(Disc, { children: /* @__PURE__ */ jsxs(View, { style: styles.bell, children: [
508
+ /* @__PURE__ */ jsx(View, { style: [styles.bellBody, { backgroundColor: t.colors.primary }] }),
509
+ /* @__PURE__ */ jsx(View, { style: [styles.bellRim, { backgroundColor: t.colors.primary }] }),
510
+ /* @__PURE__ */ jsx(View, { style: [styles.bellClapper, { backgroundColor: t.colors.primary }] })
511
+ ] }) });
512
+ };
501
513
  var BeforeAfterGlyph = () => {
502
514
  const t = useOnboardingTheme();
503
515
  return /* @__PURE__ */ jsxs(View, { style: styles.row, children: [
@@ -524,7 +536,8 @@ var BeforeAfterGlyph = () => {
524
536
  };
525
537
  var defaultIllustrations = {
526
538
  momentum: /* @__PURE__ */ jsx(MomentumGlyph, {}),
527
- "before-after": /* @__PURE__ */ jsx(BeforeAfterGlyph, {})
539
+ "before-after": /* @__PURE__ */ jsx(BeforeAfterGlyph, {}),
540
+ notifications: /* @__PURE__ */ jsx(NotificationsGlyph, {})
528
541
  };
529
542
  var styles = StyleSheet.create({
530
543
  disc: {
@@ -552,7 +565,12 @@ var styles = StyleSheet.create({
552
565
  arrow: {
553
566
  fontSize: 28,
554
567
  fontWeight: "700"
555
- }
568
+ },
569
+ // The bell: a domed body over a wider rim, with the clapper hanging below it.
570
+ bell: { alignItems: "center", justifyContent: "center" },
571
+ bellBody: { width: 34, height: 30, borderTopLeftRadius: 17, borderTopRightRadius: 17 },
572
+ bellRim: { width: 44, height: 5, borderRadius: 3, marginTop: 2 },
573
+ bellClapper: { width: 9, height: 9, borderRadius: 5, marginTop: 3 }
556
574
  });
557
575
  var EMPTY_REGISTRY2 = {};
558
576
  var IconContext = createContext(EMPTY_REGISTRY2);
@@ -802,7 +820,7 @@ var _AnimatedSparkle = ({ size = 30, color, variant = "spin" }) => {
802
820
  }
803
821
  ) });
804
822
  };
805
- var AnimatedSparkle = React18.memo(_AnimatedSparkle);
823
+ var AnimatedSparkle = React19.memo(_AnimatedSparkle);
806
824
  var styles2 = StyleSheet.create({
807
825
  box: { alignItems: "center", justifyContent: "center" },
808
826
  glyph: {
@@ -905,7 +923,7 @@ var _LoadingBlock = ({
905
923
  )
906
924
  ] });
907
925
  };
908
- var LoadingBlock = React18.memo(_LoadingBlock);
926
+ var LoadingBlock = React19.memo(_LoadingBlock);
909
927
  var styles3 = StyleSheet.create({
910
928
  center: { flex: 1, alignItems: "center", justifyContent: "center" },
911
929
  // Absolutely-positioned label column, pinned just under the centered stage: its top
@@ -950,7 +968,7 @@ var _LoadingScreen = ({ title, hint }) => {
950
968
  }
951
969
  );
952
970
  };
953
- var LoadingScreen = React18.memo(_LoadingScreen);
971
+ var LoadingScreen = React19.memo(_LoadingScreen);
954
972
  var styles4 = StyleSheet.create({
955
973
  flex: { flex: 1 }
956
974
  });
@@ -990,7 +1008,7 @@ var _StepProgress = ({ step, complete, approxScreens }) => {
990
1008
  }
991
1009
  ) });
992
1010
  };
993
- var StepProgress = React18.memo(_StepProgress);
1011
+ var StepProgress = React19.memo(_StepProgress);
994
1012
  var styles5 = StyleSheet.create({
995
1013
  track: { height: 6, width: "100%", overflow: "hidden" },
996
1014
  fill: { height: 6 }
@@ -1006,31 +1024,31 @@ var _OnboardingScaffold = ({
1006
1024
  children
1007
1025
  }) => {
1008
1026
  const t = useOnboardingTheme();
1009
- const styles17 = useMemo(() => makeStyles(t), [t]);
1027
+ const styles18 = useMemo(() => makeStyles(t), [t]);
1010
1028
  const hit = { top: 12, bottom: 12, left: 12, right: 12 };
1011
1029
  return /* @__PURE__ */ jsxs(
1012
1030
  SafeAreaView,
1013
1031
  {
1014
1032
  edges: ["top", "bottom"],
1015
- style: [styles17.flex, { backgroundColor: t.colors.background }],
1033
+ style: [styles18.flex, { backgroundColor: t.colors.background }],
1016
1034
  children: [
1017
- /* @__PURE__ */ jsxs(View, { style: [styles17.header, { paddingHorizontal: t.spacing.md, paddingTop: t.spacing.md }], children: [
1018
- /* @__PURE__ */ jsx(View, { style: styles17.progressWrap, children: /* @__PURE__ */ jsx(StepProgress, { step, complete, approxScreens }) }),
1019
- onSkip ? /* @__PURE__ */ jsx(TouchableOpacity, { onPress: onSkip, hitSlop: hit, style: styles17.skip, children: /* @__PURE__ */ jsx(Text, { style: [bodyStyle(t.fonts), { color: t.colors.textMuted }], children: skipLabel }) }) : null
1035
+ /* @__PURE__ */ jsxs(View, { style: [styles18.header, { paddingHorizontal: t.spacing.md, paddingTop: t.spacing.md }], children: [
1036
+ /* @__PURE__ */ jsx(View, { style: styles18.progressWrap, children: /* @__PURE__ */ jsx(StepProgress, { step, complete, approxScreens }) }),
1037
+ onSkip ? /* @__PURE__ */ jsx(TouchableOpacity, { onPress: onSkip, hitSlop: hit, style: styles18.skip, children: /* @__PURE__ */ jsx(Text, { style: [bodyStyle(t.fonts), { color: t.colors.textMuted }], children: skipLabel }) }) : null
1020
1038
  ] }),
1021
1039
  /* @__PURE__ */ jsx(
1022
1040
  View,
1023
1041
  {
1024
- style: [styles17.body, { paddingHorizontal: t.spacing.md, paddingTop: t.spacing.md }],
1042
+ style: [styles18.body, { paddingHorizontal: t.spacing.md, paddingTop: t.spacing.md }],
1025
1043
  children
1026
1044
  }
1027
1045
  ),
1028
- onBack ? /* @__PURE__ */ jsx(View, { style: [styles17.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
1046
+ onBack ? /* @__PURE__ */ jsx(View, { style: [styles18.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
1029
1047
  ]
1030
1048
  }
1031
1049
  );
1032
1050
  };
1033
- var OnboardingScaffold = React18.memo(_OnboardingScaffold);
1051
+ var OnboardingScaffold = React19.memo(_OnboardingScaffold);
1034
1052
  function makeStyles(t) {
1035
1053
  return StyleSheet.create({
1036
1054
  flex: { flex: 1 },
@@ -1131,7 +1149,7 @@ var _Button = ({
1131
1149
  }
1132
1150
  );
1133
1151
  };
1134
- var Button = React18.memo(_Button);
1152
+ var Button = React19.memo(_Button);
1135
1153
  var styles6 = StyleSheet.create({
1136
1154
  base: { alignItems: "center", justifyContent: "center" },
1137
1155
  fill: { width: "100%" }
@@ -1149,7 +1167,7 @@ var _ErrorBlock = ({
1149
1167
  onRetry ? /* @__PURE__ */ jsx(Button, { title: retryLabel, onPress: onRetry, variant: "outline" }) : null
1150
1168
  ] });
1151
1169
  };
1152
- var ErrorBlock = React18.memo(_ErrorBlock);
1170
+ var ErrorBlock = React19.memo(_ErrorBlock);
1153
1171
  var styles7 = StyleSheet.create({
1154
1172
  center: { flex: 1, alignItems: "center", justifyContent: "center" }
1155
1173
  });
@@ -1162,10 +1180,10 @@ var _CardLayout = ({
1162
1180
  titleNode
1163
1181
  }) => {
1164
1182
  const t = useOnboardingTheme();
1165
- const styles17 = useMemo(() => makeStyles2(t), [t]);
1183
+ const styles18 = useMemo(() => makeStyles2(t), [t]);
1166
1184
  const hasHeader = !!title || !!subtitle || !!titleNode;
1167
- return /* @__PURE__ */ jsxs(KeyboardAvoidingView, { style: styles17.fill, behavior: "padding", children: [
1168
- hasHeader ? /* @__PURE__ */ jsxs(View, { style: styles17.header, children: [
1185
+ return /* @__PURE__ */ jsxs(KeyboardAvoidingView, { style: styles18.fill, behavior: "padding", children: [
1186
+ hasHeader ? /* @__PURE__ */ jsxs(View, { style: styles18.header, children: [
1169
1187
  titleNode != null ? titleNode : title ? /* @__PURE__ */ jsx(Text, { style: [labelStyle(t.fonts), { color: t.colors.text }], children: title }) : null,
1170
1188
  subtitle ? /* @__PURE__ */ jsx(
1171
1189
  Text,
@@ -1181,10 +1199,10 @@ var _CardLayout = ({
1181
1199
  /* @__PURE__ */ jsx(
1182
1200
  ScrollView,
1183
1201
  {
1184
- style: styles17.fill,
1202
+ style: styles18.fill,
1185
1203
  contentContainerStyle: [
1186
- styles17.scrollContent,
1187
- align === "center" && styles17.center
1204
+ styles18.scrollContent,
1205
+ align === "center" && styles18.center
1188
1206
  ],
1189
1207
  keyboardShouldPersistTaps: "handled",
1190
1208
  keyboardDismissMode: "on-drag",
@@ -1192,10 +1210,10 @@ var _CardLayout = ({
1192
1210
  children
1193
1211
  }
1194
1212
  ),
1195
- footer ? /* @__PURE__ */ jsx(View, { style: styles17.footer, children: footer }) : null
1213
+ footer ? /* @__PURE__ */ jsx(View, { style: styles18.footer, children: footer }) : null
1196
1214
  ] });
1197
1215
  };
1198
- var CardLayout = React18.memo(_CardLayout);
1216
+ var CardLayout = React19.memo(_CardLayout);
1199
1217
  function makeStyles2(t) {
1200
1218
  return StyleSheet.create({
1201
1219
  fill: { flex: 1 },
@@ -1320,7 +1338,7 @@ var _CompletionView = ({
1320
1338
  }
1321
1339
  );
1322
1340
  };
1323
- var CompletionView = React18.memo(_CompletionView);
1341
+ var CompletionView = React19.memo(_CompletionView);
1324
1342
  var styles8 = StyleSheet.create({
1325
1343
  center: { width: "100%", alignItems: "center", justifyContent: "center" },
1326
1344
  badgeStage: {
@@ -1450,7 +1468,7 @@ var transformFor = (variant, t, reduced, layer, travel) => {
1450
1468
  }
1451
1469
  ];
1452
1470
  };
1453
- var CardHandoff = React18.memo(_CardHandoff);
1471
+ var CardHandoff = React19.memo(_CardHandoff);
1454
1472
  var styles9 = StyleSheet.create({
1455
1473
  fill: { flex: 1 }
1456
1474
  });
@@ -1503,13 +1521,24 @@ var readProgress = (response) => {
1503
1521
 
1504
1522
  // src/analytics/reportClientEvent.ts
1505
1523
  var makeSessionId = () => `wire_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
1524
+ var toWireEvents = (events) => events.map((event) => {
1525
+ if (typeof event.ts !== "number") return event;
1526
+ const { ts, ...rest } = event;
1527
+ if (!Number.isFinite(ts)) return rest;
1528
+ try {
1529
+ return { ...rest, ts: new Date(ts).toISOString() };
1530
+ } catch {
1531
+ return rest;
1532
+ }
1533
+ });
1506
1534
  var buildEventsRequest = (target, events) => {
1507
1535
  if (!(target == null ? void 0 : target.serverUrl) || events.length === 0) return null;
1508
1536
  try {
1509
1537
  const url = `${target.serverUrl.replace(/\/$/, "")}/v1/events`;
1510
1538
  const headers = { "Content-Type": "application/json" };
1511
1539
  if (target.apiKey) headers.Authorization = `Bearer ${target.apiKey}`;
1512
- return { url, init: { method: "POST", headers, body: JSON.stringify({ events }) } };
1540
+ const body = JSON.stringify({ events: toWireEvents(events) });
1541
+ return { url, init: { method: "POST", headers, body } };
1513
1542
  } catch {
1514
1543
  return null;
1515
1544
  }
@@ -1521,7 +1550,13 @@ var readEventsAck = async (res) => {
1521
1550
  const body = await Promise.resolve(json.call(res));
1522
1551
  const skipped = body == null ? void 0 : body.skipped;
1523
1552
  if (typeof skipped !== "number" || !Number.isFinite(skipped)) return void 0;
1524
- const reasons = Array.isArray(body == null ? void 0 : body.errors) ? body.errors.map((e) => e == null ? void 0 : e.reason).filter((r) => typeof r === "string") : [];
1553
+ const reasons = Array.isArray(body == null ? void 0 : body.errors) ? body.errors.map((e) => {
1554
+ const entry = e;
1555
+ const reason = entry == null ? void 0 : entry.reason;
1556
+ if (typeof reason !== "string") return void 0;
1557
+ const field = entry == null ? void 0 : entry.field;
1558
+ return typeof field === "string" && field.length > 0 ? `${reason} (field: ${field})` : reason;
1559
+ }).filter((r) => typeof r === "string") : [];
1525
1560
  return { written: typeof (body == null ? void 0 : body.written) === "number" ? body.written : void 0, skipped, reasons };
1526
1561
  } catch {
1527
1562
  return void 0;
@@ -1591,6 +1626,382 @@ var sendPreview = (target, { sessionId, userMessage }) => {
1591
1626
  } catch {
1592
1627
  }
1593
1628
  };
1629
+
1630
+ // src/permissions/permissionEvents.ts
1631
+ var WIRE_PERMISSION_EVENTS = {
1632
+ /** The priming screen became visible. The denominator for every rate below. */
1633
+ screenShown: "wire_permission_screen_shown",
1634
+ /** The user tapped the primary, so the OS dialog is about to open. The rationale worked. */
1635
+ primerAccepted: "wire_permission_primer_accepted",
1636
+ /** The OS granted it. */
1637
+ granted: "wire_permission_granted",
1638
+ /** The OS refused it (a permanently blocked answer reports here too, with `status: "blocked"`). */
1639
+ denied: "wire_permission_denied",
1640
+ /** The user took the secondary. The one native prompt was NOT spent. */
1641
+ skipped: "wire_permission_skipped",
1642
+ /** A blocked user was redirected to the OS settings page. */
1643
+ settingsOpened: "wire_permission_settings_opened"
1644
+ };
1645
+ var permissionEventName = (stage) => {
1646
+ switch (stage) {
1647
+ case "shown":
1648
+ return WIRE_PERMISSION_EVENTS.screenShown;
1649
+ case "accepted":
1650
+ return WIRE_PERMISSION_EVENTS.primerAccepted;
1651
+ case "granted":
1652
+ return WIRE_PERMISSION_EVENTS.granted;
1653
+ case "denied":
1654
+ return WIRE_PERMISSION_EVENTS.denied;
1655
+ case "skipped":
1656
+ return WIRE_PERMISSION_EVENTS.skipped;
1657
+ case "settings":
1658
+ return WIRE_PERMISSION_EVENTS.settingsOpened;
1659
+ default: {
1660
+ const _exhaustive = stage;
1661
+ return _exhaustive;
1662
+ }
1663
+ }
1664
+ };
1665
+ var permissionEventProps = (permission, status) => status ? { permission, status } : { permission };
1666
+ var normalizePermissionStatus = (value) => value === "granted" || value === "denied" || value === "blocked" ? value : "denied";
1667
+
1668
+ // src/haptics/haptics.ts
1669
+ var play = async (tone) => {
1670
+ try {
1671
+ const mod = await import('expo-haptics');
1672
+ if (!mod) return false;
1673
+ if (tone === "selection") ;
1674
+ if (tone === "light") ;
1675
+ await mod.notificationAsync(mod.NotificationFeedbackType.Success);
1676
+ return true;
1677
+ } catch {
1678
+ return false;
1679
+ }
1680
+ };
1681
+ var playHaptic = (tone) => {
1682
+ void play(tone);
1683
+ };
1684
+
1685
+ // src/utils/warnInDev.ts
1686
+ var warnInDev = (message) => {
1687
+ if (typeof __DEV__ !== "undefined" && __DEV__ && typeof console !== "undefined" && console.warn) {
1688
+ console.warn(message);
1689
+ return true;
1690
+ }
1691
+ return false;
1692
+ };
1693
+ var easeWire4 = Easing.bezier(...WIRE_BEZIER);
1694
+ var RISE_PX = 10;
1695
+ var PERMISSION_CARD_NAME = "PermissionCard";
1696
+ var REQUEST_WATCHDOG_MS = 9e4;
1697
+ var schema = z.object({
1698
+ permission: z.string().describe("Which OS permission this screen primes, e.g. 'notifications'"),
1699
+ title: z.string().describe("Headline for the ask"),
1700
+ message: z.string().describe("Why the app needs it, in the user's terms"),
1701
+ primaryLabel: z.string().describe("Primary button, the only control that can open the OS dialog"),
1702
+ secondaryLabel: z.string().describe("Secondary button, advances without spending the OS prompt"),
1703
+ blockedTitle: z.string().optional().describe("Headline once the permission is permanently refused"),
1704
+ blockedMessage: z.string().optional().describe("Rationale for the settings route"),
1705
+ settingsLabel: z.string().optional().describe("Primary button label on the blocked route"),
1706
+ continueLabel: z.string().optional().describe("Primary button label when there is nothing to ask"),
1707
+ illustration: z.string().optional().describe("Name of an app-provided illustration (defaults to the permission name)")
1708
+ });
1709
+ var _PermissionCard = ({
1710
+ permission,
1711
+ title,
1712
+ message,
1713
+ primaryLabel,
1714
+ secondaryLabel,
1715
+ blockedTitle,
1716
+ blockedMessage,
1717
+ settingsLabel,
1718
+ continueLabel,
1719
+ illustration,
1720
+ request,
1721
+ getStatus,
1722
+ openSettings,
1723
+ onStage,
1724
+ onSettled
1725
+ }) => {
1726
+ const t = useOnboardingTheme();
1727
+ const reduced = useReducedMotion();
1728
+ const art = useIllustration(illustration != null ? illustration : permission);
1729
+ const [status, setStatus] = useState(void 0);
1730
+ const [busy, setBusy] = useState(false);
1731
+ const settledRef = useRef(false);
1732
+ const acceptedRef = useRef(false);
1733
+ const watchdogRef = useRef(null);
1734
+ const clearWatchdog = useCallback(() => {
1735
+ if (watchdogRef.current) {
1736
+ clearTimeout(watchdogRef.current);
1737
+ watchdogRef.current = null;
1738
+ }
1739
+ }, []);
1740
+ useEffect(() => clearWatchdog, [clearWatchdog]);
1741
+ const onStageRef = useRef(onStage);
1742
+ onStageRef.current = onStage;
1743
+ const onSettledRef = useRef(onSettled);
1744
+ onSettledRef.current = onSettled;
1745
+ const getStatusRef = useRef(getStatus);
1746
+ getStatusRef.current = getStatus;
1747
+ const shownRef = useRef(false);
1748
+ useEffect(() => {
1749
+ var _a2;
1750
+ if (shownRef.current) return;
1751
+ shownRef.current = true;
1752
+ (_a2 = onStageRef.current) == null ? void 0 : _a2.call(onStageRef, "shown");
1753
+ }, []);
1754
+ useEffect(() => {
1755
+ const probe = getStatusRef.current;
1756
+ if (!probe) return;
1757
+ let cancelled = false;
1758
+ try {
1759
+ void Promise.resolve(probe()).then((value) => {
1760
+ if (!cancelled) setStatus(normalizePermissionStatus(value));
1761
+ }).catch(() => {
1762
+ });
1763
+ } catch {
1764
+ }
1765
+ return () => {
1766
+ cancelled = true;
1767
+ };
1768
+ }, []);
1769
+ const requestRef = useRef(request);
1770
+ requestRef.current = request;
1771
+ useEffect(() => {
1772
+ if (requestRef.current) return;
1773
+ warnInDev(
1774
+ `[wireai] <WireOnboarding> got a permission screen for "${permission}" with no \`request\` function, so it cannot open the OS dialog and renders as a plain continue. Pass request: () => Promise<'granted' | 'denied' | 'blocked'> (5 lines around expo-notifications, see the README).`
1775
+ );
1776
+ }, [permission]);
1777
+ const settle = useCallback((outcome) => {
1778
+ var _a2;
1779
+ if (settledRef.current) return;
1780
+ settledRef.current = true;
1781
+ clearWatchdog();
1782
+ (_a2 = onSettledRef.current) == null ? void 0 : _a2.call(onSettledRef, outcome);
1783
+ }, [clearWatchdog]);
1784
+ const primaryAction = status === "blocked" ? "settings" : status === "granted" || !request ? "continue" : "ask";
1785
+ const handlePrimary = useCallback(() => {
1786
+ var _a2, _b, _c, _d;
1787
+ if (settledRef.current || busy) return;
1788
+ if (primaryAction === "settings") {
1789
+ (_a2 = onStageRef.current) == null ? void 0 : _a2.call(onStageRef, "settings", "blocked");
1790
+ try {
1791
+ void Promise.resolve(openSettings == null ? void 0 : openSettings()).catch(() => {
1792
+ });
1793
+ } catch {
1794
+ }
1795
+ settle("blocked");
1796
+ return;
1797
+ }
1798
+ if (primaryAction === "continue") {
1799
+ if (status === "granted") {
1800
+ (_b = onStageRef.current) == null ? void 0 : _b.call(onStageRef, "granted", "granted");
1801
+ settle("granted");
1802
+ } else {
1803
+ (_c = onStageRef.current) == null ? void 0 : _c.call(onStageRef, "skipped");
1804
+ settle("skipped");
1805
+ }
1806
+ return;
1807
+ }
1808
+ if (!acceptedRef.current) {
1809
+ acceptedRef.current = true;
1810
+ (_d = onStageRef.current) == null ? void 0 : _d.call(onStageRef, "accepted");
1811
+ }
1812
+ setBusy(true);
1813
+ const finish = (value) => {
1814
+ var _a3, _b2;
1815
+ if (settledRef.current) return;
1816
+ clearWatchdog();
1817
+ const resolved = normalizePermissionStatus(value);
1818
+ setBusy(false);
1819
+ setStatus(resolved);
1820
+ if (resolved === "granted") {
1821
+ playHaptic("success");
1822
+ (_a3 = onStageRef.current) == null ? void 0 : _a3.call(onStageRef, "granted", "granted");
1823
+ } else {
1824
+ (_b2 = onStageRef.current) == null ? void 0 : _b2.call(onStageRef, "denied", resolved);
1825
+ }
1826
+ settle(resolved);
1827
+ };
1828
+ clearWatchdog();
1829
+ watchdogRef.current = setTimeout(() => {
1830
+ watchdogRef.current = null;
1831
+ if (settledRef.current) return;
1832
+ setBusy(false);
1833
+ warnInDev(
1834
+ `[wireai] the \`request\` for the "${permission}" permission screen has not settled after ${Math.round(REQUEST_WATCHDOG_MS / 1e3)}s, so the kit handed the controls back rather than leaving the user on a dead-end screen. It recorded NO outcome, because a pending request is not a denial. Make sure your request resolves to 'granted' | 'denied' | 'blocked' on every branch, including the one where the user dismisses the OS dialog.`
1835
+ );
1836
+ }, REQUEST_WATCHDOG_MS);
1837
+ try {
1838
+ void Promise.resolve(request == null ? void 0 : request()).then(finish, () => finish("denied"));
1839
+ } catch {
1840
+ finish("denied");
1841
+ }
1842
+ }, [busy, primaryAction, status, request, openSettings, settle, clearWatchdog, permission]);
1843
+ const handleSecondary = useCallback(() => {
1844
+ var _a2;
1845
+ if (settledRef.current || busy) return;
1846
+ (_a2 = onStageRef.current) == null ? void 0 : _a2.call(onStageRef, "skipped");
1847
+ settle("skipped");
1848
+ }, [busy, settle]);
1849
+ const popT = useRef(new Animated.Value(reduced ? 1 : 0)).current;
1850
+ const titleT = useRef(new Animated.Value(reduced ? 1 : 0)).current;
1851
+ const bodyT = useRef(new Animated.Value(reduced ? 1 : 0)).current;
1852
+ useEffect(() => {
1853
+ if (reduced) {
1854
+ popT.setValue(1);
1855
+ titleT.setValue(1);
1856
+ bodyT.setValue(1);
1857
+ return;
1858
+ }
1859
+ const rise = (value, delay) => Animated.timing(value, {
1860
+ toValue: 1,
1861
+ duration: scaledMs(INTERSTITIAL_HEAD_MS),
1862
+ delay,
1863
+ easing: easeWire4,
1864
+ useNativeDriver: true
1865
+ });
1866
+ const anims = [
1867
+ Animated.spring(popT, {
1868
+ toValue: 1,
1869
+ friction: STATUS_POP_SPRING.friction,
1870
+ tension: STATUS_POP_SPRING.tension,
1871
+ useNativeDriver: true
1872
+ }),
1873
+ rise(titleT, scaledMs(INTERSTITIAL_HEAD_STAGGER_MS)),
1874
+ rise(bodyT, scaledMs(INTERSTITIAL_HEAD_STAGGER_MS * 2))
1875
+ ];
1876
+ anims.forEach((a) => a.start());
1877
+ return () => anims.forEach((a) => a.stop());
1878
+ }, [reduced, popT, titleT, bodyT]);
1879
+ const popScale = popT.interpolate({
1880
+ inputRange: [0, 1],
1881
+ outputRange: [STATUS_POP_SCALE_FROM, 1]
1882
+ });
1883
+ const riseStyle = (value) => ({
1884
+ opacity: value,
1885
+ transform: [
1886
+ { translateY: value.interpolate({ inputRange: [0, 1], outputRange: [RISE_PX, 0] }) }
1887
+ ]
1888
+ });
1889
+ const blocked = status === "blocked";
1890
+ const shownTitle = blocked ? blockedTitle != null ? blockedTitle : title : title;
1891
+ const shownMessage = blocked ? blockedMessage != null ? blockedMessage : message : message;
1892
+ const primaryTitle = primaryAction === "settings" ? settingsLabel != null ? settingsLabel : primaryLabel : primaryAction === "continue" ? continueLabel != null ? continueLabel : primaryLabel : primaryLabel;
1893
+ const showSecondary = status !== "granted";
1894
+ return /* @__PURE__ */ jsx(
1895
+ CardLayout,
1896
+ {
1897
+ align: "center",
1898
+ footer: /* @__PURE__ */ jsxs(View, { style: [styles10.footer, { gap: t.spacing.sm }], children: [
1899
+ /* @__PURE__ */ jsx(Button, { title: primaryTitle, onPress: handlePrimary, variant: "primary", disabled: busy }),
1900
+ showSecondary ? /* @__PURE__ */ jsx(
1901
+ Button,
1902
+ {
1903
+ title: secondaryLabel,
1904
+ onPress: handleSecondary,
1905
+ variant: "outline",
1906
+ disabled: busy
1907
+ }
1908
+ ) : null
1909
+ ] }),
1910
+ children: /* @__PURE__ */ jsxs(View, { style: [styles10.center, { gap: t.spacing.md }], children: [
1911
+ art ? /* @__PURE__ */ jsx(
1912
+ Animated.View,
1913
+ {
1914
+ style: [styles10.art, { opacity: popT, transform: [{ scale: popScale }] }],
1915
+ children: art
1916
+ }
1917
+ ) : null,
1918
+ /* @__PURE__ */ jsx(Animated.View, { style: riseStyle(titleT), children: /* @__PURE__ */ jsx(Text, { style: [headingStyle(t.fonts), { color: t.colors.text, textAlign: "center" }], children: shownTitle }) }),
1919
+ /* @__PURE__ */ jsx(Animated.View, { style: riseStyle(bodyT), children: /* @__PURE__ */ jsx(Text, { style: [bodyStyle(t.fonts), { color: t.colors.textMuted, textAlign: "center" }], children: shownMessage }) })
1920
+ ] })
1921
+ }
1922
+ );
1923
+ };
1924
+ var PermissionCardView = React19.memo(_PermissionCard);
1925
+ var PermissionCard = {
1926
+ name: PERMISSION_CARD_NAME,
1927
+ description: "A priming screen shown BEFORE an OS permission dialog: it explains why the app wants the permission and asks only on the primary tap. Never first, never last. The user is never blocked by it: the secondary advances the flow with the OS prompt unspent.",
1928
+ component: PermissionCardView,
1929
+ propsSchema: schema
1930
+ };
1931
+ var styles10 = StyleSheet.create({
1932
+ center: { width: "100%", alignItems: "center", justifyContent: "center" },
1933
+ art: { alignItems: "center", justifyContent: "center" },
1934
+ footer: { width: "100%", alignItems: "center" }
1935
+ });
1936
+
1937
+ // src/permissions/placement.ts
1938
+ var DEFAULT_PERMISSION_PLACEMENT = "beforeEnd";
1939
+ var normalizeAfterCard = (value) => Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0;
1940
+ var targetIndex = (placement) => {
1941
+ if (placement === "beforeEnd") return void 0;
1942
+ if (placement === "start") return 0;
1943
+ return normalizeAfterCard(placement.afterCard);
1944
+ };
1945
+ var isPermissionDue = (placement, position) => {
1946
+ if (position.cardIndex < 0) return false;
1947
+ const target = targetIndex(placement);
1948
+ if (target === void 0) return position.isTerminal;
1949
+ return position.cardIndex >= target || position.isTerminal;
1950
+ };
1951
+ var permissionScreenId = (screen, index) => {
1952
+ var _a2;
1953
+ return (_a2 = screen.id) != null ? _a2 : `${screen.permission}:${index}`;
1954
+ };
1955
+ var selectDuePermissionScreen = (screens, settled, position) => {
1956
+ var _a2;
1957
+ if (!screens || screens.length === 0) return void 0;
1958
+ for (let index = 0; index < screens.length; index++) {
1959
+ const screen = screens[index];
1960
+ if (!screen) continue;
1961
+ const id = permissionScreenId(screen, index);
1962
+ if (settled.includes(id)) continue;
1963
+ if (isPermissionDue((_a2 = screen.placement) != null ? _a2 : DEFAULT_PERMISSION_PLACEMENT, position)) {
1964
+ return { screen, id };
1965
+ }
1966
+ }
1967
+ return void 0;
1968
+ };
1969
+
1970
+ // src/permissions/permissionCopy.ts
1971
+ var NOTIFICATIONS_PERMISSION_COPY = {
1972
+ title: "Want a nudge at the right moment?",
1973
+ message: "Turn notifications on and we will remind you when it actually helps. No daily noise, and you can turn them off any time.",
1974
+ primaryLabel: "Enable notifications",
1975
+ secondaryLabel: "Maybe later",
1976
+ blockedTitle: "Notifications are switched off",
1977
+ blockedMessage: "Notifications are turned off for this app, so we cannot ask from here. You can switch them back on in Settings.",
1978
+ settingsLabel: "Open settings",
1979
+ continueLabel: "Continue"
1980
+ };
1981
+ var GENERIC_PERMISSION_COPY = {
1982
+ title: "One quick permission",
1983
+ message: "We need your permission for this part to work. You can change it any time.",
1984
+ primaryLabel: "Allow",
1985
+ secondaryLabel: "Maybe later",
1986
+ blockedTitle: "Permission is switched off",
1987
+ blockedMessage: "This permission is turned off for this app, so we cannot ask from here. You can switch it back on in Settings.",
1988
+ settingsLabel: "Open settings",
1989
+ continueLabel: "Continue"
1990
+ };
1991
+ var DEFAULT_PERMISSION_COPY = {
1992
+ notifications: NOTIFICATIONS_PERMISSION_COPY
1993
+ };
1994
+ var resolvePermissionCopy = (permission, overrides) => {
1995
+ var _a2;
1996
+ const base = (_a2 = DEFAULT_PERMISSION_COPY[permission]) != null ? _a2 : GENERIC_PERMISSION_COPY;
1997
+ if (!overrides) return base;
1998
+ const merged = { ...base };
1999
+ for (const key of Object.keys(overrides)) {
2000
+ const value = overrides[key];
2001
+ if (typeof value === "string" && value.length > 0) merged[key] = value;
2002
+ }
2003
+ return merged;
2004
+ };
1594
2005
  var SKIP_ONE_SENTINEL = "__wireai_skip__";
1595
2006
  var ANSWER_CALLBACKS = ["onSubmit", "onSelect", "onConfirm", "onPress"];
1596
2007
  var DEFAULT_COPY = {
@@ -1619,7 +2030,11 @@ var OnboardingFlow = ({
1619
2030
  reportTarget,
1620
2031
  sessionId,
1621
2032
  resumed = false,
1622
- clientContext
2033
+ clientContext,
2034
+ permissionScreens,
2035
+ settledPermissions,
2036
+ permissionsPending = false,
2037
+ onPermissionSettled
1623
2038
  }) => {
1624
2039
  var _a2, _b, _c, _d, _e, _f;
1625
2040
  const { messages, error, isLoading, sendMessage, reset } = useWireAIThread();
@@ -1650,6 +2065,9 @@ var OnboardingFlow = ({
1650
2065
  const [timedOut, setTimedOut] = useState(false);
1651
2066
  const [validationError, setValidationError] = useState();
1652
2067
  const [validating, setValidating] = useState(false);
2068
+ const [locallySettledPermissions, setLocallySettledPermissions] = useState([]);
2069
+ const onPermissionSettledRef = useRef(onPermissionSettled);
2070
+ onPermissionSettledRef.current = onPermissionSettled;
1653
2071
  const lastCard = useMemo(
1654
2072
  () => [...messages].reverse().find((m) => {
1655
2073
  var _a3;
@@ -1760,6 +2178,36 @@ var OnboardingFlow = ({
1760
2178
  if (isLoading) return;
1761
2179
  sendMessage(SKIP_ONE_SENTINEL);
1762
2180
  }, [isLoading, sendMessage]);
2181
+ const emitPermissionStage = useCallback(
2182
+ (permission, stage, status) => {
2183
+ var _a3, _b2, _c2, _d2;
2184
+ (_a3 = onEventRef.current) == null ? void 0 : _a3.call(onEventRef, { type: "permission", permission, stage, status });
2185
+ reportClientEvent(reportTargetRef.current, {
2186
+ event_type: "app_event",
2187
+ session_id: sessionIdRef.current,
2188
+ question_key: permissionEventName(stage),
2189
+ component: PERMISSION_CARD_NAME,
2190
+ screen_index: lastScreenIndexRef.current >= 0 ? lastScreenIndexRef.current : void 0,
2191
+ meta: JSON.stringify(permissionEventProps(permission, status)),
2192
+ device: (_b2 = clientContextRef.current) == null ? void 0 : _b2.device,
2193
+ user_context: (_c2 = clientContextRef.current) == null ? void 0 : _c2.userContext,
2194
+ user_id: (_d2 = clientContextRef.current) == null ? void 0 : _d2.userId
2195
+ });
2196
+ },
2197
+ []
2198
+ );
2199
+ const settlePermission = useCallback(
2200
+ (id, screen, outcome) => {
2201
+ var _a3, _b2;
2202
+ setLocallySettledPermissions((prev) => prev.includes(id) ? prev : [...prev, id]);
2203
+ (_a3 = onPermissionSettledRef.current) == null ? void 0 : _a3.call(onPermissionSettledRef, id, outcome);
2204
+ try {
2205
+ (_b2 = screen.onResult) == null ? void 0 : _b2.call(screen, screen.permission, outcome);
2206
+ } catch {
2207
+ }
2208
+ },
2209
+ []
2210
+ );
1763
2211
  const previewOnSelect = useCallback((value) => {
1764
2212
  const target = reportTargetRef.current;
1765
2213
  if (!(target == null ? void 0 : target.serverUrl)) return;
@@ -1830,6 +2278,36 @@ var OnboardingFlow = ({
1830
2278
  const step = (_a2 = progress.step) != null ? _a2 : renderedCount;
1831
2279
  const total = (_b = progress.total) != null ? _b : approxScreens;
1832
2280
  const isTerminal = response.action === "render" && response.component === "StatusCard";
2281
+ const duePermission = permissionsPending ? void 0 : selectDuePermissionScreen(
2282
+ permissionScreens,
2283
+ settledPermissions ? [...settledPermissions, ...locallySettledPermissions] : locallySettledPermissions,
2284
+ { cardIndex: renderedCount - 1, isTerminal }
2285
+ );
2286
+ if (duePermission) {
2287
+ const { screen, id } = duePermission;
2288
+ const copy2 = resolvePermissionCopy(screen.permission, screen.copy);
2289
+ return /* @__PURE__ */ jsx(OnboardingScaffold, { step, approxScreens: total, children: /* @__PURE__ */ jsx(CardHandoff, { transitionKey: `permission:${id}`, variant: "spring", children: /* @__PURE__ */ jsx(
2290
+ PermissionCardView,
2291
+ {
2292
+ permission: screen.permission,
2293
+ title: copy2.title,
2294
+ message: copy2.message,
2295
+ primaryLabel: copy2.primaryLabel,
2296
+ secondaryLabel: copy2.secondaryLabel,
2297
+ blockedTitle: copy2.blockedTitle,
2298
+ blockedMessage: copy2.blockedMessage,
2299
+ settingsLabel: copy2.settingsLabel,
2300
+ continueLabel: copy2.continueLabel,
2301
+ illustration: screen.illustration,
2302
+ request: screen.request,
2303
+ getStatus: screen.getStatus,
2304
+ openSettings: screen.openSettings,
2305
+ onStage: (stage, status) => emitPermissionStage(screen.permission, stage, status),
2306
+ onSettled: (outcome) => settlePermission(id, screen, outcome)
2307
+ },
2308
+ id
2309
+ ) }) });
2310
+ }
1833
2311
  if (isTerminal) {
1834
2312
  const props = (_c = response.props) != null ? _c : {};
1835
2313
  return /* @__PURE__ */ jsx(OnboardingScaffold, { step, complete: true, approxScreens: total, children: /* @__PURE__ */ jsx(
@@ -1888,8 +2366,8 @@ var OnboardingFlow = ({
1888
2366
  }
1889
2367
  );
1890
2368
  };
1891
- var easeWire4 = Easing.bezier(...WIRE_BEZIER);
1892
- var schema = z.object({
2369
+ var easeWire5 = Easing.bezier(...WIRE_BEZIER);
2370
+ var schema2 = z.object({
1893
2371
  title: z.string().describe("Question or section header, e.g. 'Which season?'"),
1894
2372
  chips: z.array(z.string()).min(1).describe("List of option labels shown as chips, e.g. ['Spring','Summer','Autumn','Winter']"),
1895
2373
  multiSelect: z.boolean().optional().describe("Allow multiple chips selected at once, default true"),
@@ -1920,7 +2398,7 @@ var _ChipItem = ({ chip, isSelected, multiSelect, onToggle }) => {
1920
2398
  const anim = Animated.timing(fillT, {
1921
2399
  toValue: isSelected ? 1 : 0,
1922
2400
  duration: scaledMs(stateChangeDuration(reduced, CHIP_FILL_MS)),
1923
- easing: easeWire4,
2401
+ easing: easeWire5,
1924
2402
  useNativeDriver: true
1925
2403
  });
1926
2404
  anim.start();
@@ -1960,7 +2438,7 @@ var _ChipItem = ({ chip, isSelected, multiSelect, onToggle }) => {
1960
2438
  Animated.View,
1961
2439
  {
1962
2440
  style: [
1963
- styles10.chip,
2441
+ styles11.chip,
1964
2442
  {
1965
2443
  borderRadius: t.radius.full,
1966
2444
  paddingHorizontal: t.spacing.md,
@@ -1977,7 +2455,7 @@ var _ChipItem = ({ chip, isSelected, multiSelect, onToggle }) => {
1977
2455
  pointerEvents: "none",
1978
2456
  style: [
1979
2457
  StyleSheet.absoluteFill,
1980
- styles10.fillOverlay,
2458
+ styles11.fillOverlay,
1981
2459
  {
1982
2460
  borderRadius: t.radius.full,
1983
2461
  borderColor: t.colors.primary,
@@ -1991,7 +2469,7 @@ var _ChipItem = ({ chip, isSelected, multiSelect, onToggle }) => {
1991
2469
  Animated.Text,
1992
2470
  {
1993
2471
  style: [
1994
- styles10.check,
2472
+ styles11.check,
1995
2473
  { color: t.colors.onPrimary, opacity: checkT, transform: [{ scale: checkScale }] }
1996
2474
  ],
1997
2475
  children: "\u2713"
@@ -2002,7 +2480,7 @@ var _ChipItem = ({ chip, isSelected, multiSelect, onToggle }) => {
2002
2480
  {
2003
2481
  style: [
2004
2482
  captionStyle(t.fonts),
2005
- styles10.chipLabel,
2483
+ styles11.chipLabel,
2006
2484
  { color: isSelected ? t.colors.onPrimary : t.colors.textMuted }
2007
2485
  ],
2008
2486
  children: chip
@@ -2014,7 +2492,7 @@ var _ChipItem = ({ chip, isSelected, multiSelect, onToggle }) => {
2014
2492
  }
2015
2493
  );
2016
2494
  };
2017
- var ChipItem = React18.memo(_ChipItem);
2495
+ var ChipItem = React19.memo(_ChipItem);
2018
2496
  var _ChipSelectCard = ({
2019
2497
  title,
2020
2498
  chips,
@@ -2064,7 +2542,7 @@ var _ChipSelectCard = ({
2064
2542
  disabled: selected.length === 0 || submitted
2065
2543
  }
2066
2544
  ),
2067
- children: /* @__PURE__ */ jsx(View, { style: [styles10.chipsRow, { gap: t.spacing.sm + t.spacing.xs }], children: chips.map((chip) => /* @__PURE__ */ jsx(
2545
+ children: /* @__PURE__ */ jsx(View, { style: [styles11.chipsRow, { gap: t.spacing.sm + t.spacing.xs }], children: chips.map((chip) => /* @__PURE__ */ jsx(
2068
2546
  ChipItem,
2069
2547
  {
2070
2548
  chip,
@@ -2080,18 +2558,18 @@ var _ChipSelectCard = ({
2080
2558
  var ChipSelectCard = {
2081
2559
  name: "ChipSelectCard",
2082
2560
  description: "Use for compact short labels: seasons, activities, travel styles, moods, categories. Ideal for 4\u201312 single-word or two-word options. Use SelectionCard instead when options need descriptions; use CardGridSelectCard for a smaller set (2\u20136) of visual choices that each deserve an icon.",
2083
- component: React18.memo(_ChipSelectCard),
2084
- propsSchema: schema,
2561
+ component: React19.memo(_ChipSelectCard),
2562
+ propsSchema: schema2,
2085
2563
  defaultProps: { multiSelect: true, submitLabel: "Continue" }
2086
2564
  };
2087
- var styles10 = StyleSheet.create({
2565
+ var styles11 = StyleSheet.create({
2088
2566
  chipsRow: { flexDirection: "row", flexWrap: "wrap" },
2089
2567
  chip: { borderWidth: 1.5, flexDirection: "row", alignItems: "center", gap: 6, overflow: "hidden" },
2090
2568
  fillOverlay: { borderWidth: 1.5 },
2091
2569
  chipLabel: { fontWeight: "600" },
2092
2570
  check: { fontSize: 13, fontWeight: "800", lineHeight: 15 }
2093
2571
  });
2094
- var schema2 = z.object({
2572
+ var schema3 = z.object({
2095
2573
  label: z.string().describe("Input field label / the question being asked"),
2096
2574
  hint: z.string().optional().describe(
2097
2575
  "One short line under the question giving context: what the answer is used for or why you ask. Add it whenever the question alone might be unclear."
@@ -2116,7 +2594,7 @@ var _TextInputCard = ({
2116
2594
  lines = 6
2117
2595
  }) => {
2118
2596
  const t = useOnboardingTheme();
2119
- const styles17 = useMemo(() => makeStyles3(t), [t]);
2597
+ const styles18 = useMemo(() => makeStyles3(t), [t]);
2120
2598
  const { height: screenHeight } = useWindowDimensions();
2121
2599
  const [value, setValue] = useState("");
2122
2600
  const [submitted, setSubmitted] = useState(false);
@@ -2143,7 +2621,7 @@ var _TextInputCard = ({
2143
2621
  disabled: !value.trim() || locked
2144
2622
  }
2145
2623
  ),
2146
- children: /* @__PURE__ */ jsxs(View, { style: styles17.row, children: [
2624
+ children: /* @__PURE__ */ jsxs(View, { style: styles18.row, children: [
2147
2625
  /* @__PURE__ */ jsx(
2148
2626
  TextInput,
2149
2627
  {
@@ -2157,7 +2635,7 @@ var _TextInputCard = ({
2157
2635
  textAlignVertical: multiline ? "top" : "center",
2158
2636
  style: [
2159
2637
  bodyStyle(t.fonts),
2160
- styles17.input,
2638
+ styles18.input,
2161
2639
  {
2162
2640
  color: t.colors.text,
2163
2641
  backgroundColor: t.colors.surface,
@@ -2177,8 +2655,8 @@ var _TextInputCard = ({
2177
2655
  var TextInputCard = {
2178
2656
  name: "TextInputCard",
2179
2657
  description: "Use when the user needs to type a free-text answer: an open-ended goal, notes, feedback, a name, a destination, a search query. Multi-line by default (great for open-ended goals); set multiline:false for a short single-line field like a name. Use NumberStepperCard for numeric ranges with known bounds.",
2180
- component: React18.memo(_TextInputCard),
2181
- propsSchema: schema2,
2658
+ component: React19.memo(_TextInputCard),
2659
+ propsSchema: schema3,
2182
2660
  defaultProps: { submitLabel: "Submit", multiline: true, lines: 6 }
2183
2661
  };
2184
2662
  function makeStyles3(t) {
@@ -2333,7 +2811,7 @@ var _WireIcon = ({ name: name2, size = 20, color, requireModule }) => {
2333
2811
  return /* @__PURE__ */ jsx(
2334
2812
  View,
2335
2813
  {
2336
- style: styles11.decorative,
2814
+ style: styles12.decorative,
2337
2815
  accessible: false,
2338
2816
  importantForAccessibility: "no-hide-descendants",
2339
2817
  children: hostIcon
@@ -2347,7 +2825,7 @@ var _WireIcon = ({ name: name2, size = 20, color, requireModule }) => {
2347
2825
  return /* @__PURE__ */ jsx(
2348
2826
  View,
2349
2827
  {
2350
- style: styles11.decorative,
2828
+ style: styles12.decorative,
2351
2829
  accessible: false,
2352
2830
  importantForAccessibility: "no-hide-descendants",
2353
2831
  children: /* @__PURE__ */ jsx(
@@ -2362,9 +2840,9 @@ var _WireIcon = ({ name: name2, size = 20, color, requireModule }) => {
2362
2840
  }
2363
2841
  );
2364
2842
  };
2365
- var WireIcon = React18.memo(_WireIcon);
2843
+ var WireIcon = React19.memo(_WireIcon);
2366
2844
  WireIcon.displayName = "WireIcon";
2367
- var styles11 = StyleSheet.create({
2845
+ var styles12 = StyleSheet.create({
2368
2846
  // No size/margin of its own: the glyph decides its footprint, so an absent icon and a
2369
2847
  // present one differ by exactly the glyph — never by a wrapper.
2370
2848
  decorative: {}
@@ -2378,8 +2856,8 @@ var optionObjectSchema = z.object({
2378
2856
  var coerceOptions = (arr) => Array.isArray(arr) ? arr.map((item) => typeof item === "string" ? { value: item, label: item } : item) : arr;
2379
2857
  var optionsField = (description) => z.preprocess(coerceOptions, z.array(optionObjectSchema).min(1)).describe(description);
2380
2858
  var normalizeOptions = (options) => options.map((opt) => typeof opt === "string" ? { value: opt, label: opt } : opt);
2381
- var easeWire5 = Easing.bezier(...WIRE_BEZIER);
2382
- var schema3 = z.object({
2859
+ var easeWire6 = Easing.bezier(...WIRE_BEZIER);
2860
+ var schema4 = z.object({
2383
2861
  title: z.string().describe("Question or prompt for the user"),
2384
2862
  options: optionsField("List of choices"),
2385
2863
  submitLabel: z.string().optional().describe("Submit button label, default: Continue"),
@@ -2409,7 +2887,7 @@ var _OptionRow = ({ opt, isSelected, multiSelect, onToggle }) => {
2409
2887
  const anim = Animated.timing(selectT, {
2410
2888
  toValue: isSelected ? 1 : 0,
2411
2889
  duration: scaledMs(stateChangeDuration(reduced, SELECT_FILL_MS)),
2412
- easing: easeWire5,
2890
+ easing: easeWire6,
2413
2891
  useNativeDriver: true
2414
2892
  });
2415
2893
  anim.start();
@@ -2449,7 +2927,7 @@ var _OptionRow = ({ opt, isSelected, multiSelect, onToggle }) => {
2449
2927
  Animated.View,
2450
2928
  {
2451
2929
  style: [
2452
- styles12.option,
2930
+ styles13.option,
2453
2931
  {
2454
2932
  padding: t.spacing.md,
2455
2933
  borderRadius: t.radius.md,
@@ -2466,7 +2944,7 @@ var _OptionRow = ({ opt, isSelected, multiSelect, onToggle }) => {
2466
2944
  pointerEvents: "none",
2467
2945
  style: [
2468
2946
  StyleSheet.absoluteFill,
2469
- styles12.selectedOverlay,
2947
+ styles13.selectedOverlay,
2470
2948
  {
2471
2949
  borderRadius: t.radius.md,
2472
2950
  borderColor: t.colors.primary,
@@ -2480,7 +2958,7 @@ var _OptionRow = ({ opt, isSelected, multiSelect, onToggle }) => {
2480
2958
  View,
2481
2959
  {
2482
2960
  style: [
2483
- styles12.indicator,
2961
+ styles13.indicator,
2484
2962
  { borderRadius: t.radius.full },
2485
2963
  isSelected ? { borderColor: t.colors.primary, backgroundColor: t.colors.primary } : { borderColor: t.colors.borderStrong, backgroundColor: "transparent" }
2486
2964
  ],
@@ -2488,7 +2966,7 @@ var _OptionRow = ({ opt, isSelected, multiSelect, onToggle }) => {
2488
2966
  Animated.Text,
2489
2967
  {
2490
2968
  style: [
2491
- styles12.check,
2969
+ styles13.check,
2492
2970
  { color: t.colors.onPrimary, opacity: checkT, transform: [{ scale: checkScale }] }
2493
2971
  ],
2494
2972
  children: "\u2713"
@@ -2504,7 +2982,7 @@ var _OptionRow = ({ opt, isSelected, multiSelect, onToggle }) => {
2504
2982
  }
2505
2983
  );
2506
2984
  };
2507
- var OptionRow = React18.memo(_OptionRow);
2985
+ var OptionRow = React19.memo(_OptionRow);
2508
2986
  var _SelectionCard = ({
2509
2987
  title,
2510
2988
  options,
@@ -2514,7 +2992,7 @@ var _SelectionCard = ({
2514
2992
  onSelectionChange
2515
2993
  }) => {
2516
2994
  const t = useOnboardingTheme();
2517
- const opts = React18.useMemo(() => normalizeOptions(options), [options]);
2995
+ const opts = React19.useMemo(() => normalizeOptions(options), [options]);
2518
2996
  const [selected, setSelected] = useState([]);
2519
2997
  const [submitted, setSubmitted] = useState(false);
2520
2998
  useEffect(() => {
@@ -2565,18 +3043,18 @@ var _SelectionCard = ({
2565
3043
  var SelectionCard = {
2566
3044
  name: "SelectionCard",
2567
3045
  description: "Use for mutually exclusive (radio) or multi-select (checkbox) choices from 2\u20138 options, as a vertical list of rows. Best when options are wordy or need reading. Options may carry an optional `icon` from the vocabulary to make the list scannable. Use ChipSelectCard instead when options are short single words; use CardGridSelectCard when the options are visual and each has an obvious icon (occasions, channels, categories).",
2568
- component: React18.memo(_SelectionCard),
2569
- propsSchema: schema3,
3046
+ component: React19.memo(_SelectionCard),
3047
+ propsSchema: schema4,
2570
3048
  defaultProps: { submitLabel: "Continue", multiSelect: false }
2571
3049
  };
2572
- var styles12 = StyleSheet.create({
3050
+ var styles13 = StyleSheet.create({
2573
3051
  option: { borderWidth: 1.5, flexDirection: "row", alignItems: "center", overflow: "hidden" },
2574
3052
  selectedOverlay: { borderWidth: 1.5 },
2575
3053
  indicator: { width: 24, height: 24, borderWidth: 2, alignItems: "center", justifyContent: "center" },
2576
3054
  check: { fontSize: 13, fontWeight: "800", lineHeight: 15 }
2577
3055
  });
2578
- var easeWire6 = Easing.bezier(...WIRE_BEZIER);
2579
- var schema4 = z.object({
3056
+ var easeWire7 = Easing.bezier(...WIRE_BEZIER);
3057
+ var schema5 = z.object({
2580
3058
  title: z.string().describe("Question or prompt for the user, e.g. 'What brings you here?'"),
2581
3059
  options: optionsField(
2582
3060
  "The choices, 2-6 of them. Give EVERY option an `icon` from the vocabulary \u2014 the icon is the point of this card, and an option without one renders as a bare label next to icon-bearing neighbours. Keep labels short (1-3 words) so they fit a half-width card."
@@ -2608,7 +3086,7 @@ var _OptionCard = ({ opt, isSelected, multiSelect, onToggle }) => {
2608
3086
  const anim = Animated.timing(selectT, {
2609
3087
  toValue: isSelected ? 1 : 0,
2610
3088
  duration: scaledMs(stateChangeDuration(reduced, SELECT_FILL_MS)),
2611
- easing: easeWire6,
3089
+ easing: easeWire7,
2612
3090
  useNativeDriver: true
2613
3091
  });
2614
3092
  anim.start();
@@ -2623,12 +3101,12 @@ var _OptionCard = ({ opt, isSelected, multiSelect, onToggle }) => {
2623
3101
  accessibilityRole: multiSelect ? "checkbox" : "radio",
2624
3102
  accessibilityState: multiSelect ? { checked: isSelected } : { selected: isSelected },
2625
3103
  accessibilityLabel: opt.label,
2626
- style: styles13.cell,
3104
+ style: styles14.cell,
2627
3105
  children: /* @__PURE__ */ jsxs(
2628
3106
  Animated.View,
2629
3107
  {
2630
3108
  style: [
2631
- styles13.card,
3109
+ styles14.card,
2632
3110
  {
2633
3111
  padding: t.spacing.md,
2634
3112
  borderRadius: t.radius.lg,
@@ -2645,7 +3123,7 @@ var _OptionCard = ({ opt, isSelected, multiSelect, onToggle }) => {
2645
3123
  pointerEvents: "none",
2646
3124
  style: [
2647
3125
  StyleSheet.absoluteFill,
2648
- styles13.selectedOverlay,
3126
+ styles14.selectedOverlay,
2649
3127
  {
2650
3128
  borderRadius: t.radius.lg,
2651
3129
  borderColor: t.colors.primary,
@@ -2663,7 +3141,7 @@ var _OptionCard = ({ opt, isSelected, multiSelect, onToggle }) => {
2663
3141
  }
2664
3142
  );
2665
3143
  };
2666
- var OptionCard = React18.memo(_OptionCard);
3144
+ var OptionCard = React19.memo(_OptionCard);
2667
3145
  var _CardGridSelectCard = ({
2668
3146
  title,
2669
3147
  options,
@@ -2673,7 +3151,7 @@ var _CardGridSelectCard = ({
2673
3151
  onSelectionChange
2674
3152
  }) => {
2675
3153
  const t = useOnboardingTheme();
2676
- const opts = React18.useMemo(() => normalizeOptions(options), [options]);
3154
+ const opts = React19.useMemo(() => normalizeOptions(options), [options]);
2677
3155
  const [selected, setSelected] = useState([]);
2678
3156
  const [submitted, setSubmitted] = useState(false);
2679
3157
  useEffect(() => {
@@ -2710,7 +3188,7 @@ var _CardGridSelectCard = ({
2710
3188
  disabled: selected.length === 0 || submitted
2711
3189
  }
2712
3190
  ),
2713
- children: /* @__PURE__ */ jsx(View, { style: [styles13.grid, { gap: t.spacing.sm }], children: opts.map((opt) => /* @__PURE__ */ jsx(
3191
+ children: /* @__PURE__ */ jsx(View, { style: [styles14.grid, { gap: t.spacing.sm }], children: opts.map((opt) => /* @__PURE__ */ jsx(
2714
3192
  OptionCard,
2715
3193
  {
2716
3194
  opt,
@@ -2726,19 +3204,19 @@ var _CardGridSelectCard = ({
2726
3204
  var CardGridSelectCard = {
2727
3205
  name: "CardGridSelectCard",
2728
3206
  description: "Use for VISUAL, icon-led choices shown as a 2-column grid of tappable cards (icon on top, short label beneath). Ideal for 2\u20136 concrete options that each have an obvious icon \u2014 occasions, channels, goals, categories \u2014 e.g. 'What brings you here?' or 'Where did you hear about us?'. Multi-select by default. Every option should carry an `icon`. Use SelectionCard instead when options need descriptions or have no natural icon; use ChipSelectCard for many short word-like tags.",
2729
- component: React18.memo(_CardGridSelectCard),
2730
- propsSchema: schema4,
3207
+ component: React19.memo(_CardGridSelectCard),
3208
+ propsSchema: schema5,
2731
3209
  defaultProps: { submitLabel: "Continue", multiSelect: true }
2732
3210
  };
2733
- var styles13 = StyleSheet.create({
3211
+ var styles14 = StyleSheet.create({
2734
3212
  grid: { flexDirection: "row", flexWrap: "wrap" },
2735
3213
  // Two per row: ~47% basis leaves room for the gap, flexGrow fills the remainder exactly.
2736
3214
  cell: { flexBasis: "47%", flexGrow: 1 },
2737
3215
  card: { borderWidth: 1.5, alignItems: "flex-start", overflow: "hidden" },
2738
3216
  selectedOverlay: { borderWidth: 1.5 }
2739
3217
  });
2740
- var easeWire7 = Easing.bezier(...WIRE_BEZIER);
2741
- var schema5 = z.object({
3218
+ var easeWire8 = Easing.bezier(...WIRE_BEZIER);
3219
+ var schema6 = z.object({
2742
3220
  status: z.enum(["success", "warning", "error", "info"]).describe("Status type"),
2743
3221
  title: z.string().describe("Status title"),
2744
3222
  message: z.string().optional().describe("Supporting message"),
@@ -2788,7 +3266,7 @@ var _StatusCard = ({ status, title, message, ctaLabel, onContinue, onPress, onSu
2788
3266
  toValue: 1,
2789
3267
  duration: scaledMs(STATUS_COPY_MS),
2790
3268
  delay: scaledMs(STATUS_COPY_STAGGER_MS),
2791
- easing: easeWire7,
3269
+ easing: easeWire8,
2792
3270
  useNativeDriver: true
2793
3271
  })
2794
3272
  ];
@@ -2810,8 +3288,8 @@ var _StatusCard = ({ status, title, message, ctaLabel, onContinue, onPress, onSu
2810
3288
  {
2811
3289
  align: "center",
2812
3290
  footer: ctaLabel ? /* @__PURE__ */ jsx(Button, { title: ctaLabel, onPress: handleContinue, variant: "primary", disabled: submitted }) : void 0,
2813
- children: /* @__PURE__ */ jsxs(View, { style: [styles14.center, { gap: t.spacing.sm }], children: [
2814
- /* @__PURE__ */ jsxs(View, { style: [styles14.titleRow, { gap: t.spacing.sm }], children: [
3291
+ children: /* @__PURE__ */ jsxs(View, { style: [styles15.center, { gap: t.spacing.sm }], children: [
3292
+ /* @__PURE__ */ jsxs(View, { style: [styles15.titleRow, { gap: t.spacing.sm }], children: [
2815
3293
  /* @__PURE__ */ jsx(
2816
3294
  Animated.Text,
2817
3295
  {
@@ -2855,14 +3333,14 @@ var _StatusCard = ({ status, title, message, ctaLabel, onContinue, onPress, onSu
2855
3333
  var StatusCard = {
2856
3334
  name: "StatusCard",
2857
3335
  description: "Use ONLY for real outcomes: booking confirmed, data saved, action completed. NEVER for 'you just viewed X' acknowledgments. Always include ctaLabel so the user can continue after seeing the status.",
2858
- component: React18.memo(_StatusCard),
2859
- propsSchema: schema5
3336
+ component: React19.memo(_StatusCard),
3337
+ propsSchema: schema6
2860
3338
  };
2861
- var styles14 = StyleSheet.create({
3339
+ var styles15 = StyleSheet.create({
2862
3340
  center: { width: "100%", alignItems: "center", justifyContent: "center" },
2863
3341
  titleRow: { flexDirection: "row", alignItems: "center", justifyContent: "center" }
2864
3342
  });
2865
- var schema6 = z.object({
3343
+ var schema7 = z.object({
2866
3344
  label: z.string().describe("What is being counted, e.g. 'Number of days' or 'Travellers'"),
2867
3345
  min: z.number().optional().describe("Minimum value, default 1"),
2868
3346
  max: z.number().optional().describe("Maximum value, default 30"),
@@ -2883,7 +3361,7 @@ var _NumberStepperCard = ({
2883
3361
  onSubmit
2884
3362
  }) => {
2885
3363
  const t = useOnboardingTheme();
2886
- const styles17 = useMemo(() => makeStyles4(t), [t]);
3364
+ const styles18 = useMemo(() => makeStyles4(t), [t]);
2887
3365
  const safeMin = safeNum(min, 1);
2888
3366
  const safeMaxRaw = safeNum(max, 30);
2889
3367
  const safeMax = safeMaxRaw < safeMin ? safeMin : safeMaxRaw;
@@ -2907,7 +3385,7 @@ var _NumberStepperCard = ({
2907
3385
  const atMin = value <= safeMin;
2908
3386
  const atMax = value >= safeMax;
2909
3387
  const stepBtnStyle = (disabled) => [
2910
- styles17.stepBtn,
3388
+ styles18.stepBtn,
2911
3389
  { borderRadius: t.radius.full, backgroundColor: t.colors.background, borderColor: disabled ? t.colors.border : t.colors.primary }
2912
3390
  ];
2913
3391
  return /* @__PURE__ */ jsx(
@@ -2916,10 +3394,10 @@ var _NumberStepperCard = ({
2916
3394
  title: label,
2917
3395
  align: "center",
2918
3396
  footer: /* @__PURE__ */ jsx(Button, { title: submitLabel, onPress: handleSubmit, variant: "primary", disabled: submitted }),
2919
- children: /* @__PURE__ */ jsxs(View, { style: styles17.wrap, children: [
2920
- /* @__PURE__ */ jsxs(View, { style: [styles17.stepper, { gap: t.spacing.lg }], children: [
3397
+ children: /* @__PURE__ */ jsxs(View, { style: styles18.wrap, children: [
3398
+ /* @__PURE__ */ jsxs(View, { style: [styles18.stepper, { gap: t.spacing.lg }], children: [
2921
3399
  /* @__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" }) }),
2922
- /* @__PURE__ */ jsxs(View, { style: styles17.valueBox, children: [
3400
+ /* @__PURE__ */ jsxs(View, { style: styles18.valueBox, children: [
2923
3401
  /* @__PURE__ */ jsx(Text, { style: [headingStyle(t.fonts), { color: t.colors.primary }], children: String(value) }),
2924
3402
  unit ? /* @__PURE__ */ jsx(Text, { style: [captionStyle(t.fonts), { color: t.colors.textMuted }], children: unit }) : null
2925
3403
  ] }),
@@ -2933,8 +3411,8 @@ var _NumberStepperCard = ({
2933
3411
  var NumberStepperCard = {
2934
3412
  name: "NumberStepperCard",
2935
3413
  description: "Use for any bounded numeric input: days, people, portions, budget tiers, ratings. Best for ranges 1\u201399. Set unit (e.g. 'days', 'people') for clarity.",
2936
- component: React18.memo(_NumberStepperCard),
2937
- propsSchema: schema6,
3414
+ component: React19.memo(_NumberStepperCard),
3415
+ propsSchema: schema7,
2938
3416
  defaultProps: { min: 1, max: 30, defaultValue: 1, step: 1, submitLabel: "Confirm" }
2939
3417
  };
2940
3418
  function makeStyles4(t) {
@@ -2945,9 +3423,9 @@ function makeStyles4(t) {
2945
3423
  valueBox: { alignItems: "center", minWidth: 80 }
2946
3424
  });
2947
3425
  }
2948
- var easeWire8 = Easing.bezier(...WIRE_BEZIER);
2949
- var RISE_PX = 10;
2950
- var schema7 = z.object({
3426
+ var easeWire9 = Easing.bezier(...WIRE_BEZIER);
3427
+ var RISE_PX2 = 10;
3428
+ var schema8 = z.object({
2951
3429
  title: z.string().describe("Short, warm headline for the value/momentum moment"),
2952
3430
  body: z.string().optional().describe("One or two supporting lines that reflect the user's answers back and build momentum"),
2953
3431
  items: z.array(z.string()).max(4).optional().describe(
@@ -2984,7 +3462,7 @@ var _WorkItem = ({
2984
3462
  toValue: 1,
2985
3463
  duration: scaledMs(INTERSTITIAL_ITEM_MS),
2986
3464
  delay: scaledMs(interstitialItemDelay(index)),
2987
- easing: easeWire8,
3465
+ easing: easeWire9,
2988
3466
  useNativeDriver: true
2989
3467
  }),
2990
3468
  Animated.spring(checkT, {
@@ -2998,7 +3476,7 @@ var _WorkItem = ({
2998
3476
  anims.forEach((a) => a.start());
2999
3477
  return () => anims.forEach((a) => a.stop());
3000
3478
  }, [reduced, index, rowT, checkT]);
3001
- const rise = rowT.interpolate({ inputRange: [0, 1], outputRange: [RISE_PX, 0] });
3479
+ const rise = rowT.interpolate({ inputRange: [0, 1], outputRange: [RISE_PX2, 0] });
3002
3480
  const checkScale = checkT.interpolate({
3003
3481
  inputRange: [0, 1],
3004
3482
  outputRange: [INTERSTITIAL_CHECK_SCALE_FROM, 1]
@@ -3006,20 +3484,20 @@ var _WorkItem = ({
3006
3484
  return /* @__PURE__ */ jsxs(
3007
3485
  Animated.View,
3008
3486
  {
3009
- style: [styles15.itemRow, { gap: t.spacing.sm + t.spacing.xs, opacity: rowT, transform: [{ translateY: rise }] }],
3487
+ style: [styles16.itemRow, { gap: t.spacing.sm + t.spacing.xs, opacity: rowT, transform: [{ translateY: rise }] }],
3010
3488
  children: [
3011
3489
  /* @__PURE__ */ jsx(
3012
3490
  View,
3013
3491
  {
3014
3492
  style: [
3015
- styles15.itemBadge,
3493
+ styles16.itemBadge,
3016
3494
  { backgroundColor: t.colors.primarySoft, borderRadius: t.radius.full }
3017
3495
  ],
3018
3496
  children: /* @__PURE__ */ jsx(
3019
3497
  Animated.Text,
3020
3498
  {
3021
3499
  style: [
3022
- styles15.itemCheck,
3500
+ styles16.itemCheck,
3023
3501
  { color: t.colors.primary, opacity: checkT, transform: [{ scale: checkScale }] }
3024
3502
  ],
3025
3503
  children: "\u2713"
@@ -3027,12 +3505,12 @@ var _WorkItem = ({
3027
3505
  )
3028
3506
  }
3029
3507
  ),
3030
- /* @__PURE__ */ jsx(Text, { style: [bodyStyle(t.fonts), styles15.itemText, { color: t.colors.text }], children: text })
3508
+ /* @__PURE__ */ jsx(Text, { style: [bodyStyle(t.fonts), styles16.itemText, { color: t.colors.text }], children: text })
3031
3509
  ]
3032
3510
  }
3033
3511
  );
3034
3512
  };
3035
- var WorkItem = React18.memo(_WorkItem);
3513
+ var WorkItem = React19.memo(_WorkItem);
3036
3514
  var _InterstitialCard = ({
3037
3515
  title,
3038
3516
  body,
@@ -3069,7 +3547,7 @@ var _InterstitialCard = ({
3069
3547
  toValue: 1,
3070
3548
  duration: scaledMs(INTERSTITIAL_HEAD_MS),
3071
3549
  delay,
3072
- easing: easeWire8,
3550
+ easing: easeWire9,
3073
3551
  useNativeDriver: true
3074
3552
  });
3075
3553
  const anims = [
@@ -3082,7 +3560,7 @@ var _InterstitialCard = ({
3082
3560
  }, [reduced, artT, titleT, bodyT]);
3083
3561
  const riseStyle = (value) => ({
3084
3562
  opacity: value,
3085
- transform: [{ translateY: value.interpolate({ inputRange: [0, 1], outputRange: [RISE_PX, 0] }) }]
3563
+ transform: [{ translateY: value.interpolate({ inputRange: [0, 1], outputRange: [RISE_PX2, 0] }) }]
3086
3564
  });
3087
3565
  const handleContinue = useCallback(() => {
3088
3566
  if (submitted || !canAdvance) return;
@@ -3105,18 +3583,18 @@ var _InterstitialCard = ({
3105
3583
  disabled: submitted || !canAdvance
3106
3584
  }
3107
3585
  ),
3108
- children: /* @__PURE__ */ jsxs(View, { style: [styles15.center, { gap: t.spacing.md }], children: [
3109
- /* @__PURE__ */ jsx(Animated.View, { style: [styles15.fullWidth, riseStyle(artT)], children: showImage ? /* @__PURE__ */ jsx(
3586
+ children: /* @__PURE__ */ jsxs(View, { style: [styles16.center, { gap: t.spacing.md }], children: [
3587
+ /* @__PURE__ */ jsx(Animated.View, { style: [styles16.fullWidth, riseStyle(artT)], children: showImage ? /* @__PURE__ */ jsx(
3110
3588
  Image,
3111
3589
  {
3112
3590
  source: { uri: imageUrl },
3113
- style: styles15.image,
3591
+ style: styles16.image,
3114
3592
  resizeMode: "contain",
3115
3593
  onError: () => setImgFailed(true)
3116
3594
  }
3117
- ) : art ? /* @__PURE__ */ jsx(View, { style: styles15.art, children: art }) : null }),
3595
+ ) : art ? /* @__PURE__ */ jsx(View, { style: styles16.art, children: art }) : null }),
3118
3596
  /* @__PURE__ */ jsx(Animated.View, { style: riseStyle(titleT), children: /* @__PURE__ */ jsx(Text, { style: [headingStyle(t.fonts), { color: t.colors.text, textAlign: "center" }], children: title }) }),
3119
- (items == null ? void 0 : items.length) ? /* @__PURE__ */ jsx(View, { style: [styles15.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
3597
+ (items == null ? void 0 : items.length) ? /* @__PURE__ */ jsx(View, { style: [styles16.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
3120
3598
  ] })
3121
3599
  }
3122
3600
  );
@@ -3124,11 +3602,11 @@ var _InterstitialCard = ({
3124
3602
  var InterstitialCard = {
3125
3603
  name: "InterstitialCard",
3126
3604
  description: "Use ONCE mid-flow (never first, never last) as a momentum/value screen between questions \u2014 an illustration + a warm line that reflects answers back, like Duolingo/Cal-AI. NOT a question; the user just taps Continue. Set `illustration` to an app-registered artwork name. Prefer `items` (2-3 short ticked lines, each reflecting a real answer) when you have the signal. Skip it if you don't have enough signal to make the line specific.",
3127
- component: React18.memo(_InterstitialCard),
3128
- propsSchema: schema7,
3605
+ component: React19.memo(_InterstitialCard),
3606
+ propsSchema: schema8,
3129
3607
  defaultProps: { ctaLabel: "Continue", illustrationFallback: "momentum" }
3130
3608
  };
3131
- var styles15 = StyleSheet.create({
3609
+ var styles16 = StyleSheet.create({
3132
3610
  center: { width: "100%", alignItems: "center", justifyContent: "center" },
3133
3611
  fullWidth: { width: "100%", alignItems: "center" },
3134
3612
  art: { alignItems: "center", justifyContent: "center" },
@@ -3448,15 +3926,6 @@ var resetAutoDeviceKeys = () => {
3448
3926
  (_a2 = registry.pending) == null ? void 0 : _a2.clear();
3449
3927
  };
3450
3928
 
3451
- // src/utils/warnInDev.ts
3452
- var warnInDev = (message) => {
3453
- if (typeof __DEV__ !== "undefined" && __DEV__ && typeof console !== "undefined" && console.warn) {
3454
- console.warn(message);
3455
- return true;
3456
- }
3457
- return false;
3458
- };
3459
-
3460
3929
  // src/analytics/currentSession.ts
3461
3930
  var CURRENT_SESSION_ID_SLOT = /* @__PURE__ */ Symbol.for(
3462
3931
  "@wireai/activation:currentSessionId"
@@ -3661,6 +4130,43 @@ var resolveUserContext = (ctx = {}, opts = {}) => {
3661
4130
  if (Object.keys(bucket).length > 0) result.userContext = bucket;
3662
4131
  return result;
3663
4132
  };
4133
+
4134
+ // src/permissions/permissionMemory.ts
4135
+ var permissionStorageKey = (appId) => `wireai:permissions:${appId}`;
4136
+ var readSettledPermissions = (raw, sessionId) => {
4137
+ if (!raw || !sessionId) return [];
4138
+ try {
4139
+ const parsed = JSON.parse(raw);
4140
+ if (typeof parsed !== "object" || parsed === null) return [];
4141
+ const record = parsed;
4142
+ if (record.sessionId !== sessionId || !Array.isArray(record.ids)) return [];
4143
+ return record.ids.filter((id) => typeof id === "string" && id.length > 0);
4144
+ } catch {
4145
+ return [];
4146
+ }
4147
+ };
4148
+ var loadSettledPermissions = async (storage, key, sessionId) => {
4149
+ try {
4150
+ return readSettledPermissions(await withTimeout(storage.getItem(key), READ_TIMEOUT_MS), sessionId);
4151
+ } catch {
4152
+ return [];
4153
+ }
4154
+ };
4155
+ var saveSettledPermissions = (storage, key, sessionId, ids) => {
4156
+ try {
4157
+ const record = { sessionId, ids: [...ids] };
4158
+ void storage.setItem(key, JSON.stringify(record)).catch(() => {
4159
+ });
4160
+ } catch {
4161
+ }
4162
+ };
4163
+ var clearSettledPermissions = (storage, key) => {
4164
+ try {
4165
+ void storage.removeItem(key).catch(() => {
4166
+ });
4167
+ } catch {
4168
+ }
4169
+ };
3664
4170
  var AUTO_JOIN_HYDRATION_TIMEOUT_MS = 1500;
3665
4171
  var WireOnboarding = ({
3666
4172
  config,
@@ -3685,9 +4191,10 @@ var WireOnboarding = ({
3685
4191
  retainSessionOnComplete,
3686
4192
  userContext,
3687
4193
  userId,
3688
- autoJoinKey = true
4194
+ autoJoinKey = true,
4195
+ permissionScreens
3689
4196
  }) => {
3690
- var _a2, _b, _c, _d, _e;
4197
+ var _a2, _b, _c, _d, _e, _f;
3691
4198
  const boundUserId = useMemo(() => sanitizeUserId(userId), [userId]);
3692
4199
  const device = useMemo(() => {
3693
4200
  const collected = collectDeviceContext();
@@ -3775,6 +4282,7 @@ var WireOnboarding = ({
3775
4282
  () => storage ? null : { id: makeSessionId(), resumed: false }
3776
4283
  );
3777
4284
  const storageKey = persistKey != null ? persistKey : sessionStorageKey(config.appId);
4285
+ const permissionsKey = permissionStorageKey(config.appId);
3778
4286
  useEffect(() => {
3779
4287
  if (!storage || session) return;
3780
4288
  let cancelled = false;
@@ -3787,12 +4295,43 @@ var WireOnboarding = ({
3787
4295
  }, [storage, storageKey, sessionTtlMs, session]);
3788
4296
  const handleComplete = useCallback(
3789
4297
  (result) => {
3790
- if (storage && shouldClearOnComplete(retainSessionOnComplete)) clearPersistedSession(storage, storageKey);
4298
+ if (storage && shouldClearOnComplete(retainSessionOnComplete)) {
4299
+ clearPersistedSession(storage, storageKey);
4300
+ clearSettledPermissions(storage, permissionsKey);
4301
+ }
3791
4302
  onComplete(result);
3792
4303
  },
3793
- [storage, storageKey, retainSessionOnComplete, onComplete]
4304
+ [storage, storageKey, permissionsKey, retainSessionOnComplete, onComplete]
3794
4305
  );
3795
4306
  const sessionId = (_c = session == null ? void 0 : session.id) != null ? _c : "";
4307
+ const wantsPermissionMemory = Boolean(storage) && ((_d = permissionScreens == null ? void 0 : permissionScreens.length) != null ? _d : 0) > 0;
4308
+ const [settledPermissions, setSettledPermissions] = useState(void 0);
4309
+ useEffect(() => {
4310
+ if (!wantsPermissionMemory || !storage || !sessionId || settledPermissions !== void 0) return;
4311
+ let cancelled = false;
4312
+ void loadSettledPermissions(storage, permissionsKey, sessionId).then((ids) => {
4313
+ if (!cancelled) setSettledPermissions(ids);
4314
+ });
4315
+ return () => {
4316
+ cancelled = true;
4317
+ };
4318
+ }, [wantsPermissionMemory, storage, permissionsKey, sessionId, settledPermissions]);
4319
+ const settledPermissionsRef = useRef(void 0);
4320
+ settledPermissionsRef.current = settledPermissions;
4321
+ const handlePermissionSettled = useCallback(
4322
+ (id) => {
4323
+ var _a3;
4324
+ const current = (_a3 = settledPermissionsRef.current) != null ? _a3 : [];
4325
+ if (current.includes(id)) return;
4326
+ const next = [...current, id];
4327
+ settledPermissionsRef.current = next;
4328
+ setSettledPermissions(next);
4329
+ if (storage && wantsPermissionMemory && sessionId) {
4330
+ saveSettledPermissions(storage, permissionsKey, sessionId, next);
4331
+ }
4332
+ },
4333
+ [storage, wantsPermissionMemory, permissionsKey, sessionId]
4334
+ );
3796
4335
  const startupUserIdRef = useRef(boundUserId);
3797
4336
  const metadataKey = config.metadata ? JSON.stringify(config.metadata) : "";
3798
4337
  const metadataStable = useMemo(() => metadataKey ? JSON.parse(metadataKey) : void 0, [metadataKey]);
@@ -3853,8 +4392,8 @@ var WireOnboarding = ({
3853
4392
  return /* @__PURE__ */ jsx(OnboardingThemeProvider, { theme, children: /* @__PURE__ */ jsx(
3854
4393
  LoadingScreen,
3855
4394
  {
3856
- title: (_d = copy == null ? void 0 : copy.restoringTitle) != null ? _d : DEFAULT_COPY.restoringTitle,
3857
- hint: (_e = copy == null ? void 0 : copy.restoringHint) != null ? _e : DEFAULT_COPY.restoringHint
4395
+ title: (_e = copy == null ? void 0 : copy.restoringTitle) != null ? _e : DEFAULT_COPY.restoringTitle,
4396
+ hint: (_f = copy == null ? void 0 : copy.restoringHint) != null ? _f : DEFAULT_COPY.restoringHint
3858
4397
  }
3859
4398
  ) });
3860
4399
  }
@@ -3875,7 +4414,11 @@ var WireOnboarding = ({
3875
4414
  reportTarget,
3876
4415
  sessionId,
3877
4416
  resumed: session.resumed,
3878
- clientContext
4417
+ clientContext,
4418
+ permissionScreens,
4419
+ settledPermissions,
4420
+ permissionsPending: wantsPermissionMemory && settledPermissions === void 0,
4421
+ onPermissionSettled: handlePermissionSettled
3879
4422
  }
3880
4423
  ) }) }) }) });
3881
4424
  };
@@ -3892,7 +4435,7 @@ function DemoOnboarding({
3892
4435
  const [open, setOpen] = useState(false);
3893
4436
  const [runId, setRunId] = useState(0);
3894
4437
  const t = useMemo(() => mergeTheme(theme != null ? theme : {}), [theme]);
3895
- const styles17 = useMemo(() => makeStyles5(t), [t]);
4438
+ const styles18 = useMemo(() => makeStyles5(t), [t]);
3896
4439
  const openDemo = useCallback(() => {
3897
4440
  setRunId((n) => n + 1);
3898
4441
  setOpen(true);
@@ -3906,7 +4449,7 @@ function DemoOnboarding({
3906
4449
  [onComplete]
3907
4450
  );
3908
4451
  return /* @__PURE__ */ jsxs(Fragment, { children: [
3909
- renderTrigger ? renderTrigger(openDemo) : /* @__PURE__ */ jsx(Pressable, { onPress: openDemo, style: styles17.trigger, accessibilityRole: "button", children: /* @__PURE__ */ jsx(Text, { style: styles17.triggerText, children: label }) }),
4452
+ renderTrigger ? renderTrigger(openDemo) : /* @__PURE__ */ jsx(Pressable, { onPress: openDemo, style: styles18.trigger, accessibilityRole: "button", children: /* @__PURE__ */ jsx(Text, { style: styles18.triggerText, children: label }) }),
3910
4453
  /* @__PURE__ */ jsx(
3911
4454
  Modal,
3912
4455
  {
@@ -3914,7 +4457,7 @@ function DemoOnboarding({
3914
4457
  animationType: "slide",
3915
4458
  presentationStyle: "fullScreen",
3916
4459
  onRequestClose: close,
3917
- children: /* @__PURE__ */ jsx(View, { style: styles17.modal, children: config ? /* @__PURE__ */ jsx(
4460
+ children: /* @__PURE__ */ jsx(View, { style: styles18.modal, children: config ? /* @__PURE__ */ jsx(
3918
4461
  WireOnboarding,
3919
4462
  {
3920
4463
  config,
@@ -3927,10 +4470,10 @@ function DemoOnboarding({
3927
4470
  onError: close
3928
4471
  },
3929
4472
  runId
3930
- ) : /* @__PURE__ */ jsxs(View, { style: styles17.notice, children: [
3931
- /* @__PURE__ */ jsx(Text, { style: styles17.noticeTitle, children: "Wire AI not configured" }),
3932
- /* @__PURE__ */ jsx(Text, { style: styles17.noticeBody, children: "Set the Wire AI key + server URL (e.g. via wireConfigFromEnv) to demo the AI onboarding here." }),
3933
- /* @__PURE__ */ jsx(Pressable, { onPress: close, style: styles17.trigger, accessibilityRole: "button", children: /* @__PURE__ */ jsx(Text, { style: styles17.triggerText, children: "Close" }) })
4473
+ ) : /* @__PURE__ */ jsxs(View, { style: styles18.notice, children: [
4474
+ /* @__PURE__ */ jsx(Text, { style: styles18.noticeTitle, children: "Wire AI not configured" }),
4475
+ /* @__PURE__ */ jsx(Text, { style: styles18.noticeBody, children: "Set the Wire AI key + server URL (e.g. via wireConfigFromEnv) to demo the AI onboarding here." }),
4476
+ /* @__PURE__ */ jsx(Pressable, { onPress: close, style: styles18.trigger, accessibilityRole: "button", children: /* @__PURE__ */ jsx(Text, { style: styles18.triggerText, children: "Close" }) })
3934
4477
  ] }) })
3935
4478
  }
3936
4479
  )
@@ -4080,25 +4623,25 @@ var _CenteredModal = forwardRef(
4080
4623
  /* @__PURE__ */ jsx(
4081
4624
  Animated.View,
4082
4625
  {
4083
- style: [styles16.backdrop, { opacity: backdropOpacity }],
4626
+ style: [styles17.backdrop, { opacity: backdropOpacity }],
4084
4627
  pointerEvents: "none"
4085
4628
  }
4086
4629
  ),
4087
4630
  /* @__PURE__ */ jsx(
4088
4631
  Pressable,
4089
4632
  {
4090
- style: styles16.fill,
4633
+ style: styles17.fill,
4091
4634
  onPress: onBackdrop,
4092
4635
  accessibilityRole: "button",
4093
4636
  accessibilityLabel: "Dismiss",
4094
- children: /* @__PURE__ */ jsx(SafeAreaView, { style: styles16.center, pointerEvents: "box-none", children: /* @__PURE__ */ jsx(Pressable, { onPress: () => {
4095
- }, style: styles16.cardWrap, children: /* @__PURE__ */ jsx(
4637
+ children: /* @__PURE__ */ jsx(SafeAreaView, { style: styles17.center, pointerEvents: "box-none", children: /* @__PURE__ */ jsx(Pressable, { onPress: () => {
4638
+ }, style: styles17.cardWrap, children: /* @__PURE__ */ jsx(
4096
4639
  Animated.View,
4097
4640
  {
4098
4641
  accessibilityViewIsModal: true,
4099
4642
  accessibilityRole: "alert",
4100
4643
  style: [
4101
- styles16.card,
4644
+ styles17.card,
4102
4645
  {
4103
4646
  backgroundColor: theme.colors.surface,
4104
4647
  borderRadius: theme.radius.lg,
@@ -4119,8 +4662,8 @@ var _CenteredModal = forwardRef(
4119
4662
  }
4120
4663
  );
4121
4664
  _CenteredModal.displayName = "CenteredModal";
4122
- var CenteredModal = React18.memo(_CenteredModal);
4123
- var styles16 = StyleSheet.create({
4665
+ var CenteredModal = React19.memo(_CenteredModal);
4666
+ var styles17 = StyleSheet.create({
4124
4667
  fill: { flex: 1 },
4125
4668
  backdrop: { position: "absolute", top: 0, left: 0, right: 0, bottom: 0, backgroundColor: "#000" },
4126
4669
  center: { flex: 1, alignItems: "center", justifyContent: "center", padding: 24 },
@@ -4371,6 +4914,11 @@ var toAnalyticsEvent = (event) => {
4371
4914
  };
4372
4915
  case "fallback":
4373
4916
  return { name: WIRE_ONBOARDING_EVENTS.fallback, params: { reason: event.reason } };
4917
+ case "permission":
4918
+ return {
4919
+ name: permissionEventName(event.stage),
4920
+ params: permissionEventProps(event.permission, event.status)
4921
+ };
4374
4922
  default: {
4375
4923
  const _exhaustive = event;
4376
4924
  return _exhaustive;
@@ -5198,6 +5746,6 @@ var useLifecycleEvents = (config, options = {}) => {
5198
5746
  }, []);
5199
5747
  };
5200
5748
 
5201
- export { AUTO_DEVICE_ID_PREFIX, AnimatedSparkle, BACKGROUND_SESSION_MS, CardGridSelectCard, CardHandoff, CenteredModal, ChipSelectCard, CompletionView, DEFAULT_FEATURES_TTL_MS, DEFAULT_SESSION_TTL_MS, DemoOnboarding, EXTRA_KEY_PREFIX, ErrorBlock, FIRST_OPEN_EVENT, IconRegistryProvider, IllustrationProvider, InterstitialCard, LoadingBlock, LoadingScreen, NumberStepperCard, Button as OnboardingButton, OnboardingFlow, OnboardingScaffold, OnboardingThemeProvider, PLAN_TIER_CONTEXT_KEY, SESSION_STARTED_EVENT, SelectionCard, StatusCard, StepProgress, TextInputCard, USER_ID_MAX_LENGTH, WIRE_ENV_VARS, WIRE_ICON_GLYPHS, WIRE_ICON_NAMES, WIRE_ONBOARDING_EVENTS, WIRE_PURCHASE_EVENTS, WireFeaturesProvider, WireIcon, WireOnboarding, activationJoinContext, activeEntitlement, analyticsUserIdStorageKey, attributionMetadata, bumpActivationRevalidation, clearPersistedSession, clearPiiFromContext, clearUserContext, collectDeviceContext, createRevenueCatBridge, createWireActivation, defaultIllustrations, defaultOnboardingTheme, defaultWireFeatures, deriveAnswers, describeEntitlement, describeFailure, describePackage, detectAppVersion, detectNativeModel, deviceIdStorageKey, ensureCurrentSessionId, featuresCacheKey, featuresEqual, fetchWireFeatures, firstOpenStorageKey, getActivationRevalidationVersion, getCurrentSessionId, hashEmailFnv1a, hostIdentity, hydrateAutoDeviceKey, hydrateDeviceIdentity, identifyOnboarding, isFeaturesFresh, isOnboardingEnabled, isUserCancelled, isWireScalar, loadPersistedSession, looksLikeEmail, lookupIconGlyph, makeSessionId, mergeTheme, mintDeviceId, motionSpec_exports as motionSpec, namespaceExtra, onboardingComponents, parseWireFeatures, peekPersistedSession, readCachedFeatures, readProgress, reportClientEvent, reportClientEventAwait, reportClientEvents, reportClientEventsAwait, reportFirstOpen, reportSessionStart, resetActivationRevalidation, resetAutoDeviceKeys, resetCurrentSessionId, resetFirstOpenLatch, resetIdentityProvenance, resetSessionStartGuard, resolveAutoDeviceKey, resolveIdentity, resolvePlanTier, resolveUserContext, sanitizeUserId, savePersistedSession, sessionStorageKey, setCurrentSessionId, subscribeActivationRevalidation, themeFromBrand, toAnalyticsEvent, useActivationRevalidation, useHostIcon, useIllustration, useLifecycleEvents, useOnboardingTheme, useReducedMotion, useResolvedFeatures, useSessionStart, useWireActivation, useWireFeatures, useWireFeaturesContext, wireConfigFromEnv, wireLifecycleEvents, writeCachedFeatures };
5749
+ export { AUTO_DEVICE_ID_PREFIX, AnimatedSparkle, BACKGROUND_SESSION_MS, CardGridSelectCard, CardHandoff, CenteredModal, ChipSelectCard, CompletionView, DEFAULT_FEATURES_TTL_MS, DEFAULT_PERMISSION_COPY, DEFAULT_PERMISSION_PLACEMENT, DEFAULT_SESSION_TTL_MS, DemoOnboarding, EXTRA_KEY_PREFIX, ErrorBlock, FIRST_OPEN_EVENT, GENERIC_PERMISSION_COPY, IconRegistryProvider, IllustrationProvider, InterstitialCard, LoadingBlock, LoadingScreen, NOTIFICATIONS_PERMISSION_COPY, NumberStepperCard, Button as OnboardingButton, OnboardingFlow, OnboardingScaffold, OnboardingThemeProvider, PERMISSION_CARD_NAME, PLAN_TIER_CONTEXT_KEY, PermissionCard, PermissionCardView, SESSION_STARTED_EVENT, SelectionCard, StatusCard, StepProgress, TextInputCard, USER_ID_MAX_LENGTH, WIRE_ENV_VARS, WIRE_ICON_GLYPHS, WIRE_ICON_NAMES, WIRE_ONBOARDING_EVENTS, WIRE_PERMISSION_EVENTS, WIRE_PURCHASE_EVENTS, WireFeaturesProvider, WireIcon, WireOnboarding, activationJoinContext, activeEntitlement, analyticsUserIdStorageKey, attributionMetadata, bumpActivationRevalidation, clearPersistedSession, clearPiiFromContext, clearSettledPermissions, clearUserContext, collectDeviceContext, createRevenueCatBridge, createWireActivation, defaultIllustrations, defaultOnboardingTheme, defaultWireFeatures, deriveAnswers, describeEntitlement, describeFailure, describePackage, detectAppVersion, detectNativeModel, deviceIdStorageKey, ensureCurrentSessionId, featuresCacheKey, featuresEqual, fetchWireFeatures, firstOpenStorageKey, getActivationRevalidationVersion, getCurrentSessionId, hashEmailFnv1a, hostIdentity, hydrateAutoDeviceKey, hydrateDeviceIdentity, identifyOnboarding, isFeaturesFresh, isOnboardingEnabled, isPermissionDue, isUserCancelled, isWireScalar, loadPersistedSession, loadSettledPermissions, looksLikeEmail, lookupIconGlyph, makeSessionId, mergeTheme, mintDeviceId, motionSpec_exports as motionSpec, namespaceExtra, normalizeAfterCard, normalizePermissionStatus, onboardingComponents, parseWireFeatures, peekPersistedSession, permissionEventName, permissionEventProps, permissionScreenId, permissionStorageKey, readCachedFeatures, readProgress, readSettledPermissions, reportClientEvent, reportClientEventAwait, reportClientEvents, reportClientEventsAwait, reportFirstOpen, reportSessionStart, resetActivationRevalidation, resetAutoDeviceKeys, resetCurrentSessionId, resetFirstOpenLatch, resetIdentityProvenance, resetSessionStartGuard, resolveAutoDeviceKey, resolveIdentity, resolvePermissionCopy, resolvePlanTier, resolveUserContext, sanitizeUserId, savePersistedSession, saveSettledPermissions, selectDuePermissionScreen, sessionStorageKey, setCurrentSessionId, subscribeActivationRevalidation, themeFromBrand, toAnalyticsEvent, useActivationRevalidation, useHostIcon, useIllustration, useLifecycleEvents, useOnboardingTheme, useReducedMotion, useResolvedFeatures, useSessionStart, useWireActivation, useWireFeatures, useWireFeaturesContext, wireConfigFromEnv, wireLifecycleEvents, writeCachedFeatures };
5202
5750
  //# sourceMappingURL=index.mjs.map
5203
5751
  //# sourceMappingURL=index.mjs.map