@wireai/activation 0.13.0 → 0.13.3

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 (82) hide show
  1. package/AGENTS.md +7 -4
  2. package/CHANGELOG.md +341 -4
  3. package/README.md +98 -2
  4. package/dist/analytics/index.d.mts +4 -4
  5. package/dist/analytics/index.d.ts +4 -4
  6. package/dist/analytics/index.js +164 -471
  7. package/dist/analytics/index.js.map +1 -1
  8. package/dist/analytics/index.mjs +162 -469
  9. package/dist/analytics/index.mjs.map +1 -1
  10. package/dist/coachmarks/index.d.mts +14 -1
  11. package/dist/coachmarks/index.d.ts +14 -1
  12. package/dist/coachmarks/index.js +58 -14
  13. package/dist/coachmarks/index.js.map +1 -1
  14. package/dist/coachmarks/index.mjs +58 -14
  15. package/dist/coachmarks/index.mjs.map +1 -1
  16. package/dist/{currentSession-_GynvhzT.d.mts → currentSession-BoWtr3Jp.d.mts} +299 -14
  17. package/dist/{currentSession-D7zabMXK.d.ts → currentSession-CCOMlaQ2.d.ts} +299 -14
  18. package/dist/{decision-Bl_M2y3r.d.mts → decision-Cjw0jbEj.d.mts} +1 -1
  19. package/dist/{decision-Cau5KmP6.d.ts → decision-GfpXAsk_.d.ts} +1 -1
  20. package/dist/index.d.mts +218 -4
  21. package/dist/index.d.ts +218 -4
  22. package/dist/index.js +1104 -888
  23. package/dist/index.js.map +1 -1
  24. package/dist/index.mjs +887 -692
  25. package/dist/index.mjs.map +1 -1
  26. package/dist/questionnaire/index.d.mts +42 -10
  27. package/dist/questionnaire/index.d.ts +42 -10
  28. package/dist/questionnaire/index.js +45 -13
  29. package/dist/questionnaire/index.js.map +1 -1
  30. package/dist/questionnaire/index.mjs +45 -13
  31. package/dist/questionnaire/index.mjs.map +1 -1
  32. package/dist/reviews/index.d.mts +4 -4
  33. package/dist/reviews/index.d.ts +4 -4
  34. package/dist/reviews/index.js +39 -8
  35. package/dist/reviews/index.js.map +1 -1
  36. package/dist/reviews/index.mjs +39 -8
  37. package/dist/reviews/index.mjs.map +1 -1
  38. package/dist/showcase/index.js +7 -2
  39. package/dist/showcase/index.js.map +1 -1
  40. package/dist/showcase/index.mjs +7 -2
  41. package/dist/showcase/index.mjs.map +1 -1
  42. package/dist/{transport-DsRe4epC.d.ts → transport-CefdERPs.d.mts} +44 -5
  43. package/dist/{transport-CF_eHwzC.d.mts → transport-DFuPyCoJ.d.ts} +44 -5
  44. package/dist/{types-Buj9Lw9t.d.ts → types-UVYt9BJe.d.ts} +41 -1
  45. package/dist/{types-CNUqMK0D.d.mts → types-l2tfg23P.d.mts} +41 -1
  46. package/package.json +4 -1
  47. package/src/OnboardingFlow.tsx +175 -15
  48. package/src/WireOnboarding.tsx +70 -8
  49. package/src/activation/wireActivation.ts +3 -3
  50. package/src/analytics/analyticsEvent.ts +16 -1
  51. package/src/analytics/analyticsFacade.ts +2 -2
  52. package/src/analytics/eventQueue.ts +85 -20
  53. package/src/analytics/reportClientEvent.ts +68 -12
  54. package/src/cards/CardGridSelectCard.tsx +1 -1
  55. package/src/cards/ChipSelectCard.tsx +2 -2
  56. package/src/cards/PermissionCard.tsx +438 -0
  57. package/src/cards/SelectionCard.tsx +1 -1
  58. package/src/cards/index.ts +7 -0
  59. package/src/coachmarks/runtime.ts +62 -2
  60. package/src/coachmarks/useCoachmarkTour.ts +62 -14
  61. package/src/context/deviceId.ts +4 -4
  62. package/src/features/WireFeaturesProvider.tsx +4 -1
  63. package/src/identity/userIdentity.ts +1 -1
  64. package/src/illustrations/defaultIllustrations.tsx +44 -3
  65. package/src/index.ts +38 -0
  66. package/src/permissions/index.ts +64 -0
  67. package/src/permissions/permissionCopy.ts +87 -0
  68. package/src/permissions/permissionEvents.ts +76 -0
  69. package/src/permissions/permissionMemory.ts +88 -0
  70. package/src/permissions/placement.ts +88 -0
  71. package/src/permissions/types.ts +131 -0
  72. package/src/questionnaire/QuestionnaireGate.tsx +90 -10
  73. package/src/questionnaire/index.ts +9 -1
  74. package/src/questionnaire/transport.ts +46 -11
  75. package/src/reviews/ReviewGate.tsx +61 -6
  76. package/src/reviews/index.ts +10 -1
  77. package/src/reviews/transport.ts +58 -12
  78. package/src/session/persistedSession.ts +10 -3
  79. package/src/session-analytics/useLifecycleEvents.ts +1 -1
  80. package/src/theme/ThemeContext.tsx +42 -1
  81. package/src/types.ts +66 -4
  82. package/src/utils/submitResult.ts +39 -0
package/dist/index.js CHANGED
@@ -1,412 +1,27 @@
1
1
  'use strict';
2
2
 
3
- var reactNative = require('react-native');
4
- var React18 = require('react');
3
+ var React19 = require('react');
5
4
  var wireaiRn = require('wireai-rn');
6
5
  var jsxRuntime = require('react/jsx-runtime');
6
+ var reactNative = require('react-native');
7
7
  var reactNativeSafeAreaContext = require('react-native-safe-area-context');
8
8
  var zod = require('zod');
9
9
 
10
10
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
11
11
 
12
- var React18__default = /*#__PURE__*/_interopDefault(React18);
12
+ var React19__default = /*#__PURE__*/_interopDefault(React19);
13
13
 
14
14
  var __defProp = Object.defineProperty;
15
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
16
- var __getOwnPropNames = Object.getOwnPropertyNames;
17
- var __hasOwnProp = Object.prototype.hasOwnProperty;
18
15
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
19
16
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
20
17
  }) : x)(function(x) {
21
18
  if (typeof require !== "undefined") return require.apply(this, arguments);
22
19
  throw Error('Dynamic require of "' + x + '" is not supported');
23
20
  });
24
- var __esm = (fn, res) => function __init() {
25
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
26
- };
27
21
  var __export = (target, all) => {
28
- for (var name2 in all)
29
- __defProp(target, name2, { get: all[name2], enumerable: true });
30
- };
31
- var __copyProps = (to, from, except, desc) => {
32
- if (from && typeof from === "object" || typeof from === "function") {
33
- for (let key of __getOwnPropNames(from))
34
- if (!__hasOwnProp.call(to, key) && key !== except)
35
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
36
- }
37
- return to;
22
+ for (var name in all)
23
+ __defProp(target, name, { get: all[name], enumerable: true });
38
24
  };
39
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
40
-
41
- // node_modules/expo-modules-core/src/errors/CodedError.ts
42
- var CodedError;
43
- var init_CodedError = __esm({
44
- "node_modules/expo-modules-core/src/errors/CodedError.ts"() {
45
- CodedError = class extends Error {
46
- constructor(code, message) {
47
- super(message);
48
- this.code = code;
49
- }
50
- };
51
- }
52
- });
53
-
54
- // node_modules/expo-modules-core/src/NativeModulesProxy.ts
55
- var NativeModulesProxy_default;
56
- var init_NativeModulesProxy = __esm({
57
- "node_modules/expo-modules-core/src/NativeModulesProxy.ts"() {
58
- NativeModulesProxy_default = {};
59
- }
60
- });
61
-
62
- // node_modules/expo-modules-core/src/TurboModuleToExpoModuleProxy.ts
63
- function createTurboModuleToExpoProxy(turboModule, name2) {
64
- return null;
65
- }
66
- var init_TurboModuleToExpoModuleProxy = __esm({
67
- "node_modules/expo-modules-core/src/TurboModuleToExpoModuleProxy.ts"() {
68
- }
69
- });
70
- var init_ensureNativeModulesAreInstalled = __esm({
71
- "node_modules/expo-modules-core/src/ensureNativeModulesAreInstalled.ts"() {
72
- }
73
- });
74
- function requireOptionalNativeModule(moduleName) {
75
- var _a2, _b, _c, _d, _e;
76
- try {
77
- return (_e = (_d = (_c = (_b = (_a2 = globalThis.expo) == null ? void 0 : _a2.modules) == null ? void 0 : _b[moduleName]) != null ? _c : NativeModulesProxy_default[moduleName]) != null ? _d : createTurboModuleToExpoProxy(reactNative.TurboModuleRegistry.get(moduleName), moduleName)) != null ? _e : null;
78
- } catch (e) {
79
- const error = e;
80
- console.warn(`An error occurred while requiring the '${moduleName}' module: ${error.message}`);
81
- return null;
82
- }
83
- }
84
- var init_requireNativeModule = __esm({
85
- "node_modules/expo-modules-core/src/requireNativeModule.ts"() {
86
- init_NativeModulesProxy();
87
- init_TurboModuleToExpoModuleProxy();
88
- init_ensureNativeModulesAreInstalled();
89
- }
90
- });
91
-
92
- // node_modules/expo-modules-core/src/sweet/setUpJsLogger.fx.ts
93
- var init_setUpJsLogger_fx = __esm({
94
- "node_modules/expo-modules-core/src/sweet/setUpJsLogger.fx.ts"() {
95
- init_CodedError();
96
- init_requireNativeModule();
97
- if (__DEV__ && typeof window !== "undefined" && (process.env.EXPO_OS === "android" || process.env.EXPO_OS === "ios")) {
98
- const NativeJSLogger = requireOptionalNativeModule("ExpoModulesCoreJSLogger");
99
- if (NativeJSLogger) {
100
- const onNewException = {
101
- eventName: "ExpoModulesCoreJSLogger.onNewError",
102
- action: console.error
103
- };
104
- const onNewWarning = {
105
- eventName: "ExpoModulesCoreJSLogger.onNewWarning",
106
- action: console.warn
107
- };
108
- const onNewDebug = {
109
- eventName: "ExpoModulesCoreJSLogger.onNewDebug",
110
- action: console.debug
111
- };
112
- const onNewInfo = {
113
- eventName: "ExpoModulesCoreJSLogger.onNewInfo",
114
- action: console.info
115
- };
116
- const onNewTrace = {
117
- eventName: "ExpoModulesCoreJSLogger.onNewTrace",
118
- action: console.trace
119
- };
120
- const listeners = [
121
- onNewException,
122
- onNewWarning,
123
- onNewDebug,
124
- onNewInfo,
125
- onNewTrace
126
- ];
127
- for (const listener of listeners) {
128
- NativeJSLogger.addListener(listener.eventName, ({ message }) => {
129
- listener.action(message);
130
- });
131
- }
132
- }
133
- }
134
- globalThis.ExpoModulesCore_CodedError = CodedError;
135
- }
136
- });
137
-
138
- // node_modules/expo-modules-core/src/registerWebModule.ts
139
- var init_registerWebModule = __esm({
140
- "node_modules/expo-modules-core/src/registerWebModule.ts"() {
141
- }
142
- });
143
-
144
- // node_modules/expo-modules-core/src/TypedArrays.types.ts
145
- var init_TypedArrays_types = __esm({
146
- "node_modules/expo-modules-core/src/TypedArrays.types.ts"() {
147
- }
148
- });
149
-
150
- // node_modules/expo-modules-core/src/PermissionsInterface.ts
151
- var init_PermissionsInterface = __esm({
152
- "node_modules/expo-modules-core/src/PermissionsInterface.ts"() {
153
- }
154
- });
155
- var init_PermissionsHook = __esm({
156
- "node_modules/expo-modules-core/src/PermissionsHook.ts"() {
157
- "use client";
158
- }
159
- });
160
- var init_Refs = __esm({
161
- "node_modules/expo-modules-core/src/Refs.ts"() {
162
- }
163
- });
164
- var init_useReleasingSharedObject = __esm({
165
- "node_modules/expo-modules-core/src/hooks/useReleasingSharedObject.ts"() {
166
- "use client";
167
- }
168
- });
169
-
170
- // node_modules/expo-modules-core/src/reload.ts
171
- var init_reload = __esm({
172
- "node_modules/expo-modules-core/src/reload.ts"() {
173
- }
174
- });
175
-
176
- // node_modules/expo-modules-core/src/index.ts
177
- var init_src = __esm({
178
- "node_modules/expo-modules-core/src/index.ts"() {
179
- init_setUpJsLogger_fx();
180
- init_requireNativeModule();
181
- init_registerWebModule();
182
- init_TypedArrays_types();
183
- init_PermissionsInterface();
184
- init_PermissionsHook();
185
- init_Refs();
186
- init_useReleasingSharedObject();
187
- init_reload();
188
- init_CodedError();
189
- }
190
- });
191
-
192
- // node_modules/expo-constants/build/Constants.types.js
193
- var AppOwnership, ExecutionEnvironment, UserInterfaceIdiom;
194
- var init_Constants_types = __esm({
195
- "node_modules/expo-constants/build/Constants.types.js"() {
196
- (function(AppOwnership2) {
197
- AppOwnership2["Expo"] = "expo";
198
- })(AppOwnership || (AppOwnership = {}));
199
- (function(ExecutionEnvironment2) {
200
- ExecutionEnvironment2["Bare"] = "bare";
201
- ExecutionEnvironment2["Standalone"] = "standalone";
202
- ExecutionEnvironment2["StoreClient"] = "storeClient";
203
- })(ExecutionEnvironment || (ExecutionEnvironment = {}));
204
- (function(UserInterfaceIdiom2) {
205
- UserInterfaceIdiom2["Handset"] = "handset";
206
- UserInterfaceIdiom2["Tablet"] = "tablet";
207
- UserInterfaceIdiom2["Desktop"] = "desktop";
208
- UserInterfaceIdiom2["TV"] = "tv";
209
- UserInterfaceIdiom2["Unsupported"] = "unsupported";
210
- })(UserInterfaceIdiom || (UserInterfaceIdiom = {}));
211
- }
212
- });
213
-
214
- // node_modules/expo-constants/build/ExponentConstants.js
215
- var ExponentConstants_default;
216
- var init_ExponentConstants = __esm({
217
- "node_modules/expo-constants/build/ExponentConstants.js"() {
218
- init_src();
219
- ExponentConstants_default = requireOptionalNativeModule("ExponentConstants");
220
- }
221
- });
222
-
223
- // node_modules/expo-constants/build/Constants.js
224
- var Constants_exports = {};
225
- __export(Constants_exports, {
226
- AppOwnership: () => AppOwnership,
227
- ExecutionEnvironment: () => ExecutionEnvironment,
228
- UserInterfaceIdiom: () => UserInterfaceIdiom,
229
- default: () => Constants_default
230
- });
231
- function isEmbeddedManifest(manifest) {
232
- return !isExpoUpdatesManifest(manifest);
233
- }
234
- function isExpoUpdatesManifest(manifest) {
235
- return "metadata" in manifest;
236
- }
237
- function getManifest(suppressWarning = false) {
238
- if (!rawManifest) {
239
- const invalidManifestType = rawManifest === null ? "null" : "undefined";
240
- if (nativeConstants.executionEnvironment === ExecutionEnvironment.Bare && reactNative.Platform.OS !== "web") {
241
- if (!suppressWarning) {
242
- console.warn(`Constants.manifest is ${invalidManifestType} because the embedded app.config could not be read. Ensure that you have installed the expo-constants build scripts if you need to read from Constants.manifest.`);
243
- }
244
- } else if (nativeConstants.executionEnvironment === ExecutionEnvironment.StoreClient || nativeConstants.executionEnvironment === ExecutionEnvironment.Standalone) {
245
- throw new CodedError("ERR_CONSTANTS_MANIFEST_UNAVAILABLE", `Constants.manifest is ${invalidManifestType}, must be an object.`);
246
- }
247
- }
248
- return rawManifest;
249
- }
250
- var ExpoUpdates, rawUpdatesManifest, rawDevLauncherManifest, rawAppConfig, _a, rawManifest, name, appOwnership, nativeConstants, constants, Constants_default;
251
- var init_Constants = __esm({
252
- "node_modules/expo-constants/build/Constants.js"() {
253
- init_src();
254
- init_Constants_types();
255
- init_ExponentConstants();
256
- init_Constants_types();
257
- if (!ExponentConstants_default) {
258
- console.warn("No native ExponentConstants module found, are you sure the expo-constants's module is linked properly?");
259
- }
260
- ExpoUpdates = requireOptionalNativeModule("ExpoUpdates");
261
- rawUpdatesManifest = null;
262
- if (ExpoUpdates) {
263
- let updatesManifest;
264
- if (ExpoUpdates.manifest) {
265
- updatesManifest = ExpoUpdates.manifest;
266
- } else if (ExpoUpdates.manifestString) {
267
- updatesManifest = JSON.parse(ExpoUpdates.manifestString);
268
- }
269
- if (updatesManifest && Object.keys(updatesManifest).length > 0) {
270
- rawUpdatesManifest = updatesManifest;
271
- }
272
- }
273
- rawDevLauncherManifest = null;
274
- if (reactNative.NativeModules.EXDevLauncher) {
275
- let devLauncherManifest;
276
- if (reactNative.NativeModules.EXDevLauncher.manifestString) {
277
- devLauncherManifest = JSON.parse(reactNative.NativeModules.EXDevLauncher.manifestString);
278
- }
279
- if (devLauncherManifest && Object.keys(devLauncherManifest).length > 0) {
280
- rawDevLauncherManifest = devLauncherManifest;
281
- }
282
- }
283
- rawAppConfig = null;
284
- if (ExponentConstants_default && ExponentConstants_default.manifest) {
285
- const appConfig = ExponentConstants_default.manifest;
286
- if (typeof appConfig === "string") {
287
- rawAppConfig = JSON.parse(appConfig);
288
- } else {
289
- rawAppConfig = appConfig;
290
- }
291
- }
292
- rawManifest = (_a = rawUpdatesManifest != null ? rawUpdatesManifest : rawDevLauncherManifest) != null ? _a : rawAppConfig;
293
- ({ name, appOwnership, ...nativeConstants } = ExponentConstants_default || {});
294
- constants = {
295
- ...nativeConstants,
296
- // Ensure this is null in bare workflow
297
- appOwnership: appOwnership != null ? appOwnership : null
298
- };
299
- Object.defineProperties(constants, {
300
- /**
301
- * Use `manifest` property by default.
302
- * This property is only used for internal purposes.
303
- * It behaves similarly to the original one, but suppresses warning upon no manifest available.
304
- * `expo-asset` uses it to prevent users from seeing mentioned warning.
305
- */
306
- __unsafeNoWarnManifest: {
307
- get() {
308
- const maybeManifest = getManifest(true);
309
- if (!maybeManifest || !isEmbeddedManifest(maybeManifest)) {
310
- return null;
311
- }
312
- return maybeManifest;
313
- },
314
- enumerable: false
315
- },
316
- __unsafeNoWarnManifest2: {
317
- get() {
318
- const maybeManifest = getManifest(true);
319
- if (!maybeManifest || !isExpoUpdatesManifest(maybeManifest)) {
320
- return null;
321
- }
322
- return maybeManifest;
323
- },
324
- enumerable: false
325
- },
326
- manifest: {
327
- get() {
328
- const maybeManifest = getManifest();
329
- if (!maybeManifest || !isEmbeddedManifest(maybeManifest)) {
330
- return null;
331
- }
332
- return maybeManifest;
333
- },
334
- enumerable: true
335
- },
336
- manifest2: {
337
- get() {
338
- const maybeManifest = getManifest();
339
- if (!maybeManifest || !isExpoUpdatesManifest(maybeManifest)) {
340
- return null;
341
- }
342
- return maybeManifest;
343
- },
344
- enumerable: true
345
- },
346
- expoConfig: {
347
- get() {
348
- var _a2, _b;
349
- const maybeManifest = getManifest(true);
350
- if (!maybeManifest) {
351
- return null;
352
- }
353
- if (ExpoUpdates && ExpoUpdates.isEmbeddedLaunch) {
354
- return rawAppConfig;
355
- }
356
- if (isExpoUpdatesManifest(maybeManifest)) {
357
- return (_b = (_a2 = maybeManifest.extra) == null ? void 0 : _a2.expoClient) != null ? _b : null;
358
- } else if (isEmbeddedManifest(maybeManifest)) {
359
- return maybeManifest;
360
- }
361
- return null;
362
- },
363
- enumerable: true
364
- },
365
- expoGoConfig: {
366
- get() {
367
- var _a2, _b;
368
- const maybeManifest = getManifest(true);
369
- if (!maybeManifest) {
370
- return null;
371
- }
372
- if (isExpoUpdatesManifest(maybeManifest)) {
373
- return (_b = (_a2 = maybeManifest.extra) == null ? void 0 : _a2.expoGo) != null ? _b : null;
374
- } else if (isEmbeddedManifest(maybeManifest)) {
375
- return maybeManifest;
376
- }
377
- return null;
378
- },
379
- enumerable: true
380
- },
381
- easConfig: {
382
- get() {
383
- var _a2, _b;
384
- const maybeManifest = getManifest(true);
385
- if (!maybeManifest) {
386
- return null;
387
- }
388
- if (isExpoUpdatesManifest(maybeManifest)) {
389
- return (_b = (_a2 = maybeManifest.extra) == null ? void 0 : _a2.eas) != null ? _b : null;
390
- } else if (isEmbeddedManifest(maybeManifest)) {
391
- return maybeManifest;
392
- }
393
- return null;
394
- },
395
- enumerable: true
396
- },
397
- __rawManifest_TEST: {
398
- get() {
399
- return rawManifest;
400
- },
401
- set(value) {
402
- rawManifest = value;
403
- },
404
- enumerable: false
405
- }
406
- });
407
- Constants_default = constants;
408
- }
409
- });
410
25
 
411
26
  // src/theme/defaultTheme.ts
412
27
  var defaultOnboardingTheme = {
@@ -464,33 +79,38 @@ var mergeTheme = (override) => {
464
79
  button: { ...defaultOnboardingTheme.button, ...override.button }
465
80
  };
466
81
  };
467
- var OnboardingThemeContext = React18.createContext(null);
82
+ var THEME_CONTEXT_SLOT = /* @__PURE__ */ Symbol.for("@wireai/activation:themeContext");
83
+ var themeGlobal = globalThis;
84
+ if (!themeGlobal[THEME_CONTEXT_SLOT]) {
85
+ themeGlobal[THEME_CONTEXT_SLOT] = React19.createContext(null);
86
+ }
87
+ var OnboardingThemeContext = themeGlobal[THEME_CONTEXT_SLOT];
468
88
  var OnboardingThemeProvider = ({
469
89
  theme,
470
90
  children
471
91
  }) => {
472
- const resolved = React18.useMemo(() => mergeTheme(theme), [theme]);
92
+ const resolved = React19.useMemo(() => mergeTheme(theme), [theme]);
473
93
  return /* @__PURE__ */ jsxRuntime.jsx(OnboardingThemeContext.Provider, { value: resolved, children });
474
94
  };
475
95
  var useOnboardingTheme = () => {
476
- const ctx = React18.useContext(OnboardingThemeContext);
96
+ const ctx = React19.useContext(OnboardingThemeContext);
477
97
  return ctx != null ? ctx : mergeTheme();
478
98
  };
479
99
  var EMPTY_REGISTRY = {};
480
- var IllustrationContext = React18.createContext(EMPTY_REGISTRY);
100
+ var IllustrationContext = React19.createContext(EMPTY_REGISTRY);
481
101
  var IllustrationProvider = ({
482
102
  registry,
483
103
  children
484
104
  }) => {
485
- const value = React18.useMemo(() => registry != null ? registry : EMPTY_REGISTRY, [registry]);
105
+ const value = React19.useMemo(() => registry != null ? registry : EMPTY_REGISTRY, [registry]);
486
106
  return /* @__PURE__ */ jsxRuntime.jsx(IllustrationContext.Provider, { value, children });
487
107
  };
488
- var useIllustration = (name2) => {
489
- const registry = React18.useContext(IllustrationContext);
490
- if (!name2) return void 0;
491
- return registry[name2];
108
+ var useIllustration = (name) => {
109
+ const registry = React19.useContext(IllustrationContext);
110
+ if (!name) return void 0;
111
+ return registry[name];
492
112
  };
493
- var Glyph = ({ children }) => {
113
+ var Disc = ({ children }) => {
494
114
  const t = useOnboardingTheme();
495
115
  return /* @__PURE__ */ jsxRuntime.jsx(
496
116
  reactNative.View,
@@ -499,11 +119,23 @@ var Glyph = ({ children }) => {
499
119
  styles.disc,
500
120
  { backgroundColor: t.colors.primarySoft, borderRadius: t.radius.full }
501
121
  ],
502
- children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles.glyphText, { color: t.colors.primary }], allowFontScaling: false, children })
122
+ children
503
123
  }
504
124
  );
505
125
  };
126
+ var Glyph = ({ children }) => {
127
+ const t = useOnboardingTheme();
128
+ return /* @__PURE__ */ jsxRuntime.jsx(Disc, { children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles.glyphText, { color: t.colors.primary }], allowFontScaling: false, children }) });
129
+ };
506
130
  var MomentumGlyph = () => /* @__PURE__ */ jsxRuntime.jsx(Glyph, { children: "\u2197" });
131
+ var NotificationsGlyph = () => {
132
+ const t = useOnboardingTheme();
133
+ return /* @__PURE__ */ jsxRuntime.jsx(Disc, { children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles.bell, children: [
134
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles.bellBody, { backgroundColor: t.colors.primary }] }),
135
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles.bellRim, { backgroundColor: t.colors.primary }] }),
136
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles.bellClapper, { backgroundColor: t.colors.primary }] })
137
+ ] }) });
138
+ };
507
139
  var BeforeAfterGlyph = () => {
508
140
  const t = useOnboardingTheme();
509
141
  return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles.row, children: [
@@ -530,7 +162,8 @@ var BeforeAfterGlyph = () => {
530
162
  };
531
163
  var defaultIllustrations = {
532
164
  momentum: /* @__PURE__ */ jsxRuntime.jsx(MomentumGlyph, {}),
533
- "before-after": /* @__PURE__ */ jsxRuntime.jsx(BeforeAfterGlyph, {})
165
+ "before-after": /* @__PURE__ */ jsxRuntime.jsx(BeforeAfterGlyph, {}),
166
+ notifications: /* @__PURE__ */ jsxRuntime.jsx(NotificationsGlyph, {})
534
167
  };
535
168
  var styles = reactNative.StyleSheet.create({
536
169
  disc: {
@@ -558,22 +191,27 @@ var styles = reactNative.StyleSheet.create({
558
191
  arrow: {
559
192
  fontSize: 28,
560
193
  fontWeight: "700"
561
- }
194
+ },
195
+ // The bell: a domed body over a wider rim, with the clapper hanging below it.
196
+ bell: { alignItems: "center", justifyContent: "center" },
197
+ bellBody: { width: 34, height: 30, borderTopLeftRadius: 17, borderTopRightRadius: 17 },
198
+ bellRim: { width: 44, height: 5, borderRadius: 3, marginTop: 2 },
199
+ bellClapper: { width: 9, height: 9, borderRadius: 5, marginTop: 3 }
562
200
  });
563
201
  var EMPTY_REGISTRY2 = {};
564
- var IconContext = React18.createContext(EMPTY_REGISTRY2);
202
+ var IconContext = React19.createContext(EMPTY_REGISTRY2);
565
203
  var IconRegistryProvider = ({
566
204
  registry,
567
205
  children
568
206
  }) => {
569
- const value = React18.useMemo(() => registry != null ? registry : EMPTY_REGISTRY2, [registry]);
207
+ const value = React19.useMemo(() => registry != null ? registry : EMPTY_REGISTRY2, [registry]);
570
208
  return /* @__PURE__ */ jsxRuntime.jsx(IconContext.Provider, { value, children });
571
209
  };
572
- var useHostIcon = (name2) => {
573
- const registry = React18.useContext(IconContext);
574
- if (!name2) return void 0;
575
- if (!Object.prototype.hasOwnProperty.call(registry, name2)) return void 0;
576
- return registry[name2];
210
+ var useHostIcon = (name) => {
211
+ const registry = React19.useContext(IconContext);
212
+ if (!name) return void 0;
213
+ if (!Object.prototype.hasOwnProperty.call(registry, name)) return void 0;
214
+ return registry[name];
577
215
  };
578
216
 
579
217
  // src/theme/typography.ts
@@ -589,9 +227,9 @@ var captionStyle = (f) => ({
589
227
  lineHeight: round(f.baseSize * 0.8125 * f.scale * 1.5)
590
228
  });
591
229
  var labelStyle = (f) => {
592
- var _a2;
230
+ var _a;
593
231
  return {
594
- fontFamily: (_a2 = f.medium) != null ? _a2 : f.bold,
232
+ fontFamily: (_a = f.medium) != null ? _a : f.bold,
595
233
  fontSize: round(f.baseSize * f.scale),
596
234
  lineHeight: round(f.baseSize * f.scale * 1.4),
597
235
  fontWeight: f.medium ? void 0 : "600"
@@ -604,9 +242,9 @@ var headingStyle = (f) => ({
604
242
  fontWeight: f.bold ? void 0 : "700"
605
243
  });
606
244
  var buttonLabelStyle = (f) => {
607
- var _a2;
245
+ var _a;
608
246
  return {
609
- fontFamily: (_a2 = f.bold) != null ? _a2 : f.medium,
247
+ fontFamily: (_a = f.bold) != null ? _a : f.medium,
610
248
  fontSize: round(f.baseSize * f.scale),
611
249
  fontWeight: f.bold ? void 0 : "600"
612
250
  };
@@ -729,8 +367,8 @@ var stateChangeDuration = (reduced, durationMs) => reduced ? 0 : durationMs;
729
367
  var loopEnabled = (reduced) => !reduced;
730
368
  var lastKnown = false;
731
369
  var useReducedMotion = () => {
732
- const [reduced, setReduced] = React18.useState(lastKnown);
733
- React18.useEffect(() => {
370
+ const [reduced, setReduced] = React19.useState(lastKnown);
371
+ React19.useEffect(() => {
734
372
  let mounted = true;
735
373
  reactNative.AccessibilityInfo.isReduceMotionEnabled().then((value) => {
736
374
  lastKnown = value;
@@ -752,8 +390,8 @@ var ROTATION_MS = 1800;
752
390
  var _AnimatedSparkle = ({ size = 30, color, variant = "spin" }) => {
753
391
  const t = useOnboardingTheme();
754
392
  const reduced = useReducedMotion();
755
- const progress = React18.useRef(new reactNative.Animated.Value(0)).current;
756
- React18.useEffect(() => {
393
+ const progress = React19.useRef(new reactNative.Animated.Value(0)).current;
394
+ React19.useEffect(() => {
757
395
  if (!loopEnabled(reduced)) {
758
396
  progress.setValue(0);
759
397
  return;
@@ -808,7 +446,7 @@ var _AnimatedSparkle = ({ size = 30, color, variant = "spin" }) => {
808
446
  }
809
447
  ) });
810
448
  };
811
- var AnimatedSparkle = React18__default.default.memo(_AnimatedSparkle);
449
+ var AnimatedSparkle = React19__default.default.memo(_AnimatedSparkle);
812
450
  var styles2 = reactNative.StyleSheet.create({
813
451
  box: { alignItems: "center", justifyContent: "center" },
814
452
  glyph: {
@@ -820,8 +458,8 @@ var styles2 = reactNative.StyleSheet.create({
820
458
  var STAGE_SIZE = 96;
821
459
  var ORBIT_DOT = 9;
822
460
  var OrbitDot = ({ color, reduced }) => {
823
- const spin = React18.useRef(new reactNative.Animated.Value(0)).current;
824
- React18.useEffect(() => {
461
+ const spin = React19.useRef(new reactNative.Animated.Value(0)).current;
462
+ React19.useEffect(() => {
825
463
  if (!loopEnabled(reduced)) return;
826
464
  const loop = reactNative.Animated.loop(
827
465
  reactNative.Animated.timing(spin, {
@@ -844,8 +482,8 @@ var SkeletonLine = ({
844
482
  color,
845
483
  reduced
846
484
  }) => {
847
- const pulse = React18.useRef(new reactNative.Animated.Value(1)).current;
848
- React18.useEffect(() => {
485
+ const pulse = React19.useRef(new reactNative.Animated.Value(1)).current;
486
+ React19.useEffect(() => {
849
487
  if (!loopEnabled(reduced)) return;
850
488
  const loop = reactNative.Animated.loop(
851
489
  reactNative.Animated.sequence([
@@ -880,8 +518,8 @@ var _LoadingBlock = ({
880
518
  }) => {
881
519
  const t = useOnboardingTheme();
882
520
  const reduced = useReducedMotion();
883
- const [showSkeleton, setShowSkeleton] = React18.useState(false);
884
- React18.useEffect(() => {
521
+ const [showSkeleton, setShowSkeleton] = React19.useState(false);
522
+ React19.useEffect(() => {
885
523
  const timer = setTimeout(() => setShowSkeleton(true), SKELETON_DELAY_MS);
886
524
  return () => clearTimeout(timer);
887
525
  }, []);
@@ -911,7 +549,7 @@ var _LoadingBlock = ({
911
549
  )
912
550
  ] });
913
551
  };
914
- var LoadingBlock = React18__default.default.memo(_LoadingBlock);
552
+ var LoadingBlock = React19__default.default.memo(_LoadingBlock);
915
553
  var styles3 = reactNative.StyleSheet.create({
916
554
  center: { flex: 1, alignItems: "center", justifyContent: "center" },
917
555
  // Absolutely-positioned label column, pinned just under the centered stage: its top
@@ -956,7 +594,7 @@ var _LoadingScreen = ({ title, hint }) => {
956
594
  }
957
595
  );
958
596
  };
959
- var LoadingScreen = React18__default.default.memo(_LoadingScreen);
597
+ var LoadingScreen = React19__default.default.memo(_LoadingScreen);
960
598
  var styles4 = reactNative.StyleSheet.create({
961
599
  flex: { flex: 1 }
962
600
  });
@@ -975,8 +613,8 @@ var targetFor = (step, complete, approxScreens) => {
975
613
  var _StepProgress = ({ step, complete, approxScreens }) => {
976
614
  const t = useOnboardingTheme();
977
615
  const reduced = useReducedMotion();
978
- const fill = React18.useRef(new reactNative.Animated.Value(targetFor(step, complete, approxScreens))).current;
979
- React18.useEffect(() => {
616
+ const fill = React19.useRef(new reactNative.Animated.Value(targetFor(step, complete, approxScreens))).current;
617
+ React19.useEffect(() => {
980
618
  reactNative.Animated.timing(fill, {
981
619
  toValue: targetFor(step, complete, approxScreens),
982
620
  duration: scaledMs(stateChangeDuration(reduced, PROGRESS_MS)),
@@ -996,7 +634,7 @@ var _StepProgress = ({ step, complete, approxScreens }) => {
996
634
  }
997
635
  ) });
998
636
  };
999
- var StepProgress = React18__default.default.memo(_StepProgress);
637
+ var StepProgress = React19__default.default.memo(_StepProgress);
1000
638
  var styles5 = reactNative.StyleSheet.create({
1001
639
  track: { height: 6, width: "100%", overflow: "hidden" },
1002
640
  fill: { height: 6 }
@@ -1012,31 +650,31 @@ var _OnboardingScaffold = ({
1012
650
  children
1013
651
  }) => {
1014
652
  const t = useOnboardingTheme();
1015
- const styles17 = React18.useMemo(() => makeStyles(t), [t]);
653
+ const styles18 = React19.useMemo(() => makeStyles(t), [t]);
1016
654
  const hit = { top: 12, bottom: 12, left: 12, right: 12 };
1017
655
  return /* @__PURE__ */ jsxRuntime.jsxs(
1018
656
  reactNativeSafeAreaContext.SafeAreaView,
1019
657
  {
1020
658
  edges: ["top", "bottom"],
1021
- style: [styles17.flex, { backgroundColor: t.colors.background }],
659
+ style: [styles18.flex, { backgroundColor: t.colors.background }],
1022
660
  children: [
1023
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles17.header, { paddingHorizontal: t.spacing.md, paddingTop: t.spacing.md }], children: [
1024
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles17.progressWrap, children: /* @__PURE__ */ jsxRuntime.jsx(StepProgress, { step, complete, approxScreens }) }),
1025
- onSkip ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.TouchableOpacity, { onPress: onSkip, hitSlop: hit, style: styles17.skip, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [bodyStyle(t.fonts), { color: t.colors.textMuted }], children: skipLabel }) }) : null
661
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles18.header, { paddingHorizontal: t.spacing.md, paddingTop: t.spacing.md }], children: [
662
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles18.progressWrap, children: /* @__PURE__ */ jsxRuntime.jsx(StepProgress, { step, complete, approxScreens }) }),
663
+ onSkip ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.TouchableOpacity, { onPress: onSkip, hitSlop: hit, style: styles18.skip, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [bodyStyle(t.fonts), { color: t.colors.textMuted }], children: skipLabel }) }) : null
1026
664
  ] }),
1027
665
  /* @__PURE__ */ jsxRuntime.jsx(
1028
666
  reactNative.View,
1029
667
  {
1030
- style: [styles17.body, { paddingHorizontal: t.spacing.md, paddingTop: t.spacing.md }],
668
+ style: [styles18.body, { paddingHorizontal: t.spacing.md, paddingTop: t.spacing.md }],
1031
669
  children
1032
670
  }
1033
671
  ),
1034
- onBack ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles17.footer, { paddingHorizontal: t.spacing.md, paddingBottom: t.spacing.sm }], children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.TouchableOpacity, { onPress: onBack, hitSlop: hit, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [bodyStyle(t.fonts), { color: t.colors.textMuted }], children: backLabel }) }) }) : null
672
+ onBack ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles18.footer, { paddingHorizontal: t.spacing.md, paddingBottom: t.spacing.sm }], children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.TouchableOpacity, { onPress: onBack, hitSlop: hit, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [bodyStyle(t.fonts), { color: t.colors.textMuted }], children: backLabel }) }) }) : null
1035
673
  ]
1036
674
  }
1037
675
  );
1038
676
  };
1039
- var OnboardingScaffold = React18__default.default.memo(_OnboardingScaffold);
677
+ var OnboardingScaffold = React19__default.default.memo(_OnboardingScaffold);
1040
678
  function makeStyles(t) {
1041
679
  return reactNative.StyleSheet.create({
1042
680
  flex: { flex: 1 },
@@ -1057,8 +695,8 @@ var _Button = ({
1057
695
  const t = useOnboardingTheme();
1058
696
  const reduced = useReducedMotion();
1059
697
  const isPrimary = variant === "primary";
1060
- const pressT = React18.useRef(new reactNative.Animated.Value(0)).current;
1061
- const pressTo = React18.useCallback(
698
+ const pressT = React19.useRef(new reactNative.Animated.Value(0)).current;
699
+ const pressTo = React19.useCallback(
1062
700
  (to) => {
1063
701
  if (reduced) {
1064
702
  reactNative.Animated.timing(pressT, {
@@ -1077,16 +715,16 @@ var _Button = ({
1077
715
  },
1078
716
  [pressT, reduced]
1079
717
  );
1080
- const lastPress = React18.useRef(0);
1081
- const handlePress = React18.useCallback(() => {
718
+ const lastPress = React19.useRef(0);
719
+ const handlePress = React19.useCallback(() => {
1082
720
  if (!onPress) return;
1083
721
  const now = Date.now();
1084
722
  if (now - lastPress.current < 500) return;
1085
723
  lastPress.current = now;
1086
724
  onPress();
1087
725
  }, [onPress]);
1088
- const handlePressIn = React18.useCallback(() => pressTo(1), [pressTo]);
1089
- const handlePressOut = React18.useCallback(() => pressTo(0), [pressTo]);
726
+ const handlePressIn = React19.useCallback(() => pressTo(1), [pressTo]);
727
+ const handlePressOut = React19.useCallback(() => pressTo(0), [pressTo]);
1090
728
  const scale = pressT.interpolate({
1091
729
  inputRange: [0, 1],
1092
730
  outputRange: [1, reduced ? 1 : BUTTON_PRESS_SCALE]
@@ -1137,7 +775,7 @@ var _Button = ({
1137
775
  }
1138
776
  );
1139
777
  };
1140
- var Button = React18__default.default.memo(_Button);
778
+ var Button = React19__default.default.memo(_Button);
1141
779
  var styles6 = reactNative.StyleSheet.create({
1142
780
  base: { alignItems: "center", justifyContent: "center" },
1143
781
  fill: { width: "100%" }
@@ -1155,7 +793,7 @@ var _ErrorBlock = ({
1155
793
  onRetry ? /* @__PURE__ */ jsxRuntime.jsx(Button, { title: retryLabel, onPress: onRetry, variant: "outline" }) : null
1156
794
  ] });
1157
795
  };
1158
- var ErrorBlock = React18__default.default.memo(_ErrorBlock);
796
+ var ErrorBlock = React19__default.default.memo(_ErrorBlock);
1159
797
  var styles7 = reactNative.StyleSheet.create({
1160
798
  center: { flex: 1, alignItems: "center", justifyContent: "center" }
1161
799
  });
@@ -1168,10 +806,10 @@ var _CardLayout = ({
1168
806
  titleNode
1169
807
  }) => {
1170
808
  const t = useOnboardingTheme();
1171
- const styles17 = React18.useMemo(() => makeStyles2(t), [t]);
809
+ const styles18 = React19.useMemo(() => makeStyles2(t), [t]);
1172
810
  const hasHeader = !!title || !!subtitle || !!titleNode;
1173
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.KeyboardAvoidingView, { style: styles17.fill, behavior: "padding", children: [
1174
- hasHeader ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles17.header, children: [
811
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.KeyboardAvoidingView, { style: styles18.fill, behavior: "padding", children: [
812
+ hasHeader ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles18.header, children: [
1175
813
  titleNode != null ? titleNode : title ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [labelStyle(t.fonts), { color: t.colors.text }], children: title }) : null,
1176
814
  subtitle ? /* @__PURE__ */ jsxRuntime.jsx(
1177
815
  reactNative.Text,
@@ -1187,10 +825,10 @@ var _CardLayout = ({
1187
825
  /* @__PURE__ */ jsxRuntime.jsx(
1188
826
  reactNative.ScrollView,
1189
827
  {
1190
- style: styles17.fill,
828
+ style: styles18.fill,
1191
829
  contentContainerStyle: [
1192
- styles17.scrollContent,
1193
- align === "center" && styles17.center
830
+ styles18.scrollContent,
831
+ align === "center" && styles18.center
1194
832
  ],
1195
833
  keyboardShouldPersistTaps: "handled",
1196
834
  keyboardDismissMode: "on-drag",
@@ -1198,10 +836,10 @@ var _CardLayout = ({
1198
836
  children
1199
837
  }
1200
838
  ),
1201
- footer ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles17.footer, children: footer }) : null
839
+ footer ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles18.footer, children: footer }) : null
1202
840
  ] });
1203
841
  };
1204
- var CardLayout = React18__default.default.memo(_CardLayout);
842
+ var CardLayout = React19__default.default.memo(_CardLayout);
1205
843
  function makeStyles2(t) {
1206
844
  return reactNative.StyleSheet.create({
1207
845
  fill: { flex: 1 },
@@ -1224,12 +862,12 @@ var _CompletionView = ({
1224
862
  }) => {
1225
863
  const t = useOnboardingTheme();
1226
864
  const reduced = useReducedMotion();
1227
- const [submitted, setSubmitted] = React18.useState(false);
1228
- const pop = React18.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
1229
- const burst = React18.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
1230
- const titleT = React18.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
1231
- const messageT = React18.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
1232
- React18.useEffect(() => {
865
+ const [submitted, setSubmitted] = React19.useState(false);
866
+ const pop = React19.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
867
+ const burst = React19.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
868
+ const titleT = React19.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
869
+ const messageT = React19.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
870
+ React19.useEffect(() => {
1233
871
  if (reduced) {
1234
872
  pop.setValue(1);
1235
873
  burst.setValue(1);
@@ -1264,7 +902,7 @@ var _CompletionView = ({
1264
902
  anims.forEach((a) => a.start());
1265
903
  return () => anims.forEach((a) => a.stop());
1266
904
  }, [reduced, pop, burst, titleT, messageT]);
1267
- const handlePress = React18.useCallback(() => {
905
+ const handlePress = React19.useCallback(() => {
1268
906
  if (submitted) return;
1269
907
  setSubmitted(true);
1270
908
  onContinue();
@@ -1326,7 +964,7 @@ var _CompletionView = ({
1326
964
  }
1327
965
  );
1328
966
  };
1329
- var CompletionView = React18__default.default.memo(_CompletionView);
967
+ var CompletionView = React19__default.default.memo(_CompletionView);
1330
968
  var styles8 = reactNative.StyleSheet.create({
1331
969
  center: { width: "100%", alignItems: "center", justifyContent: "center" },
1332
970
  badgeStage: {
@@ -1348,14 +986,14 @@ var _CardHandoff = ({
1348
986
  children
1349
987
  }) => {
1350
988
  const reduced = useReducedMotion();
1351
- const [leaving, setLeaving] = React18.useState(null);
1352
- const enterT = React18.useRef(new reactNative.Animated.Value(0)).current;
1353
- const exitT = React18.useRef(new reactNative.Animated.Value(0)).current;
1354
- const prev = React18.useRef({ key: transitionKey, node: children, variant });
1355
- const running = React18.useRef({});
1356
- const mounted = React18.useRef(false);
1357
- React18.useEffect(() => {
1358
- var _a2, _b;
989
+ const [leaving, setLeaving] = React19.useState(null);
990
+ const enterT = React19.useRef(new reactNative.Animated.Value(0)).current;
991
+ const exitT = React19.useRef(new reactNative.Animated.Value(0)).current;
992
+ const prev = React19.useRef({ key: transitionKey, node: children, variant });
993
+ const running = React19.useRef({});
994
+ const mounted = React19.useRef(false);
995
+ React19.useEffect(() => {
996
+ var _a, _b;
1359
997
  const last = prev.current;
1360
998
  prev.current = { key: transitionKey, node: children, variant };
1361
999
  if (!mounted.current) {
@@ -1364,7 +1002,7 @@ var _CardHandoff = ({
1364
1002
  return;
1365
1003
  }
1366
1004
  if (last.key === transitionKey) return;
1367
- (_a2 = running.current.enter) == null ? void 0 : _a2.stop();
1005
+ (_a = running.current.enter) == null ? void 0 : _a.stop();
1368
1006
  (_b = running.current.exit) == null ? void 0 : _b.stop();
1369
1007
  setLeaving(last);
1370
1008
  enterT.setValue(0);
@@ -1387,10 +1025,10 @@ var _CardHandoff = ({
1387
1025
  );
1388
1026
  running.current.enter = enter;
1389
1027
  });
1390
- React18.useEffect(
1028
+ React19.useEffect(
1391
1029
  () => () => {
1392
- var _a2, _b;
1393
- (_a2 = running.current.enter) == null ? void 0 : _a2.stop();
1030
+ var _a, _b;
1031
+ (_a = running.current.enter) == null ? void 0 : _a.stop();
1394
1032
  (_b = running.current.exit) == null ? void 0 : _b.stop();
1395
1033
  },
1396
1034
  []
@@ -1456,7 +1094,7 @@ var transformFor = (variant, t, reduced, layer, travel) => {
1456
1094
  }
1457
1095
  ];
1458
1096
  };
1459
- var CardHandoff = React18__default.default.memo(_CardHandoff);
1097
+ var CardHandoff = React19__default.default.memo(_CardHandoff);
1460
1098
  var styles9 = reactNative.StyleSheet.create({
1461
1099
  fill: { flex: 1 }
1462
1100
  });
@@ -1471,13 +1109,13 @@ var tryParse = (s) => {
1471
1109
  }
1472
1110
  };
1473
1111
  var deriveAnswers = (messages) => {
1474
- var _a2, _b, _c;
1112
+ var _a, _b, _c;
1475
1113
  const out = {};
1476
1114
  for (let i = 0; i < messages.length; i++) {
1477
1115
  const m = messages[i];
1478
1116
  const r = m == null ? void 0 : m.response;
1479
1117
  if (!m || m.role !== "assistant" || !r || r.action !== "render") continue;
1480
- const props = (_a2 = r.props) != null ? _a2 : {};
1118
+ const props = (_a = r.props) != null ? _a : {};
1481
1119
  const progress = props.progress;
1482
1120
  const questionText = (_c = (_b = props.title) != null ? _b : props.label) != null ? _c : props.question;
1483
1121
  const key = (progress == null ? void 0 : progress.slot_id) || (progress == null ? void 0 : progress.key) || questionText;
@@ -1491,9 +1129,9 @@ var deriveAnswers = (messages) => {
1491
1129
 
1492
1130
  // src/utils/readProgress.ts
1493
1131
  var readProgress = (response) => {
1494
- var _a2;
1132
+ var _a;
1495
1133
  if (!response || response.action !== "render") return {};
1496
- const props = (_a2 = response.props) != null ? _a2 : {};
1134
+ const props = (_a = response.props) != null ? _a : {};
1497
1135
  const raw = props.progress;
1498
1136
  if (!raw || typeof raw !== "object") return {};
1499
1137
  const p = raw;
@@ -1509,13 +1147,24 @@ var readProgress = (response) => {
1509
1147
 
1510
1148
  // src/analytics/reportClientEvent.ts
1511
1149
  var makeSessionId = () => `wire_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
1150
+ var toWireEvents = (events) => events.map((event) => {
1151
+ if (typeof event.ts !== "number") return event;
1152
+ const { ts, ...rest } = event;
1153
+ if (!Number.isFinite(ts)) return rest;
1154
+ try {
1155
+ return { ...rest, ts: new Date(ts).toISOString() };
1156
+ } catch {
1157
+ return rest;
1158
+ }
1159
+ });
1512
1160
  var buildEventsRequest = (target, events) => {
1513
1161
  if (!(target == null ? void 0 : target.serverUrl) || events.length === 0) return null;
1514
1162
  try {
1515
1163
  const url = `${target.serverUrl.replace(/\/$/, "")}/v1/events`;
1516
1164
  const headers = { "Content-Type": "application/json" };
1517
1165
  if (target.apiKey) headers.Authorization = `Bearer ${target.apiKey}`;
1518
- return { url, init: { method: "POST", headers, body: JSON.stringify({ events }) } };
1166
+ const body = JSON.stringify({ events: toWireEvents(events) });
1167
+ return { url, init: { method: "POST", headers, body } };
1519
1168
  } catch {
1520
1169
  return null;
1521
1170
  }
@@ -1527,7 +1176,13 @@ var readEventsAck = async (res) => {
1527
1176
  const body = await Promise.resolve(json.call(res));
1528
1177
  const skipped = body == null ? void 0 : body.skipped;
1529
1178
  if (typeof skipped !== "number" || !Number.isFinite(skipped)) return void 0;
1530
- 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") : [];
1179
+ const reasons = Array.isArray(body == null ? void 0 : body.errors) ? body.errors.map((e) => {
1180
+ const entry = e;
1181
+ const reason = entry == null ? void 0 : entry.reason;
1182
+ if (typeof reason !== "string") return void 0;
1183
+ const field = entry == null ? void 0 : entry.field;
1184
+ return typeof field === "string" && field.length > 0 ? `${reason} (field: ${field})` : reason;
1185
+ }).filter((r) => typeof r === "string") : [];
1531
1186
  return { written: typeof (body == null ? void 0 : body.written) === "number" ? body.written : void 0, skipped, reasons };
1532
1187
  } catch {
1533
1188
  return void 0;
@@ -1597,6 +1252,382 @@ var sendPreview = (target, { sessionId, userMessage }) => {
1597
1252
  } catch {
1598
1253
  }
1599
1254
  };
1255
+
1256
+ // src/permissions/permissionEvents.ts
1257
+ var WIRE_PERMISSION_EVENTS = {
1258
+ /** The priming screen became visible. The denominator for every rate below. */
1259
+ screenShown: "wire_permission_screen_shown",
1260
+ /** The user tapped the primary, so the OS dialog is about to open. The rationale worked. */
1261
+ primerAccepted: "wire_permission_primer_accepted",
1262
+ /** The OS granted it. */
1263
+ granted: "wire_permission_granted",
1264
+ /** The OS refused it (a permanently blocked answer reports here too, with `status: "blocked"`). */
1265
+ denied: "wire_permission_denied",
1266
+ /** The user took the secondary. The one native prompt was NOT spent. */
1267
+ skipped: "wire_permission_skipped",
1268
+ /** A blocked user was redirected to the OS settings page. */
1269
+ settingsOpened: "wire_permission_settings_opened"
1270
+ };
1271
+ var permissionEventName = (stage) => {
1272
+ switch (stage) {
1273
+ case "shown":
1274
+ return WIRE_PERMISSION_EVENTS.screenShown;
1275
+ case "accepted":
1276
+ return WIRE_PERMISSION_EVENTS.primerAccepted;
1277
+ case "granted":
1278
+ return WIRE_PERMISSION_EVENTS.granted;
1279
+ case "denied":
1280
+ return WIRE_PERMISSION_EVENTS.denied;
1281
+ case "skipped":
1282
+ return WIRE_PERMISSION_EVENTS.skipped;
1283
+ case "settings":
1284
+ return WIRE_PERMISSION_EVENTS.settingsOpened;
1285
+ default: {
1286
+ const _exhaustive = stage;
1287
+ return _exhaustive;
1288
+ }
1289
+ }
1290
+ };
1291
+ var permissionEventProps = (permission, status) => status ? { permission, status } : { permission };
1292
+ var normalizePermissionStatus = (value) => value === "granted" || value === "denied" || value === "blocked" ? value : "denied";
1293
+
1294
+ // src/haptics/haptics.ts
1295
+ var play = async (tone) => {
1296
+ try {
1297
+ const mod = await import('expo-haptics');
1298
+ if (!mod) return false;
1299
+ if (tone === "selection") ;
1300
+ if (tone === "light") ;
1301
+ await mod.notificationAsync(mod.NotificationFeedbackType.Success);
1302
+ return true;
1303
+ } catch {
1304
+ return false;
1305
+ }
1306
+ };
1307
+ var playHaptic = (tone) => {
1308
+ void play(tone);
1309
+ };
1310
+
1311
+ // src/utils/warnInDev.ts
1312
+ var warnInDev = (message) => {
1313
+ if (typeof __DEV__ !== "undefined" && __DEV__ && typeof console !== "undefined" && console.warn) {
1314
+ console.warn(message);
1315
+ return true;
1316
+ }
1317
+ return false;
1318
+ };
1319
+ var easeWire4 = reactNative.Easing.bezier(...WIRE_BEZIER);
1320
+ var RISE_PX = 10;
1321
+ var PERMISSION_CARD_NAME = "PermissionCard";
1322
+ var REQUEST_WATCHDOG_MS = 9e4;
1323
+ var schema = zod.z.object({
1324
+ permission: zod.z.string().describe("Which OS permission this screen primes, e.g. 'notifications'"),
1325
+ title: zod.z.string().describe("Headline for the ask"),
1326
+ message: zod.z.string().describe("Why the app needs it, in the user's terms"),
1327
+ primaryLabel: zod.z.string().describe("Primary button, the only control that can open the OS dialog"),
1328
+ secondaryLabel: zod.z.string().describe("Secondary button, advances without spending the OS prompt"),
1329
+ blockedTitle: zod.z.string().optional().describe("Headline once the permission is permanently refused"),
1330
+ blockedMessage: zod.z.string().optional().describe("Rationale for the settings route"),
1331
+ settingsLabel: zod.z.string().optional().describe("Primary button label on the blocked route"),
1332
+ continueLabel: zod.z.string().optional().describe("Primary button label when there is nothing to ask"),
1333
+ illustration: zod.z.string().optional().describe("Name of an app-provided illustration (defaults to the permission name)")
1334
+ });
1335
+ var _PermissionCard = ({
1336
+ permission,
1337
+ title,
1338
+ message,
1339
+ primaryLabel,
1340
+ secondaryLabel,
1341
+ blockedTitle,
1342
+ blockedMessage,
1343
+ settingsLabel,
1344
+ continueLabel,
1345
+ illustration,
1346
+ request,
1347
+ getStatus,
1348
+ openSettings,
1349
+ onStage,
1350
+ onSettled
1351
+ }) => {
1352
+ const t = useOnboardingTheme();
1353
+ const reduced = useReducedMotion();
1354
+ const art = useIllustration(illustration != null ? illustration : permission);
1355
+ const [status, setStatus] = React19.useState(void 0);
1356
+ const [busy, setBusy] = React19.useState(false);
1357
+ const settledRef = React19.useRef(false);
1358
+ const acceptedRef = React19.useRef(false);
1359
+ const watchdogRef = React19.useRef(null);
1360
+ const clearWatchdog = React19.useCallback(() => {
1361
+ if (watchdogRef.current) {
1362
+ clearTimeout(watchdogRef.current);
1363
+ watchdogRef.current = null;
1364
+ }
1365
+ }, []);
1366
+ React19.useEffect(() => clearWatchdog, [clearWatchdog]);
1367
+ const onStageRef = React19.useRef(onStage);
1368
+ onStageRef.current = onStage;
1369
+ const onSettledRef = React19.useRef(onSettled);
1370
+ onSettledRef.current = onSettled;
1371
+ const getStatusRef = React19.useRef(getStatus);
1372
+ getStatusRef.current = getStatus;
1373
+ const shownRef = React19.useRef(false);
1374
+ React19.useEffect(() => {
1375
+ var _a;
1376
+ if (shownRef.current) return;
1377
+ shownRef.current = true;
1378
+ (_a = onStageRef.current) == null ? void 0 : _a.call(onStageRef, "shown");
1379
+ }, []);
1380
+ React19.useEffect(() => {
1381
+ const probe = getStatusRef.current;
1382
+ if (!probe) return;
1383
+ let cancelled = false;
1384
+ try {
1385
+ void Promise.resolve(probe()).then((value) => {
1386
+ if (!cancelled) setStatus(normalizePermissionStatus(value));
1387
+ }).catch(() => {
1388
+ });
1389
+ } catch {
1390
+ }
1391
+ return () => {
1392
+ cancelled = true;
1393
+ };
1394
+ }, []);
1395
+ const requestRef = React19.useRef(request);
1396
+ requestRef.current = request;
1397
+ React19.useEffect(() => {
1398
+ if (requestRef.current) return;
1399
+ warnInDev(
1400
+ `[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).`
1401
+ );
1402
+ }, [permission]);
1403
+ const settle = React19.useCallback((outcome) => {
1404
+ var _a;
1405
+ if (settledRef.current) return;
1406
+ settledRef.current = true;
1407
+ clearWatchdog();
1408
+ (_a = onSettledRef.current) == null ? void 0 : _a.call(onSettledRef, outcome);
1409
+ }, [clearWatchdog]);
1410
+ const primaryAction = status === "blocked" ? "settings" : status === "granted" || !request ? "continue" : "ask";
1411
+ const handlePrimary = React19.useCallback(() => {
1412
+ var _a, _b, _c, _d;
1413
+ if (settledRef.current || busy) return;
1414
+ if (primaryAction === "settings") {
1415
+ (_a = onStageRef.current) == null ? void 0 : _a.call(onStageRef, "settings", "blocked");
1416
+ try {
1417
+ void Promise.resolve(openSettings == null ? void 0 : openSettings()).catch(() => {
1418
+ });
1419
+ } catch {
1420
+ }
1421
+ settle("blocked");
1422
+ return;
1423
+ }
1424
+ if (primaryAction === "continue") {
1425
+ if (status === "granted") {
1426
+ (_b = onStageRef.current) == null ? void 0 : _b.call(onStageRef, "granted", "granted");
1427
+ settle("granted");
1428
+ } else {
1429
+ (_c = onStageRef.current) == null ? void 0 : _c.call(onStageRef, "skipped");
1430
+ settle("skipped");
1431
+ }
1432
+ return;
1433
+ }
1434
+ if (!acceptedRef.current) {
1435
+ acceptedRef.current = true;
1436
+ (_d = onStageRef.current) == null ? void 0 : _d.call(onStageRef, "accepted");
1437
+ }
1438
+ setBusy(true);
1439
+ const finish = (value) => {
1440
+ var _a2, _b2;
1441
+ if (settledRef.current) return;
1442
+ clearWatchdog();
1443
+ const resolved = normalizePermissionStatus(value);
1444
+ setBusy(false);
1445
+ setStatus(resolved);
1446
+ if (resolved === "granted") {
1447
+ playHaptic("success");
1448
+ (_a2 = onStageRef.current) == null ? void 0 : _a2.call(onStageRef, "granted", "granted");
1449
+ } else {
1450
+ (_b2 = onStageRef.current) == null ? void 0 : _b2.call(onStageRef, "denied", resolved);
1451
+ }
1452
+ settle(resolved);
1453
+ };
1454
+ clearWatchdog();
1455
+ watchdogRef.current = setTimeout(() => {
1456
+ watchdogRef.current = null;
1457
+ if (settledRef.current) return;
1458
+ setBusy(false);
1459
+ warnInDev(
1460
+ `[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.`
1461
+ );
1462
+ }, REQUEST_WATCHDOG_MS);
1463
+ try {
1464
+ void Promise.resolve(request == null ? void 0 : request()).then(finish, () => finish("denied"));
1465
+ } catch {
1466
+ finish("denied");
1467
+ }
1468
+ }, [busy, primaryAction, status, request, openSettings, settle, clearWatchdog, permission]);
1469
+ const handleSecondary = React19.useCallback(() => {
1470
+ var _a;
1471
+ if (settledRef.current || busy) return;
1472
+ (_a = onStageRef.current) == null ? void 0 : _a.call(onStageRef, "skipped");
1473
+ settle("skipped");
1474
+ }, [busy, settle]);
1475
+ const popT = React19.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
1476
+ const titleT = React19.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
1477
+ const bodyT = React19.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
1478
+ React19.useEffect(() => {
1479
+ if (reduced) {
1480
+ popT.setValue(1);
1481
+ titleT.setValue(1);
1482
+ bodyT.setValue(1);
1483
+ return;
1484
+ }
1485
+ const rise = (value, delay) => reactNative.Animated.timing(value, {
1486
+ toValue: 1,
1487
+ duration: scaledMs(INTERSTITIAL_HEAD_MS),
1488
+ delay,
1489
+ easing: easeWire4,
1490
+ useNativeDriver: true
1491
+ });
1492
+ const anims = [
1493
+ reactNative.Animated.spring(popT, {
1494
+ toValue: 1,
1495
+ friction: STATUS_POP_SPRING.friction,
1496
+ tension: STATUS_POP_SPRING.tension,
1497
+ useNativeDriver: true
1498
+ }),
1499
+ rise(titleT, scaledMs(INTERSTITIAL_HEAD_STAGGER_MS)),
1500
+ rise(bodyT, scaledMs(INTERSTITIAL_HEAD_STAGGER_MS * 2))
1501
+ ];
1502
+ anims.forEach((a) => a.start());
1503
+ return () => anims.forEach((a) => a.stop());
1504
+ }, [reduced, popT, titleT, bodyT]);
1505
+ const popScale = popT.interpolate({
1506
+ inputRange: [0, 1],
1507
+ outputRange: [STATUS_POP_SCALE_FROM, 1]
1508
+ });
1509
+ const riseStyle = (value) => ({
1510
+ opacity: value,
1511
+ transform: [
1512
+ { translateY: value.interpolate({ inputRange: [0, 1], outputRange: [RISE_PX, 0] }) }
1513
+ ]
1514
+ });
1515
+ const blocked = status === "blocked";
1516
+ const shownTitle = blocked ? blockedTitle != null ? blockedTitle : title : title;
1517
+ const shownMessage = blocked ? blockedMessage != null ? blockedMessage : message : message;
1518
+ const primaryTitle = primaryAction === "settings" ? settingsLabel != null ? settingsLabel : primaryLabel : primaryAction === "continue" ? continueLabel != null ? continueLabel : primaryLabel : primaryLabel;
1519
+ const showSecondary = status !== "granted";
1520
+ return /* @__PURE__ */ jsxRuntime.jsx(
1521
+ CardLayout,
1522
+ {
1523
+ align: "center",
1524
+ footer: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles10.footer, { gap: t.spacing.sm }], children: [
1525
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { title: primaryTitle, onPress: handlePrimary, variant: "primary", disabled: busy }),
1526
+ showSecondary ? /* @__PURE__ */ jsxRuntime.jsx(
1527
+ Button,
1528
+ {
1529
+ title: secondaryLabel,
1530
+ onPress: handleSecondary,
1531
+ variant: "outline",
1532
+ disabled: busy
1533
+ }
1534
+ ) : null
1535
+ ] }),
1536
+ children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles10.center, { gap: t.spacing.md }], children: [
1537
+ art ? /* @__PURE__ */ jsxRuntime.jsx(
1538
+ reactNative.Animated.View,
1539
+ {
1540
+ style: [styles10.art, { opacity: popT, transform: [{ scale: popScale }] }],
1541
+ children: art
1542
+ }
1543
+ ) : null,
1544
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Animated.View, { style: riseStyle(titleT), children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [headingStyle(t.fonts), { color: t.colors.text, textAlign: "center" }], children: shownTitle }) }),
1545
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Animated.View, { style: riseStyle(bodyT), children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [bodyStyle(t.fonts), { color: t.colors.textMuted, textAlign: "center" }], children: shownMessage }) })
1546
+ ] })
1547
+ }
1548
+ );
1549
+ };
1550
+ var PermissionCardView = React19__default.default.memo(_PermissionCard);
1551
+ var PermissionCard = {
1552
+ name: PERMISSION_CARD_NAME,
1553
+ 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.",
1554
+ component: PermissionCardView,
1555
+ propsSchema: schema
1556
+ };
1557
+ var styles10 = reactNative.StyleSheet.create({
1558
+ center: { width: "100%", alignItems: "center", justifyContent: "center" },
1559
+ art: { alignItems: "center", justifyContent: "center" },
1560
+ footer: { width: "100%", alignItems: "center" }
1561
+ });
1562
+
1563
+ // src/permissions/placement.ts
1564
+ var DEFAULT_PERMISSION_PLACEMENT = "beforeEnd";
1565
+ var normalizeAfterCard = (value) => Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0;
1566
+ var targetIndex = (placement) => {
1567
+ if (placement === "beforeEnd") return void 0;
1568
+ if (placement === "start") return 0;
1569
+ return normalizeAfterCard(placement.afterCard);
1570
+ };
1571
+ var isPermissionDue = (placement, position) => {
1572
+ if (position.cardIndex < 0) return false;
1573
+ const target = targetIndex(placement);
1574
+ if (target === void 0) return position.isTerminal;
1575
+ return position.cardIndex >= target || position.isTerminal;
1576
+ };
1577
+ var permissionScreenId = (screen, index) => {
1578
+ var _a;
1579
+ return (_a = screen.id) != null ? _a : `${screen.permission}:${index}`;
1580
+ };
1581
+ var selectDuePermissionScreen = (screens, settled, position) => {
1582
+ var _a;
1583
+ if (!screens || screens.length === 0) return void 0;
1584
+ for (let index = 0; index < screens.length; index++) {
1585
+ const screen = screens[index];
1586
+ if (!screen) continue;
1587
+ const id = permissionScreenId(screen, index);
1588
+ if (settled.includes(id)) continue;
1589
+ if (isPermissionDue((_a = screen.placement) != null ? _a : DEFAULT_PERMISSION_PLACEMENT, position)) {
1590
+ return { screen, id };
1591
+ }
1592
+ }
1593
+ return void 0;
1594
+ };
1595
+
1596
+ // src/permissions/permissionCopy.ts
1597
+ var NOTIFICATIONS_PERMISSION_COPY = {
1598
+ title: "Want a nudge at the right moment?",
1599
+ message: "Turn notifications on and we will remind you when it actually helps. No daily noise, and you can turn them off any time.",
1600
+ primaryLabel: "Enable notifications",
1601
+ secondaryLabel: "Maybe later",
1602
+ blockedTitle: "Notifications are switched off",
1603
+ blockedMessage: "Notifications are turned off for this app, so we cannot ask from here. You can switch them back on in Settings.",
1604
+ settingsLabel: "Open settings",
1605
+ continueLabel: "Continue"
1606
+ };
1607
+ var GENERIC_PERMISSION_COPY = {
1608
+ title: "One quick permission",
1609
+ message: "We need your permission for this part to work. You can change it any time.",
1610
+ primaryLabel: "Allow",
1611
+ secondaryLabel: "Maybe later",
1612
+ blockedTitle: "Permission is switched off",
1613
+ blockedMessage: "This permission is turned off for this app, so we cannot ask from here. You can switch it back on in Settings.",
1614
+ settingsLabel: "Open settings",
1615
+ continueLabel: "Continue"
1616
+ };
1617
+ var DEFAULT_PERMISSION_COPY = {
1618
+ notifications: NOTIFICATIONS_PERMISSION_COPY
1619
+ };
1620
+ var resolvePermissionCopy = (permission, overrides) => {
1621
+ var _a;
1622
+ const base = (_a = DEFAULT_PERMISSION_COPY[permission]) != null ? _a : GENERIC_PERMISSION_COPY;
1623
+ if (!overrides) return base;
1624
+ const merged = { ...base };
1625
+ for (const key of Object.keys(overrides)) {
1626
+ const value = overrides[key];
1627
+ if (typeof value === "string" && value.length > 0) merged[key] = value;
1628
+ }
1629
+ return merged;
1630
+ };
1600
1631
  var SKIP_ONE_SENTINEL = "__wireai_skip__";
1601
1632
  var ANSWER_CALLBACKS = ["onSubmit", "onSelect", "onConfirm", "onPress"];
1602
1633
  var DEFAULT_COPY = {
@@ -1625,72 +1656,82 @@ var OnboardingFlow = ({
1625
1656
  reportTarget,
1626
1657
  sessionId,
1627
1658
  resumed = false,
1628
- clientContext
1659
+ clientContext,
1660
+ permissionScreens,
1661
+ settledPermissions,
1662
+ permissionsPending = false,
1663
+ onPermissionSettled
1629
1664
  }) => {
1630
- var _a2, _b, _c, _d, _e, _f;
1665
+ var _a, _b, _c, _d, _e, _f;
1631
1666
  const { messages, error, isLoading, sendMessage, reset } = wireaiRn.useWireAIThread();
1632
1667
  const makeActions = wireaiRn.useWireAIAction(sendMessage);
1633
- const c = React18.useMemo(() => ({ ...DEFAULT_COPY, ...copy }), [copy]);
1634
- const onEventRef = React18.useRef(onEvent);
1668
+ const c = React19.useMemo(() => ({ ...DEFAULT_COPY, ...copy }), [copy]);
1669
+ const onEventRef = React19.useRef(onEvent);
1635
1670
  onEventRef.current = onEvent;
1636
- const onErrorRef = React18.useRef(onError);
1671
+ const onErrorRef = React19.useRef(onError);
1637
1672
  onErrorRef.current = onError;
1638
- const onCompleteRef = React18.useRef(onComplete);
1673
+ const onCompleteRef = React19.useRef(onComplete);
1639
1674
  onCompleteRef.current = onComplete;
1640
- const validatorsRef = React18.useRef(validators);
1675
+ const validatorsRef = React19.useRef(validators);
1641
1676
  validatorsRef.current = validators;
1642
- const reportTargetRef = React18.useRef(reportTarget);
1677
+ const reportTargetRef = React19.useRef(reportTarget);
1643
1678
  reportTargetRef.current = reportTarget;
1644
- const sessionIdRef = React18.useRef(sessionId);
1679
+ const sessionIdRef = React19.useRef(sessionId);
1645
1680
  sessionIdRef.current = sessionId;
1646
- const clientContextRef = React18.useRef(clientContext);
1681
+ const clientContextRef = React19.useRef(clientContext);
1647
1682
  clientContextRef.current = clientContext;
1648
- const lastScreenIndexRef = React18.useRef(-1);
1649
- const previewTimer = React18.useRef(null);
1650
- const started = React18.useRef(false);
1651
- const finished = React18.useRef(false);
1652
- const errored = React18.useRef(false);
1653
- const attempts = React18.useRef(0);
1654
- const degradedRef = React18.useRef(false);
1655
- const [degraded, setDegraded] = React18.useState(false);
1656
- const [timedOut, setTimedOut] = React18.useState(false);
1657
- const [validationError, setValidationError] = React18.useState();
1658
- const [validating, setValidating] = React18.useState(false);
1659
- const lastCard = React18.useMemo(
1683
+ const lastScreenIndexRef = React19.useRef(-1);
1684
+ const previewTimer = React19.useRef(null);
1685
+ const [runId, setRunId] = React19.useState(0);
1686
+ const startedRunRef = React19.useRef(-1);
1687
+ const finished = React19.useRef(false);
1688
+ const errored = React19.useRef(false);
1689
+ const attempts = React19.useRef(0);
1690
+ const degradedRef = React19.useRef(false);
1691
+ const [degraded, setDegraded] = React19.useState(false);
1692
+ const [timedOut, setTimedOut] = React19.useState(false);
1693
+ const [validationError, setValidationError] = React19.useState();
1694
+ const [validating, setValidating] = React19.useState(false);
1695
+ const [locallySettledPermissions, setLocallySettledPermissions] = React19.useState([]);
1696
+ const onPermissionSettledRef = React19.useRef(onPermissionSettled);
1697
+ onPermissionSettledRef.current = onPermissionSettled;
1698
+ const lastCard = React19.useMemo(
1660
1699
  () => [...messages].reverse().find((m) => {
1661
- var _a3;
1662
- return m.role === "assistant" && ((_a3 = m.response) == null ? void 0 : _a3.action) === "render";
1700
+ var _a2;
1701
+ return m.role === "assistant" && ((_a2 = m.response) == null ? void 0 : _a2.action) === "render";
1663
1702
  }),
1664
1703
  [messages]
1665
1704
  );
1666
- const renderedCount = React18.useMemo(
1705
+ const renderedCount = React19.useMemo(
1667
1706
  () => messages.filter((m) => {
1668
- var _a3;
1669
- return m.role === "assistant" && ((_a3 = m.response) == null ? void 0 : _a3.action) === "render";
1707
+ var _a2;
1708
+ return m.role === "assistant" && ((_a2 = m.response) == null ? void 0 : _a2.action) === "render";
1670
1709
  }).length,
1671
1710
  [messages]
1672
1711
  );
1673
- const progress = React18.useMemo(() => readProgress(lastCard == null ? void 0 : lastCard.response), [lastCard]);
1674
- React18.useEffect(() => {
1675
- var _a3;
1676
- if (started.current) return;
1677
- started.current = true;
1678
- sendMessage(resumed ? SKIP_ONE_SENTINEL : startMessage);
1679
- (_a3 = onEventRef.current) == null ? void 0 : _a3.call(
1712
+ const progress = React19.useMemo(() => readProgress(lastCard == null ? void 0 : lastCard.response), [lastCard]);
1713
+ React19.useEffect(() => {
1714
+ var _a2;
1715
+ if (startedRunRef.current === runId) return;
1716
+ const isFirstRun = startedRunRef.current < 0;
1717
+ startedRunRef.current = runId;
1718
+ sendMessage(isFirstRun && !resumed ? startMessage : SKIP_ONE_SENTINEL);
1719
+ if (!isFirstRun) return;
1720
+ (_a2 = onEventRef.current) == null ? void 0 : _a2.call(
1680
1721
  onEventRef,
1681
1722
  resumed ? { type: "resumed", contextId: sessionId } : { type: "started", contextId: sessionId }
1682
1723
  );
1683
- }, [sendMessage, startMessage, resumed, sessionId]);
1684
- React18.useEffect(() => {
1724
+ }, [sendMessage, startMessage, resumed, sessionId, runId]);
1725
+ React19.useEffect(() => {
1685
1726
  if (lastCard) return;
1686
1727
  const timer = setTimeout(() => setTimedOut(true), startTimeoutMs);
1687
1728
  return () => clearTimeout(timer);
1688
- }, [lastCard, startTimeoutMs]);
1689
- React18.useEffect(() => {
1690
- var _a3, _b2, _c2, _d2;
1729
+ }, [lastCard, startTimeoutMs, runId]);
1730
+ React19.useEffect(() => {
1731
+ var _a2, _b2, _c2, _d2;
1691
1732
  setValidationError(void 0);
1692
1733
  if (!(lastCard == null ? void 0 : lastCard.id)) return;
1693
- const step2 = (_a3 = progress.step) != null ? _a3 : renderedCount;
1734
+ const step2 = (_a2 = progress.step) != null ? _a2 : renderedCount;
1694
1735
  lastScreenIndexRef.current = Math.max(0, step2 - 1);
1695
1736
  (_c2 = onEventRef.current) == null ? void 0 : _c2.call(onEventRef, {
1696
1737
  type: "turn",
@@ -1704,33 +1745,33 @@ var OnboardingFlow = ({
1704
1745
  });
1705
1746
  }
1706
1747
  }, [lastCard == null ? void 0 : lastCard.id]);
1707
- React18.useEffect(() => {
1748
+ React19.useEffect(() => {
1708
1749
  return () => {
1709
- var _a3, _b2, _c2;
1750
+ var _a2, _b2, _c2;
1710
1751
  if (finished.current || degradedRef.current) return;
1711
1752
  reportClientEvent(reportTargetRef.current, {
1712
1753
  event_type: "dropped",
1713
1754
  session_id: sessionIdRef.current,
1714
1755
  screen_index: lastScreenIndexRef.current >= 0 ? lastScreenIndexRef.current : void 0,
1715
- device: (_a3 = clientContextRef.current) == null ? void 0 : _a3.device,
1756
+ device: (_a2 = clientContextRef.current) == null ? void 0 : _a2.device,
1716
1757
  user_context: (_b2 = clientContextRef.current) == null ? void 0 : _b2.userContext,
1717
1758
  user_id: (_c2 = clientContextRef.current) == null ? void 0 : _c2.userId
1718
1759
  });
1719
1760
  };
1720
1761
  }, []);
1721
- const restartThread = React18.useCallback(() => {
1762
+ const restartThread = React19.useCallback(() => {
1722
1763
  setTimedOut(false);
1723
- started.current = false;
1724
1764
  errored.current = false;
1765
+ setRunId((n) => n + 1);
1725
1766
  reset();
1726
1767
  }, [reset]);
1727
- React18.useEffect(() => {
1728
- var _a3, _b2, _c2, _d2, _e2, _f2, _g;
1768
+ React19.useEffect(() => {
1769
+ var _a2, _b2, _c2, _d2, _e2, _f2, _g;
1729
1770
  if (errored.current) return;
1730
1771
  if (!error && !timedOut) return;
1731
1772
  errored.current = true;
1732
1773
  const reason = error ? "backend" : "timeout";
1733
- (_a3 = onEventRef.current) == null ? void 0 : _a3.call(onEventRef, { type: "error", reason });
1774
+ (_a2 = onEventRef.current) == null ? void 0 : _a2.call(onEventRef, { type: "error", reason });
1734
1775
  attempts.current += 1;
1735
1776
  if (attempts.current <= maxRetries) {
1736
1777
  (_b2 = onEventRef.current) == null ? void 0 : _b2.call(onEventRef, { type: "retry", reason, attempt: attempts.current });
@@ -1751,22 +1792,52 @@ var OnboardingFlow = ({
1751
1792
  (_g = onErrorRef.current) == null ? void 0 : _g.call(onErrorRef, error != null ? error : new Error("Onboarding timed out"));
1752
1793
  }
1753
1794
  }, [error, timedOut, maxRetries]);
1754
- const handleFinish = React18.useCallback(() => {
1795
+ const handleFinish = React19.useCallback(() => {
1755
1796
  if (finished.current) return;
1756
1797
  finished.current = true;
1757
1798
  onCompleteRef.current({ answers: deriveAnswers(messages), raw: messages });
1758
1799
  }, [messages]);
1759
- const handleRetry = React18.useCallback(() => {
1800
+ const handleRetry = React19.useCallback(() => {
1760
1801
  attempts.current = 0;
1761
1802
  degradedRef.current = false;
1762
1803
  setDegraded(false);
1763
1804
  restartThread();
1764
1805
  }, [restartThread]);
1765
- const skipQuestion = React18.useCallback(() => {
1806
+ const skipQuestion = React19.useCallback(() => {
1766
1807
  if (isLoading) return;
1767
1808
  sendMessage(SKIP_ONE_SENTINEL);
1768
1809
  }, [isLoading, sendMessage]);
1769
- const previewOnSelect = React18.useCallback((value) => {
1810
+ const emitPermissionStage = React19.useCallback(
1811
+ (permission, stage, status) => {
1812
+ var _a2, _b2, _c2, _d2;
1813
+ (_a2 = onEventRef.current) == null ? void 0 : _a2.call(onEventRef, { type: "permission", permission, stage, status });
1814
+ reportClientEvent(reportTargetRef.current, {
1815
+ event_type: "app_event",
1816
+ session_id: sessionIdRef.current,
1817
+ question_key: permissionEventName(stage),
1818
+ component: PERMISSION_CARD_NAME,
1819
+ screen_index: lastScreenIndexRef.current >= 0 ? lastScreenIndexRef.current : void 0,
1820
+ meta: JSON.stringify(permissionEventProps(permission, status)),
1821
+ device: (_b2 = clientContextRef.current) == null ? void 0 : _b2.device,
1822
+ user_context: (_c2 = clientContextRef.current) == null ? void 0 : _c2.userContext,
1823
+ user_id: (_d2 = clientContextRef.current) == null ? void 0 : _d2.userId
1824
+ });
1825
+ },
1826
+ []
1827
+ );
1828
+ const settlePermission = React19.useCallback(
1829
+ (id, screen, outcome) => {
1830
+ var _a2, _b2;
1831
+ setLocallySettledPermissions((prev) => prev.includes(id) ? prev : [...prev, id]);
1832
+ (_a2 = onPermissionSettledRef.current) == null ? void 0 : _a2.call(onPermissionSettledRef, id, outcome);
1833
+ try {
1834
+ (_b2 = screen.onResult) == null ? void 0 : _b2.call(screen, screen.permission, outcome);
1835
+ } catch {
1836
+ }
1837
+ },
1838
+ []
1839
+ );
1840
+ const previewOnSelect = React19.useCallback((value) => {
1770
1841
  const target = reportTargetRef.current;
1771
1842
  if (!(target == null ? void 0 : target.serverUrl)) return;
1772
1843
  if (Array.isArray(value) ? value.length === 0 : !value) return;
@@ -1778,31 +1849,31 @@ var OnboardingFlow = ({
1778
1849
  });
1779
1850
  }, 300);
1780
1851
  }, []);
1781
- React18.useEffect(() => () => {
1852
+ React19.useEffect(() => () => {
1782
1853
  if (previewTimer.current) clearTimeout(previewTimer.current);
1783
1854
  }, []);
1784
- const wrappedActions = React18.useCallback(
1855
+ const wrappedActions = React19.useCallback(
1785
1856
  (messageId) => {
1786
- var _a3;
1857
+ var _a2;
1787
1858
  const base = makeActions(messageId);
1788
1859
  const validators2 = validatorsRef.current;
1789
- const validator = (_a3 = progress.slot_id ? validators2 == null ? void 0 : validators2[progress.slot_id] : void 0) != null ? _a3 : progress.key ? validators2 == null ? void 0 : validators2[progress.key] : void 0;
1860
+ const validator = (_a2 = progress.slot_id ? validators2 == null ? void 0 : validators2[progress.slot_id] : void 0) != null ? _a2 : progress.key ? validators2 == null ? void 0 : validators2[progress.key] : void 0;
1790
1861
  if (!validator) return base;
1791
1862
  const out = { ...base };
1792
- for (const name2 of ANSWER_CALLBACKS) {
1793
- const original = base[name2];
1863
+ for (const name of ANSWER_CALLBACKS) {
1864
+ const original = base[name];
1794
1865
  if (!original) continue;
1795
- out[name2] = (...args) => {
1866
+ out[name] = (...args) => {
1796
1867
  const value = args[0];
1797
1868
  if (typeof value !== "string") return original(...args);
1798
1869
  setValidating(true);
1799
1870
  validator(value).then((res) => {
1800
- var _a4;
1871
+ var _a3;
1801
1872
  if (res.ok) {
1802
1873
  setValidationError(void 0);
1803
1874
  original(...args);
1804
1875
  } else {
1805
- setValidationError((_a4 = res.error) != null ? _a4 : "That value isn't available.");
1876
+ setValidationError((_a3 = res.error) != null ? _a3 : "That value isn't available.");
1806
1877
  }
1807
1878
  }).catch(() => {
1808
1879
  original(...args);
@@ -1833,9 +1904,39 @@ var OnboardingFlow = ({
1833
1904
  if (!lastCard || !response) {
1834
1905
  return /* @__PURE__ */ jsxRuntime.jsx(LoadingScreen, { title: c.startingTitle, hint: c.startingHint });
1835
1906
  }
1836
- const step = (_a2 = progress.step) != null ? _a2 : renderedCount;
1907
+ const step = (_a = progress.step) != null ? _a : renderedCount;
1837
1908
  const total = (_b = progress.total) != null ? _b : approxScreens;
1838
1909
  const isTerminal = response.action === "render" && response.component === "StatusCard";
1910
+ const duePermission = permissionsPending ? void 0 : selectDuePermissionScreen(
1911
+ permissionScreens,
1912
+ settledPermissions ? [...settledPermissions, ...locallySettledPermissions] : locallySettledPermissions,
1913
+ { cardIndex: renderedCount - 1, isTerminal }
1914
+ );
1915
+ if (duePermission) {
1916
+ const { screen, id } = duePermission;
1917
+ const copy2 = resolvePermissionCopy(screen.permission, screen.copy);
1918
+ return /* @__PURE__ */ jsxRuntime.jsx(OnboardingScaffold, { step, approxScreens: total, children: /* @__PURE__ */ jsxRuntime.jsx(CardHandoff, { transitionKey: `permission:${id}`, variant: "spring", children: /* @__PURE__ */ jsxRuntime.jsx(
1919
+ PermissionCardView,
1920
+ {
1921
+ permission: screen.permission,
1922
+ title: copy2.title,
1923
+ message: copy2.message,
1924
+ primaryLabel: copy2.primaryLabel,
1925
+ secondaryLabel: copy2.secondaryLabel,
1926
+ blockedTitle: copy2.blockedTitle,
1927
+ blockedMessage: copy2.blockedMessage,
1928
+ settingsLabel: copy2.settingsLabel,
1929
+ continueLabel: copy2.continueLabel,
1930
+ illustration: screen.illustration,
1931
+ request: screen.request,
1932
+ getStatus: screen.getStatus,
1933
+ openSettings: screen.openSettings,
1934
+ onStage: (stage, status) => emitPermissionStage(screen.permission, stage, status),
1935
+ onSettled: (outcome) => settlePermission(id, screen, outcome)
1936
+ },
1937
+ id
1938
+ ) }) });
1939
+ }
1839
1940
  if (isTerminal) {
1840
1941
  const props = (_c = response.props) != null ? _c : {};
1841
1942
  return /* @__PURE__ */ jsxRuntime.jsx(OnboardingScaffold, { step, complete: true, approxScreens: total, children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -1894,8 +1995,8 @@ var OnboardingFlow = ({
1894
1995
  }
1895
1996
  );
1896
1997
  };
1897
- var easeWire4 = reactNative.Easing.bezier(...WIRE_BEZIER);
1898
- var schema = zod.z.object({
1998
+ var easeWire5 = reactNative.Easing.bezier(...WIRE_BEZIER);
1999
+ var schema2 = zod.z.object({
1899
2000
  title: zod.z.string().describe("Question or section header, e.g. 'Which season?'"),
1900
2001
  chips: zod.z.array(zod.z.string()).min(1).describe("List of option labels shown as chips, e.g. ['Spring','Summer','Autumn','Winter']"),
1901
2002
  multiSelect: zod.z.boolean().optional().describe("Allow multiple chips selected at once, default true"),
@@ -1905,9 +2006,9 @@ var schema = zod.z.object({
1905
2006
  var _ChipItem = ({ chip, isSelected, multiSelect, onToggle }) => {
1906
2007
  const t = useOnboardingTheme();
1907
2008
  const reduced = useReducedMotion();
1908
- const handlePress = React18.useCallback(() => onToggle(chip), [chip, onToggle]);
1909
- const pressScale = React18.useRef(new reactNative.Animated.Value(1)).current;
1910
- const pressTo = React18.useCallback(
2009
+ const handlePress = React19.useCallback(() => onToggle(chip), [chip, onToggle]);
2010
+ const pressScale = React19.useRef(new reactNative.Animated.Value(1)).current;
2011
+ const pressTo = React19.useCallback(
1911
2012
  (to) => {
1912
2013
  if (reduced) return;
1913
2014
  reactNative.Animated.spring(pressScale, {
@@ -1919,21 +2020,21 @@ var _ChipItem = ({ chip, isSelected, multiSelect, onToggle }) => {
1919
2020
  },
1920
2021
  [pressScale, reduced]
1921
2022
  );
1922
- const handlePressIn = React18.useCallback(() => pressTo(CHIP_PRESS_SCALE), [pressTo]);
1923
- const handlePressOut = React18.useCallback(() => pressTo(1), [pressTo]);
1924
- const fillT = React18.useRef(new reactNative.Animated.Value(isSelected ? 1 : 0)).current;
1925
- React18.useEffect(() => {
2023
+ const handlePressIn = React19.useCallback(() => pressTo(CHIP_PRESS_SCALE), [pressTo]);
2024
+ const handlePressOut = React19.useCallback(() => pressTo(1), [pressTo]);
2025
+ const fillT = React19.useRef(new reactNative.Animated.Value(isSelected ? 1 : 0)).current;
2026
+ React19.useEffect(() => {
1926
2027
  const anim = reactNative.Animated.timing(fillT, {
1927
2028
  toValue: isSelected ? 1 : 0,
1928
2029
  duration: scaledMs(stateChangeDuration(reduced, CHIP_FILL_MS)),
1929
- easing: easeWire4,
2030
+ easing: easeWire5,
1930
2031
  useNativeDriver: true
1931
2032
  });
1932
2033
  anim.start();
1933
2034
  return () => anim.stop();
1934
2035
  }, [isSelected, reduced, fillT]);
1935
- const checkT = React18.useRef(new reactNative.Animated.Value(isSelected ? 1 : 0)).current;
1936
- React18.useEffect(() => {
2036
+ const checkT = React19.useRef(new reactNative.Animated.Value(isSelected ? 1 : 0)).current;
2037
+ React19.useEffect(() => {
1937
2038
  if (!isSelected) {
1938
2039
  checkT.setValue(0);
1939
2040
  return;
@@ -1966,7 +2067,7 @@ var _ChipItem = ({ chip, isSelected, multiSelect, onToggle }) => {
1966
2067
  reactNative.Animated.View,
1967
2068
  {
1968
2069
  style: [
1969
- styles10.chip,
2070
+ styles11.chip,
1970
2071
  {
1971
2072
  borderRadius: t.radius.full,
1972
2073
  paddingHorizontal: t.spacing.md,
@@ -1983,7 +2084,7 @@ var _ChipItem = ({ chip, isSelected, multiSelect, onToggle }) => {
1983
2084
  pointerEvents: "none",
1984
2085
  style: [
1985
2086
  reactNative.StyleSheet.absoluteFill,
1986
- styles10.fillOverlay,
2087
+ styles11.fillOverlay,
1987
2088
  {
1988
2089
  borderRadius: t.radius.full,
1989
2090
  borderColor: t.colors.primary,
@@ -1997,7 +2098,7 @@ var _ChipItem = ({ chip, isSelected, multiSelect, onToggle }) => {
1997
2098
  reactNative.Animated.Text,
1998
2099
  {
1999
2100
  style: [
2000
- styles10.check,
2101
+ styles11.check,
2001
2102
  { color: t.colors.onPrimary, opacity: checkT, transform: [{ scale: checkScale }] }
2002
2103
  ],
2003
2104
  children: "\u2713"
@@ -2008,7 +2109,7 @@ var _ChipItem = ({ chip, isSelected, multiSelect, onToggle }) => {
2008
2109
  {
2009
2110
  style: [
2010
2111
  captionStyle(t.fonts),
2011
- styles10.chipLabel,
2112
+ styles11.chipLabel,
2012
2113
  { color: isSelected ? t.colors.onPrimary : t.colors.textMuted }
2013
2114
  ],
2014
2115
  children: chip
@@ -2020,7 +2121,7 @@ var _ChipItem = ({ chip, isSelected, multiSelect, onToggle }) => {
2020
2121
  }
2021
2122
  );
2022
2123
  };
2023
- var ChipItem = React18__default.default.memo(_ChipItem);
2124
+ var ChipItem = React19__default.default.memo(_ChipItem);
2024
2125
  var _ChipSelectCard = ({
2025
2126
  title,
2026
2127
  chips,
@@ -2031,12 +2132,12 @@ var _ChipSelectCard = ({
2031
2132
  onSelectionChange
2032
2133
  }) => {
2033
2134
  const t = useOnboardingTheme();
2034
- const [selected, setSelected] = React18.useState([]);
2035
- const [submitted, setSubmitted] = React18.useState(false);
2036
- React18.useEffect(() => {
2135
+ const [selected, setSelected] = React19.useState([]);
2136
+ const [submitted, setSubmitted] = React19.useState(false);
2137
+ React19.useEffect(() => {
2037
2138
  if (!submitted && selected.length) onSelectionChange == null ? void 0 : onSelectionChange(multiSelect ? selected : selected[0]);
2038
2139
  }, [selected, submitted, multiSelect, onSelectionChange]);
2039
- const toggle = React18.useCallback(
2140
+ const toggle = React19.useCallback(
2040
2141
  (chip) => {
2041
2142
  if (submitted) return;
2042
2143
  if (multiSelect) {
@@ -2051,7 +2152,7 @@ var _ChipSelectCard = ({
2051
2152
  },
2052
2153
  [submitted, multiSelect, maxSelections]
2053
2154
  );
2054
- const handleSubmit = React18.useCallback(() => {
2155
+ const handleSubmit = React19.useCallback(() => {
2055
2156
  if (!selected.length || !onSelect || submitted) return;
2056
2157
  setSubmitted(true);
2057
2158
  onSelect(multiSelect ? selected : selected[0]);
@@ -2070,7 +2171,7 @@ var _ChipSelectCard = ({
2070
2171
  disabled: selected.length === 0 || submitted
2071
2172
  }
2072
2173
  ),
2073
- children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles10.chipsRow, { gap: t.spacing.sm + t.spacing.xs }], children: chips.map((chip) => /* @__PURE__ */ jsxRuntime.jsx(
2174
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles11.chipsRow, { gap: t.spacing.sm + t.spacing.xs }], children: chips.map((chip) => /* @__PURE__ */ jsxRuntime.jsx(
2074
2175
  ChipItem,
2075
2176
  {
2076
2177
  chip,
@@ -2086,18 +2187,18 @@ var _ChipSelectCard = ({
2086
2187
  var ChipSelectCard = {
2087
2188
  name: "ChipSelectCard",
2088
2189
  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.",
2089
- component: React18__default.default.memo(_ChipSelectCard),
2090
- propsSchema: schema,
2190
+ component: React19__default.default.memo(_ChipSelectCard),
2191
+ propsSchema: schema2,
2091
2192
  defaultProps: { multiSelect: true, submitLabel: "Continue" }
2092
2193
  };
2093
- var styles10 = reactNative.StyleSheet.create({
2194
+ var styles11 = reactNative.StyleSheet.create({
2094
2195
  chipsRow: { flexDirection: "row", flexWrap: "wrap" },
2095
2196
  chip: { borderWidth: 1.5, flexDirection: "row", alignItems: "center", gap: 6, overflow: "hidden" },
2096
2197
  fillOverlay: { borderWidth: 1.5 },
2097
2198
  chipLabel: { fontWeight: "600" },
2098
2199
  check: { fontSize: 13, fontWeight: "800", lineHeight: 15 }
2099
2200
  });
2100
- var schema2 = zod.z.object({
2201
+ var schema3 = zod.z.object({
2101
2202
  label: zod.z.string().describe("Input field label / the question being asked"),
2102
2203
  hint: zod.z.string().optional().describe(
2103
2204
  "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."
@@ -2122,14 +2223,14 @@ var _TextInputCard = ({
2122
2223
  lines = 6
2123
2224
  }) => {
2124
2225
  const t = useOnboardingTheme();
2125
- const styles17 = React18.useMemo(() => makeStyles3(t), [t]);
2226
+ const styles18 = React19.useMemo(() => makeStyles3(t), [t]);
2126
2227
  const { height: screenHeight } = reactNative.useWindowDimensions();
2127
- const [value, setValue] = React18.useState("");
2128
- const [submitted, setSubmitted] = React18.useState(false);
2228
+ const [value, setValue] = React19.useState("");
2229
+ const [submitted, setSubmitted] = React19.useState(false);
2129
2230
  const locked = submitted && !validationError;
2130
2231
  const APPROX_ROW_HEIGHT = 26;
2131
2232
  const minHeight = multiline ? Math.min(Math.max(lines, 4) * APPROX_ROW_HEIGHT + t.spacing.md * 2, screenHeight * 0.4) : 52;
2132
- const handleSubmit = React18.useCallback(() => {
2233
+ const handleSubmit = React19.useCallback(() => {
2133
2234
  if (value.trim() && onSubmit && !locked) {
2134
2235
  setSubmitted(true);
2135
2236
  onSubmit(value.trim());
@@ -2149,7 +2250,7 @@ var _TextInputCard = ({
2149
2250
  disabled: !value.trim() || locked
2150
2251
  }
2151
2252
  ),
2152
- children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles17.row, children: [
2253
+ children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles18.row, children: [
2153
2254
  /* @__PURE__ */ jsxRuntime.jsx(
2154
2255
  reactNative.TextInput,
2155
2256
  {
@@ -2163,7 +2264,7 @@ var _TextInputCard = ({
2163
2264
  textAlignVertical: multiline ? "top" : "center",
2164
2265
  style: [
2165
2266
  bodyStyle(t.fonts),
2166
- styles17.input,
2267
+ styles18.input,
2167
2268
  {
2168
2269
  color: t.colors.text,
2169
2270
  backgroundColor: t.colors.surface,
@@ -2183,8 +2284,8 @@ var _TextInputCard = ({
2183
2284
  var TextInputCard = {
2184
2285
  name: "TextInputCard",
2185
2286
  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.",
2186
- component: React18__default.default.memo(_TextInputCard),
2187
- propsSchema: schema2,
2287
+ component: React19__default.default.memo(_TextInputCard),
2288
+ propsSchema: schema3,
2188
2289
  defaultProps: { submitLabel: "Submit", multiline: true, lines: 6 }
2189
2290
  };
2190
2291
  function makeStyles3(t) {
@@ -2199,7 +2300,7 @@ var runtimeRequire = (moduleName) => {
2199
2300
  if (moduleName !== "@expo/vector-icons") return void 0;
2200
2301
  if (typeof __require !== "function") return void 0;
2201
2302
  try {
2202
- return __require("@expo/vector-icons");
2303
+ return require("@expo/vector-icons");
2203
2304
  } catch {
2204
2305
  return void 0;
2205
2306
  }
@@ -2328,32 +2429,32 @@ var WIRE_ICON_GLYPHS = {
2328
2429
  other: { family: "MaterialCommunityIcons", name: "dots-horizontal-circle" }
2329
2430
  };
2330
2431
  var WIRE_ICON_NAMES = Object.keys(WIRE_ICON_GLYPHS).sort();
2331
- var lookupIconGlyph = (name2) => {
2332
- if (!name2) return void 0;
2333
- if (!Object.prototype.hasOwnProperty.call(WIRE_ICON_GLYPHS, name2)) return void 0;
2334
- return WIRE_ICON_GLYPHS[name2];
2432
+ var lookupIconGlyph = (name) => {
2433
+ if (!name) return void 0;
2434
+ if (!Object.prototype.hasOwnProperty.call(WIRE_ICON_GLYPHS, name)) return void 0;
2435
+ return WIRE_ICON_GLYPHS[name];
2335
2436
  };
2336
- var _WireIcon = ({ name: name2, size = 20, color, requireModule }) => {
2337
- const hostIcon = useHostIcon(name2);
2437
+ var _WireIcon = ({ name, size = 20, color, requireModule }) => {
2438
+ const hostIcon = useHostIcon(name);
2338
2439
  if (hostIcon) {
2339
2440
  return /* @__PURE__ */ jsxRuntime.jsx(
2340
2441
  reactNative.View,
2341
2442
  {
2342
- style: styles11.decorative,
2443
+ style: styles12.decorative,
2343
2444
  accessible: false,
2344
2445
  importantForAccessibility: "no-hide-descendants",
2345
2446
  children: hostIcon
2346
2447
  }
2347
2448
  );
2348
2449
  }
2349
- const glyph = lookupIconGlyph(name2);
2450
+ const glyph = lookupIconGlyph(name);
2350
2451
  if (!glyph) return null;
2351
2452
  const Family = resolveIconFamily(glyph.family, requireModule);
2352
2453
  if (!Family) return null;
2353
2454
  return /* @__PURE__ */ jsxRuntime.jsx(
2354
2455
  reactNative.View,
2355
2456
  {
2356
- style: styles11.decorative,
2457
+ style: styles12.decorative,
2357
2458
  accessible: false,
2358
2459
  importantForAccessibility: "no-hide-descendants",
2359
2460
  children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -2368,9 +2469,9 @@ var _WireIcon = ({ name: name2, size = 20, color, requireModule }) => {
2368
2469
  }
2369
2470
  );
2370
2471
  };
2371
- var WireIcon = React18__default.default.memo(_WireIcon);
2472
+ var WireIcon = React19__default.default.memo(_WireIcon);
2372
2473
  WireIcon.displayName = "WireIcon";
2373
- var styles11 = reactNative.StyleSheet.create({
2474
+ var styles12 = reactNative.StyleSheet.create({
2374
2475
  // No size/margin of its own: the glyph decides its footprint, so an absent icon and a
2375
2476
  // present one differ by exactly the glyph — never by a wrapper.
2376
2477
  decorative: {}
@@ -2384,8 +2485,8 @@ var optionObjectSchema = zod.z.object({
2384
2485
  var coerceOptions = (arr) => Array.isArray(arr) ? arr.map((item) => typeof item === "string" ? { value: item, label: item } : item) : arr;
2385
2486
  var optionsField = (description) => zod.z.preprocess(coerceOptions, zod.z.array(optionObjectSchema).min(1)).describe(description);
2386
2487
  var normalizeOptions = (options) => options.map((opt) => typeof opt === "string" ? { value: opt, label: opt } : opt);
2387
- var easeWire5 = reactNative.Easing.bezier(...WIRE_BEZIER);
2388
- var schema3 = zod.z.object({
2488
+ var easeWire6 = reactNative.Easing.bezier(...WIRE_BEZIER);
2489
+ var schema4 = zod.z.object({
2389
2490
  title: zod.z.string().describe("Question or prompt for the user"),
2390
2491
  options: optionsField("List of choices"),
2391
2492
  submitLabel: zod.z.string().optional().describe("Submit button label, default: Continue"),
@@ -2394,9 +2495,9 @@ var schema3 = zod.z.object({
2394
2495
  var _OptionRow = ({ opt, isSelected, multiSelect, onToggle }) => {
2395
2496
  const t = useOnboardingTheme();
2396
2497
  const reduced = useReducedMotion();
2397
- const handlePress = React18.useCallback(() => onToggle(opt.value), [opt.value, onToggle]);
2398
- const pressScale = React18.useRef(new reactNative.Animated.Value(1)).current;
2399
- const pressTo = React18.useCallback(
2498
+ const handlePress = React19.useCallback(() => onToggle(opt.value), [opt.value, onToggle]);
2499
+ const pressScale = React19.useRef(new reactNative.Animated.Value(1)).current;
2500
+ const pressTo = React19.useCallback(
2400
2501
  (to) => {
2401
2502
  if (reduced) return;
2402
2503
  reactNative.Animated.spring(pressScale, {
@@ -2408,21 +2509,21 @@ var _OptionRow = ({ opt, isSelected, multiSelect, onToggle }) => {
2408
2509
  },
2409
2510
  [pressScale, reduced]
2410
2511
  );
2411
- const handlePressIn = React18.useCallback(() => pressTo(ROW_PRESS_SCALE), [pressTo]);
2412
- const handlePressOut = React18.useCallback(() => pressTo(1), [pressTo]);
2413
- const selectT = React18.useRef(new reactNative.Animated.Value(isSelected ? 1 : 0)).current;
2414
- React18.useEffect(() => {
2512
+ const handlePressIn = React19.useCallback(() => pressTo(ROW_PRESS_SCALE), [pressTo]);
2513
+ const handlePressOut = React19.useCallback(() => pressTo(1), [pressTo]);
2514
+ const selectT = React19.useRef(new reactNative.Animated.Value(isSelected ? 1 : 0)).current;
2515
+ React19.useEffect(() => {
2415
2516
  const anim = reactNative.Animated.timing(selectT, {
2416
2517
  toValue: isSelected ? 1 : 0,
2417
2518
  duration: scaledMs(stateChangeDuration(reduced, SELECT_FILL_MS)),
2418
- easing: easeWire5,
2519
+ easing: easeWire6,
2419
2520
  useNativeDriver: true
2420
2521
  });
2421
2522
  anim.start();
2422
2523
  return () => anim.stop();
2423
2524
  }, [isSelected, reduced, selectT]);
2424
- const checkT = React18.useRef(new reactNative.Animated.Value(isSelected ? 1 : 0)).current;
2425
- React18.useEffect(() => {
2525
+ const checkT = React19.useRef(new reactNative.Animated.Value(isSelected ? 1 : 0)).current;
2526
+ React19.useEffect(() => {
2426
2527
  if (!isSelected) {
2427
2528
  checkT.setValue(0);
2428
2529
  return;
@@ -2455,7 +2556,7 @@ var _OptionRow = ({ opt, isSelected, multiSelect, onToggle }) => {
2455
2556
  reactNative.Animated.View,
2456
2557
  {
2457
2558
  style: [
2458
- styles12.option,
2559
+ styles13.option,
2459
2560
  {
2460
2561
  padding: t.spacing.md,
2461
2562
  borderRadius: t.radius.md,
@@ -2472,7 +2573,7 @@ var _OptionRow = ({ opt, isSelected, multiSelect, onToggle }) => {
2472
2573
  pointerEvents: "none",
2473
2574
  style: [
2474
2575
  reactNative.StyleSheet.absoluteFill,
2475
- styles12.selectedOverlay,
2576
+ styles13.selectedOverlay,
2476
2577
  {
2477
2578
  borderRadius: t.radius.md,
2478
2579
  borderColor: t.colors.primary,
@@ -2486,7 +2587,7 @@ var _OptionRow = ({ opt, isSelected, multiSelect, onToggle }) => {
2486
2587
  reactNative.View,
2487
2588
  {
2488
2589
  style: [
2489
- styles12.indicator,
2590
+ styles13.indicator,
2490
2591
  { borderRadius: t.radius.full },
2491
2592
  isSelected ? { borderColor: t.colors.primary, backgroundColor: t.colors.primary } : { borderColor: t.colors.borderStrong, backgroundColor: "transparent" }
2492
2593
  ],
@@ -2494,7 +2595,7 @@ var _OptionRow = ({ opt, isSelected, multiSelect, onToggle }) => {
2494
2595
  reactNative.Animated.Text,
2495
2596
  {
2496
2597
  style: [
2497
- styles12.check,
2598
+ styles13.check,
2498
2599
  { color: t.colors.onPrimary, opacity: checkT, transform: [{ scale: checkScale }] }
2499
2600
  ],
2500
2601
  children: "\u2713"
@@ -2510,7 +2611,7 @@ var _OptionRow = ({ opt, isSelected, multiSelect, onToggle }) => {
2510
2611
  }
2511
2612
  );
2512
2613
  };
2513
- var OptionRow = React18__default.default.memo(_OptionRow);
2614
+ var OptionRow = React19__default.default.memo(_OptionRow);
2514
2615
  var _SelectionCard = ({
2515
2616
  title,
2516
2617
  options,
@@ -2520,13 +2621,13 @@ var _SelectionCard = ({
2520
2621
  onSelectionChange
2521
2622
  }) => {
2522
2623
  const t = useOnboardingTheme();
2523
- const opts = React18__default.default.useMemo(() => normalizeOptions(options), [options]);
2524
- const [selected, setSelected] = React18.useState([]);
2525
- const [submitted, setSubmitted] = React18.useState(false);
2526
- React18.useEffect(() => {
2624
+ const opts = React19__default.default.useMemo(() => normalizeOptions(options), [options]);
2625
+ const [selected, setSelected] = React19.useState([]);
2626
+ const [submitted, setSubmitted] = React19.useState(false);
2627
+ React19.useEffect(() => {
2527
2628
  if (!submitted && selected.length) onSelectionChange == null ? void 0 : onSelectionChange(multiSelect ? selected : selected[0]);
2528
2629
  }, [selected, submitted, multiSelect, onSelectionChange]);
2529
- const toggle = React18.useCallback(
2630
+ const toggle = React19.useCallback(
2530
2631
  (value) => {
2531
2632
  if (submitted) return;
2532
2633
  if (multiSelect) {
@@ -2537,7 +2638,7 @@ var _SelectionCard = ({
2537
2638
  },
2538
2639
  [submitted, multiSelect]
2539
2640
  );
2540
- const handleSubmit = React18.useCallback(() => {
2641
+ const handleSubmit = React19.useCallback(() => {
2541
2642
  if (!selected.length || !onSelect || submitted) return;
2542
2643
  setSubmitted(true);
2543
2644
  onSelect(multiSelect ? selected : selected[0]);
@@ -2571,18 +2672,18 @@ var _SelectionCard = ({
2571
2672
  var SelectionCard = {
2572
2673
  name: "SelectionCard",
2573
2674
  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).",
2574
- component: React18__default.default.memo(_SelectionCard),
2575
- propsSchema: schema3,
2675
+ component: React19__default.default.memo(_SelectionCard),
2676
+ propsSchema: schema4,
2576
2677
  defaultProps: { submitLabel: "Continue", multiSelect: false }
2577
2678
  };
2578
- var styles12 = reactNative.StyleSheet.create({
2679
+ var styles13 = reactNative.StyleSheet.create({
2579
2680
  option: { borderWidth: 1.5, flexDirection: "row", alignItems: "center", overflow: "hidden" },
2580
2681
  selectedOverlay: { borderWidth: 1.5 },
2581
2682
  indicator: { width: 24, height: 24, borderWidth: 2, alignItems: "center", justifyContent: "center" },
2582
2683
  check: { fontSize: 13, fontWeight: "800", lineHeight: 15 }
2583
2684
  });
2584
- var easeWire6 = reactNative.Easing.bezier(...WIRE_BEZIER);
2585
- var schema4 = zod.z.object({
2685
+ var easeWire7 = reactNative.Easing.bezier(...WIRE_BEZIER);
2686
+ var schema5 = zod.z.object({
2586
2687
  title: zod.z.string().describe("Question or prompt for the user, e.g. 'What brings you here?'"),
2587
2688
  options: optionsField(
2588
2689
  "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."
@@ -2593,9 +2694,9 @@ var schema4 = zod.z.object({
2593
2694
  var _OptionCard = ({ opt, isSelected, multiSelect, onToggle }) => {
2594
2695
  const t = useOnboardingTheme();
2595
2696
  const reduced = useReducedMotion();
2596
- const handlePress = React18.useCallback(() => onToggle(opt.value), [opt.value, onToggle]);
2597
- const pressScale = React18.useRef(new reactNative.Animated.Value(1)).current;
2598
- const pressTo = React18.useCallback(
2697
+ const handlePress = React19.useCallback(() => onToggle(opt.value), [opt.value, onToggle]);
2698
+ const pressScale = React19.useRef(new reactNative.Animated.Value(1)).current;
2699
+ const pressTo = React19.useCallback(
2599
2700
  (to) => {
2600
2701
  if (reduced) return;
2601
2702
  reactNative.Animated.spring(pressScale, {
@@ -2607,14 +2708,14 @@ var _OptionCard = ({ opt, isSelected, multiSelect, onToggle }) => {
2607
2708
  },
2608
2709
  [pressScale, reduced]
2609
2710
  );
2610
- const handlePressIn = React18.useCallback(() => pressTo(ROW_PRESS_SCALE), [pressTo]);
2611
- const handlePressOut = React18.useCallback(() => pressTo(1), [pressTo]);
2612
- const selectT = React18.useRef(new reactNative.Animated.Value(isSelected ? 1 : 0)).current;
2613
- React18.useEffect(() => {
2711
+ const handlePressIn = React19.useCallback(() => pressTo(ROW_PRESS_SCALE), [pressTo]);
2712
+ const handlePressOut = React19.useCallback(() => pressTo(1), [pressTo]);
2713
+ const selectT = React19.useRef(new reactNative.Animated.Value(isSelected ? 1 : 0)).current;
2714
+ React19.useEffect(() => {
2614
2715
  const anim = reactNative.Animated.timing(selectT, {
2615
2716
  toValue: isSelected ? 1 : 0,
2616
2717
  duration: scaledMs(stateChangeDuration(reduced, SELECT_FILL_MS)),
2617
- easing: easeWire6,
2718
+ easing: easeWire7,
2618
2719
  useNativeDriver: true
2619
2720
  });
2620
2721
  anim.start();
@@ -2629,12 +2730,12 @@ var _OptionCard = ({ opt, isSelected, multiSelect, onToggle }) => {
2629
2730
  accessibilityRole: multiSelect ? "checkbox" : "radio",
2630
2731
  accessibilityState: multiSelect ? { checked: isSelected } : { selected: isSelected },
2631
2732
  accessibilityLabel: opt.label,
2632
- style: styles13.cell,
2733
+ style: styles14.cell,
2633
2734
  children: /* @__PURE__ */ jsxRuntime.jsxs(
2634
2735
  reactNative.Animated.View,
2635
2736
  {
2636
2737
  style: [
2637
- styles13.card,
2738
+ styles14.card,
2638
2739
  {
2639
2740
  padding: t.spacing.md,
2640
2741
  borderRadius: t.radius.lg,
@@ -2651,7 +2752,7 @@ var _OptionCard = ({ opt, isSelected, multiSelect, onToggle }) => {
2651
2752
  pointerEvents: "none",
2652
2753
  style: [
2653
2754
  reactNative.StyleSheet.absoluteFill,
2654
- styles13.selectedOverlay,
2755
+ styles14.selectedOverlay,
2655
2756
  {
2656
2757
  borderRadius: t.radius.lg,
2657
2758
  borderColor: t.colors.primary,
@@ -2669,7 +2770,7 @@ var _OptionCard = ({ opt, isSelected, multiSelect, onToggle }) => {
2669
2770
  }
2670
2771
  );
2671
2772
  };
2672
- var OptionCard = React18__default.default.memo(_OptionCard);
2773
+ var OptionCard = React19__default.default.memo(_OptionCard);
2673
2774
  var _CardGridSelectCard = ({
2674
2775
  title,
2675
2776
  options,
@@ -2679,13 +2780,13 @@ var _CardGridSelectCard = ({
2679
2780
  onSelectionChange
2680
2781
  }) => {
2681
2782
  const t = useOnboardingTheme();
2682
- const opts = React18__default.default.useMemo(() => normalizeOptions(options), [options]);
2683
- const [selected, setSelected] = React18.useState([]);
2684
- const [submitted, setSubmitted] = React18.useState(false);
2685
- React18.useEffect(() => {
2783
+ const opts = React19__default.default.useMemo(() => normalizeOptions(options), [options]);
2784
+ const [selected, setSelected] = React19.useState([]);
2785
+ const [submitted, setSubmitted] = React19.useState(false);
2786
+ React19.useEffect(() => {
2686
2787
  if (!submitted && selected.length) onSelectionChange == null ? void 0 : onSelectionChange(multiSelect ? selected : selected[0]);
2687
2788
  }, [selected, submitted, multiSelect, onSelectionChange]);
2688
- const toggle = React18.useCallback(
2789
+ const toggle = React19.useCallback(
2689
2790
  (value) => {
2690
2791
  if (submitted) return;
2691
2792
  if (multiSelect) {
@@ -2698,7 +2799,7 @@ var _CardGridSelectCard = ({
2698
2799
  },
2699
2800
  [submitted, multiSelect]
2700
2801
  );
2701
- const handleSubmit = React18.useCallback(() => {
2802
+ const handleSubmit = React19.useCallback(() => {
2702
2803
  if (!selected.length || !onSelect || submitted) return;
2703
2804
  setSubmitted(true);
2704
2805
  onSelect(multiSelect ? selected : selected[0]);
@@ -2716,7 +2817,7 @@ var _CardGridSelectCard = ({
2716
2817
  disabled: selected.length === 0 || submitted
2717
2818
  }
2718
2819
  ),
2719
- children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles13.grid, { gap: t.spacing.sm }], children: opts.map((opt) => /* @__PURE__ */ jsxRuntime.jsx(
2820
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles14.grid, { gap: t.spacing.sm }], children: opts.map((opt) => /* @__PURE__ */ jsxRuntime.jsx(
2720
2821
  OptionCard,
2721
2822
  {
2722
2823
  opt,
@@ -2732,19 +2833,19 @@ var _CardGridSelectCard = ({
2732
2833
  var CardGridSelectCard = {
2733
2834
  name: "CardGridSelectCard",
2734
2835
  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.",
2735
- component: React18__default.default.memo(_CardGridSelectCard),
2736
- propsSchema: schema4,
2836
+ component: React19__default.default.memo(_CardGridSelectCard),
2837
+ propsSchema: schema5,
2737
2838
  defaultProps: { submitLabel: "Continue", multiSelect: true }
2738
2839
  };
2739
- var styles13 = reactNative.StyleSheet.create({
2840
+ var styles14 = reactNative.StyleSheet.create({
2740
2841
  grid: { flexDirection: "row", flexWrap: "wrap" },
2741
2842
  // Two per row: ~47% basis leaves room for the gap, flexGrow fills the remainder exactly.
2742
2843
  cell: { flexBasis: "47%", flexGrow: 1 },
2743
2844
  card: { borderWidth: 1.5, alignItems: "flex-start", overflow: "hidden" },
2744
2845
  selectedOverlay: { borderWidth: 1.5 }
2745
2846
  });
2746
- var easeWire7 = reactNative.Easing.bezier(...WIRE_BEZIER);
2747
- var schema5 = zod.z.object({
2847
+ var easeWire8 = reactNative.Easing.bezier(...WIRE_BEZIER);
2848
+ var schema6 = zod.z.object({
2748
2849
  status: zod.z.enum(["success", "warning", "error", "info"]).describe("Status type"),
2749
2850
  title: zod.z.string().describe("Status title"),
2750
2851
  message: zod.z.string().optional().describe("Supporting message"),
@@ -2759,11 +2860,11 @@ var STATUS_ICONS = {
2759
2860
  info: "\u2139"
2760
2861
  };
2761
2862
  var _StatusCard = ({ status, title, message, ctaLabel, onContinue, onPress, onSubmit }) => {
2762
- var _a2;
2863
+ var _a;
2763
2864
  const t = useOnboardingTheme();
2764
2865
  const reduced = useReducedMotion();
2765
- const [submitted, setSubmitted] = React18.useState(false);
2766
- const color = React18.useMemo(() => {
2866
+ const [submitted, setSubmitted] = React19.useState(false);
2867
+ const color = React19.useMemo(() => {
2767
2868
  switch (status) {
2768
2869
  case "success":
2769
2870
  return t.colors.success;
@@ -2775,9 +2876,9 @@ var _StatusCard = ({ status, title, message, ctaLabel, onContinue, onPress, onSu
2775
2876
  return t.colors.info;
2776
2877
  }
2777
2878
  }, [status, t.colors]);
2778
- const popT = React18.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
2779
- const copyT = React18.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
2780
- React18.useEffect(() => {
2879
+ const popT = React19.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
2880
+ const copyT = React19.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
2881
+ React19.useEffect(() => {
2781
2882
  if (reduced) {
2782
2883
  popT.setValue(1);
2783
2884
  copyT.setValue(1);
@@ -2794,7 +2895,7 @@ var _StatusCard = ({ status, title, message, ctaLabel, onContinue, onPress, onSu
2794
2895
  toValue: 1,
2795
2896
  duration: scaledMs(STATUS_COPY_MS),
2796
2897
  delay: scaledMs(STATUS_COPY_STAGGER_MS),
2797
- easing: easeWire7,
2898
+ easing: easeWire8,
2798
2899
  useNativeDriver: true
2799
2900
  })
2800
2901
  ];
@@ -2803,7 +2904,7 @@ var _StatusCard = ({ status, title, message, ctaLabel, onContinue, onPress, onSu
2803
2904
  }, [reduced, popT, copyT]);
2804
2905
  const popScale = popT.interpolate({ inputRange: [0, 1], outputRange: [STATUS_POP_SCALE_FROM, 1] });
2805
2906
  const copyRise = copyT.interpolate({ inputRange: [0, 1], outputRange: [10, 0] });
2806
- const handleContinue = React18.useCallback(() => {
2907
+ const handleContinue = React19.useCallback(() => {
2807
2908
  if (!submitted) {
2808
2909
  setSubmitted(true);
2809
2910
  if (onContinue) onContinue();
@@ -2816,8 +2917,8 @@ var _StatusCard = ({ status, title, message, ctaLabel, onContinue, onPress, onSu
2816
2917
  {
2817
2918
  align: "center",
2818
2919
  footer: ctaLabel ? /* @__PURE__ */ jsxRuntime.jsx(Button, { title: ctaLabel, onPress: handleContinue, variant: "primary", disabled: submitted }) : void 0,
2819
- children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles14.center, { gap: t.spacing.sm }], children: [
2820
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles14.titleRow, { gap: t.spacing.sm }], children: [
2920
+ children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles15.center, { gap: t.spacing.sm }], children: [
2921
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles15.titleRow, { gap: t.spacing.sm }], children: [
2821
2922
  /* @__PURE__ */ jsxRuntime.jsx(
2822
2923
  reactNative.Animated.Text,
2823
2924
  {
@@ -2825,7 +2926,7 @@ var _StatusCard = ({ status, title, message, ctaLabel, onContinue, onPress, onSu
2825
2926
  headingStyle(t.fonts),
2826
2927
  { color, opacity: popT, transform: [{ scale: popScale }] }
2827
2928
  ],
2828
- children: (_a2 = STATUS_ICONS[status]) != null ? _a2 : "\xB7"
2929
+ children: (_a = STATUS_ICONS[status]) != null ? _a : "\xB7"
2829
2930
  }
2830
2931
  ),
2831
2932
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -2861,14 +2962,14 @@ var _StatusCard = ({ status, title, message, ctaLabel, onContinue, onPress, onSu
2861
2962
  var StatusCard = {
2862
2963
  name: "StatusCard",
2863
2964
  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.",
2864
- component: React18__default.default.memo(_StatusCard),
2865
- propsSchema: schema5
2965
+ component: React19__default.default.memo(_StatusCard),
2966
+ propsSchema: schema6
2866
2967
  };
2867
- var styles14 = reactNative.StyleSheet.create({
2968
+ var styles15 = reactNative.StyleSheet.create({
2868
2969
  center: { width: "100%", alignItems: "center", justifyContent: "center" },
2869
2970
  titleRow: { flexDirection: "row", alignItems: "center", justifyContent: "center" }
2870
2971
  });
2871
- var schema6 = zod.z.object({
2972
+ var schema7 = zod.z.object({
2872
2973
  label: zod.z.string().describe("What is being counted, e.g. 'Number of days' or 'Travellers'"),
2873
2974
  min: zod.z.number().optional().describe("Minimum value, default 1"),
2874
2975
  max: zod.z.number().optional().describe("Maximum value, default 30"),
@@ -2889,22 +2990,22 @@ var _NumberStepperCard = ({
2889
2990
  onSubmit
2890
2991
  }) => {
2891
2992
  const t = useOnboardingTheme();
2892
- const styles17 = React18.useMemo(() => makeStyles4(t), [t]);
2993
+ const styles18 = React19.useMemo(() => makeStyles4(t), [t]);
2893
2994
  const safeMin = safeNum(min, 1);
2894
2995
  const safeMaxRaw = safeNum(max, 30);
2895
2996
  const safeMax = safeMaxRaw < safeMin ? safeMin : safeMaxRaw;
2896
2997
  const safeStepRaw = safeNum(step, 1);
2897
2998
  const safeStep = safeStepRaw > 0 ? safeStepRaw : 1;
2898
2999
  const safeDefault = safeNum(defaultValue, safeMin);
2899
- const [value, setValue] = React18.useState(Math.min(Math.max(safeDefault, safeMin), safeMax));
2900
- const [submitted, setSubmitted] = React18.useState(false);
2901
- const handleDecrement = React18.useCallback(() => {
3000
+ const [value, setValue] = React19.useState(Math.min(Math.max(safeDefault, safeMin), safeMax));
3001
+ const [submitted, setSubmitted] = React19.useState(false);
3002
+ const handleDecrement = React19.useCallback(() => {
2902
3003
  if (!submitted) setValue((v) => Math.max(v - safeStep, safeMin));
2903
3004
  }, [submitted, safeStep, safeMin]);
2904
- const handleIncrement = React18.useCallback(() => {
3005
+ const handleIncrement = React19.useCallback(() => {
2905
3006
  if (!submitted) setValue((v) => Math.min(v + safeStep, safeMax));
2906
3007
  }, [submitted, safeStep, safeMax]);
2907
- const handleSubmit = React18.useCallback(() => {
3008
+ const handleSubmit = React19.useCallback(() => {
2908
3009
  if (!submitted) {
2909
3010
  setSubmitted(true);
2910
3011
  onSubmit == null ? void 0 : onSubmit(value);
@@ -2913,7 +3014,7 @@ var _NumberStepperCard = ({
2913
3014
  const atMin = value <= safeMin;
2914
3015
  const atMax = value >= safeMax;
2915
3016
  const stepBtnStyle = (disabled) => [
2916
- styles17.stepBtn,
3017
+ styles18.stepBtn,
2917
3018
  { borderRadius: t.radius.full, backgroundColor: t.colors.background, borderColor: disabled ? t.colors.border : t.colors.primary }
2918
3019
  ];
2919
3020
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -2922,10 +3023,10 @@ var _NumberStepperCard = ({
2922
3023
  title: label,
2923
3024
  align: "center",
2924
3025
  footer: /* @__PURE__ */ jsxRuntime.jsx(Button, { title: submitLabel, onPress: handleSubmit, variant: "primary", disabled: submitted }),
2925
- children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles17.wrap, children: [
2926
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles17.stepper, { gap: t.spacing.lg }], children: [
3026
+ children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles18.wrap, children: [
3027
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles18.stepper, { gap: t.spacing.lg }], children: [
2927
3028
  /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { onPress: atMin || submitted ? void 0 : handleDecrement, disabled: atMin || submitted, style: stepBtnStyle(atMin), children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [headingStyle(t.fonts), { color: atMin ? t.colors.textMuted : t.colors.primary }], children: "\u2212" }) }),
2928
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles17.valueBox, children: [
3029
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles18.valueBox, children: [
2929
3030
  /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [headingStyle(t.fonts), { color: t.colors.primary }], children: String(value) }),
2930
3031
  unit ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [captionStyle(t.fonts), { color: t.colors.textMuted }], children: unit }) : null
2931
3032
  ] }),
@@ -2939,8 +3040,8 @@ var _NumberStepperCard = ({
2939
3040
  var NumberStepperCard = {
2940
3041
  name: "NumberStepperCard",
2941
3042
  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.",
2942
- component: React18__default.default.memo(_NumberStepperCard),
2943
- propsSchema: schema6,
3043
+ component: React19__default.default.memo(_NumberStepperCard),
3044
+ propsSchema: schema7,
2944
3045
  defaultProps: { min: 1, max: 30, defaultValue: 1, step: 1, submitLabel: "Confirm" }
2945
3046
  };
2946
3047
  function makeStyles4(t) {
@@ -2951,9 +3052,9 @@ function makeStyles4(t) {
2951
3052
  valueBox: { alignItems: "center", minWidth: 80 }
2952
3053
  });
2953
3054
  }
2954
- var easeWire8 = reactNative.Easing.bezier(...WIRE_BEZIER);
2955
- var RISE_PX = 10;
2956
- var schema7 = zod.z.object({
3055
+ var easeWire9 = reactNative.Easing.bezier(...WIRE_BEZIER);
3056
+ var RISE_PX2 = 10;
3057
+ var schema8 = zod.z.object({
2957
3058
  title: zod.z.string().describe("Short, warm headline for the value/momentum moment"),
2958
3059
  body: zod.z.string().optional().describe("One or two supporting lines that reflect the user's answers back and build momentum"),
2959
3060
  items: zod.z.array(zod.z.string()).max(4).optional().describe(
@@ -2977,9 +3078,9 @@ var _WorkItem = ({
2977
3078
  reduced
2978
3079
  }) => {
2979
3080
  const t = useOnboardingTheme();
2980
- const rowT = React18.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
2981
- const checkT = React18.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
2982
- React18.useEffect(() => {
3081
+ const rowT = React19.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
3082
+ const checkT = React19.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
3083
+ React19.useEffect(() => {
2983
3084
  if (reduced) {
2984
3085
  rowT.setValue(1);
2985
3086
  checkT.setValue(1);
@@ -2990,7 +3091,7 @@ var _WorkItem = ({
2990
3091
  toValue: 1,
2991
3092
  duration: scaledMs(INTERSTITIAL_ITEM_MS),
2992
3093
  delay: scaledMs(interstitialItemDelay(index)),
2993
- easing: easeWire8,
3094
+ easing: easeWire9,
2994
3095
  useNativeDriver: true
2995
3096
  }),
2996
3097
  reactNative.Animated.spring(checkT, {
@@ -3004,7 +3105,7 @@ var _WorkItem = ({
3004
3105
  anims.forEach((a) => a.start());
3005
3106
  return () => anims.forEach((a) => a.stop());
3006
3107
  }, [reduced, index, rowT, checkT]);
3007
- const rise = rowT.interpolate({ inputRange: [0, 1], outputRange: [RISE_PX, 0] });
3108
+ const rise = rowT.interpolate({ inputRange: [0, 1], outputRange: [RISE_PX2, 0] });
3008
3109
  const checkScale = checkT.interpolate({
3009
3110
  inputRange: [0, 1],
3010
3111
  outputRange: [INTERSTITIAL_CHECK_SCALE_FROM, 1]
@@ -3012,20 +3113,20 @@ var _WorkItem = ({
3012
3113
  return /* @__PURE__ */ jsxRuntime.jsxs(
3013
3114
  reactNative.Animated.View,
3014
3115
  {
3015
- style: [styles15.itemRow, { gap: t.spacing.sm + t.spacing.xs, opacity: rowT, transform: [{ translateY: rise }] }],
3116
+ style: [styles16.itemRow, { gap: t.spacing.sm + t.spacing.xs, opacity: rowT, transform: [{ translateY: rise }] }],
3016
3117
  children: [
3017
3118
  /* @__PURE__ */ jsxRuntime.jsx(
3018
3119
  reactNative.View,
3019
3120
  {
3020
3121
  style: [
3021
- styles15.itemBadge,
3122
+ styles16.itemBadge,
3022
3123
  { backgroundColor: t.colors.primarySoft, borderRadius: t.radius.full }
3023
3124
  ],
3024
3125
  children: /* @__PURE__ */ jsxRuntime.jsx(
3025
3126
  reactNative.Animated.Text,
3026
3127
  {
3027
3128
  style: [
3028
- styles15.itemCheck,
3129
+ styles16.itemCheck,
3029
3130
  { color: t.colors.primary, opacity: checkT, transform: [{ scale: checkScale }] }
3030
3131
  ],
3031
3132
  children: "\u2713"
@@ -3033,12 +3134,12 @@ var _WorkItem = ({
3033
3134
  )
3034
3135
  }
3035
3136
  ),
3036
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [bodyStyle(t.fonts), styles15.itemText, { color: t.colors.text }], children: text })
3137
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [bodyStyle(t.fonts), styles16.itemText, { color: t.colors.text }], children: text })
3037
3138
  ]
3038
3139
  }
3039
3140
  );
3040
3141
  };
3041
- var WorkItem = React18__default.default.memo(_WorkItem);
3142
+ var WorkItem = React19__default.default.memo(_WorkItem);
3042
3143
  var _InterstitialCard = ({
3043
3144
  title,
3044
3145
  body,
@@ -3054,17 +3155,17 @@ var _InterstitialCard = ({
3054
3155
  const reduced = useReducedMotion();
3055
3156
  const node = useIllustration(illustration);
3056
3157
  const fallbackNode = useIllustration(illustrationFallback);
3057
- const [submitted, setSubmitted] = React18.useState(false);
3058
- const [imgFailed, setImgFailed] = React18.useState(false);
3059
- const [canAdvance, setCanAdvance] = React18.useState(false);
3060
- React18.useEffect(() => {
3158
+ const [submitted, setSubmitted] = React19.useState(false);
3159
+ const [imgFailed, setImgFailed] = React19.useState(false);
3160
+ const [canAdvance, setCanAdvance] = React19.useState(false);
3161
+ React19.useEffect(() => {
3061
3162
  const timer = setTimeout(() => setCanAdvance(true), MIN_DISPLAY_MS);
3062
3163
  return () => clearTimeout(timer);
3063
3164
  }, []);
3064
- const artT = React18.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
3065
- const titleT = React18.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
3066
- const bodyT = React18.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
3067
- React18.useEffect(() => {
3165
+ const artT = React19.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
3166
+ const titleT = React19.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
3167
+ const bodyT = React19.useRef(new reactNative.Animated.Value(reduced ? 1 : 0)).current;
3168
+ React19.useEffect(() => {
3068
3169
  if (reduced) {
3069
3170
  artT.setValue(1);
3070
3171
  titleT.setValue(1);
@@ -3075,7 +3176,7 @@ var _InterstitialCard = ({
3075
3176
  toValue: 1,
3076
3177
  duration: scaledMs(INTERSTITIAL_HEAD_MS),
3077
3178
  delay,
3078
- easing: easeWire8,
3179
+ easing: easeWire9,
3079
3180
  useNativeDriver: true
3080
3181
  });
3081
3182
  const anims = [
@@ -3088,9 +3189,9 @@ var _InterstitialCard = ({
3088
3189
  }, [reduced, artT, titleT, bodyT]);
3089
3190
  const riseStyle = (value) => ({
3090
3191
  opacity: value,
3091
- transform: [{ translateY: value.interpolate({ inputRange: [0, 1], outputRange: [RISE_PX, 0] }) }]
3192
+ transform: [{ translateY: value.interpolate({ inputRange: [0, 1], outputRange: [RISE_PX2, 0] }) }]
3092
3193
  });
3093
- const handleContinue = React18.useCallback(() => {
3194
+ const handleContinue = React19.useCallback(() => {
3094
3195
  if (submitted || !canAdvance) return;
3095
3196
  setSubmitted(true);
3096
3197
  if (onSubmit) onSubmit("continue");
@@ -3111,18 +3212,18 @@ var _InterstitialCard = ({
3111
3212
  disabled: submitted || !canAdvance
3112
3213
  }
3113
3214
  ),
3114
- children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles15.center, { gap: t.spacing.md }], children: [
3115
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Animated.View, { style: [styles15.fullWidth, riseStyle(artT)], children: showImage ? /* @__PURE__ */ jsxRuntime.jsx(
3215
+ children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles16.center, { gap: t.spacing.md }], children: [
3216
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Animated.View, { style: [styles16.fullWidth, riseStyle(artT)], children: showImage ? /* @__PURE__ */ jsxRuntime.jsx(
3116
3217
  reactNative.Image,
3117
3218
  {
3118
3219
  source: { uri: imageUrl },
3119
- style: styles15.image,
3220
+ style: styles16.image,
3120
3221
  resizeMode: "contain",
3121
3222
  onError: () => setImgFailed(true)
3122
3223
  }
3123
- ) : art ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles15.art, children: art }) : null }),
3224
+ ) : art ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles16.art, children: art }) : null }),
3124
3225
  /* @__PURE__ */ jsxRuntime.jsx(reactNative.Animated.View, { style: riseStyle(titleT), children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [headingStyle(t.fonts), { color: t.colors.text, textAlign: "center" }], children: title }) }),
3125
- (items == null ? void 0 : items.length) ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles15.items, { gap: t.spacing.md, marginTop: t.spacing.sm }], children: items.map((item, i) => /* @__PURE__ */ jsxRuntime.jsx(WorkItem, { text: item, index: i, reduced }, `${item}-${i}`)) }) : body ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Animated.View, { style: riseStyle(bodyT), children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [bodyStyle(t.fonts), { color: t.colors.textMuted, textAlign: "center" }], children: body }) }) : null
3226
+ (items == null ? void 0 : items.length) ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles16.items, { gap: t.spacing.md, marginTop: t.spacing.sm }], children: items.map((item, i) => /* @__PURE__ */ jsxRuntime.jsx(WorkItem, { text: item, index: i, reduced }, `${item}-${i}`)) }) : body ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Animated.View, { style: riseStyle(bodyT), children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [bodyStyle(t.fonts), { color: t.colors.textMuted, textAlign: "center" }], children: body }) }) : null
3126
3227
  ] })
3127
3228
  }
3128
3229
  );
@@ -3130,11 +3231,11 @@ var _InterstitialCard = ({
3130
3231
  var InterstitialCard = {
3131
3232
  name: "InterstitialCard",
3132
3233
  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.",
3133
- component: React18__default.default.memo(_InterstitialCard),
3134
- propsSchema: schema7,
3234
+ component: React19__default.default.memo(_InterstitialCard),
3235
+ propsSchema: schema8,
3135
3236
  defaultProps: { ctaLabel: "Continue", illustrationFallback: "momentum" }
3136
3237
  };
3137
- var styles15 = reactNative.StyleSheet.create({
3238
+ var styles16 = reactNative.StyleSheet.create({
3138
3239
  center: { width: "100%", alignItems: "center", justifyContent: "center" },
3139
3240
  fullWidth: { width: "100%", alignItems: "center" },
3140
3241
  art: { alignItems: "center", justifyContent: "center" },
@@ -3168,9 +3269,9 @@ var runtimeRequire2 = (moduleName) => {
3168
3269
  try {
3169
3270
  switch (moduleName) {
3170
3271
  case "expo-constants":
3171
- return init_Constants(), __toCommonJS(Constants_exports);
3272
+ return require("expo-constants");
3172
3273
  case "expo-application":
3173
- return __require("expo-application");
3274
+ return require("expo-application");
3174
3275
  default:
3175
3276
  return void 0;
3176
3277
  }
@@ -3193,14 +3294,14 @@ var safeInterop = (requireModule, moduleName) => {
3193
3294
  };
3194
3295
  var detectAppVersion = (requireModule = runtimeRequire2) => {
3195
3296
  try {
3196
- const constants2 = safeInterop(requireModule, "expo-constants");
3197
- if (constants2) {
3198
- const expoConfig = constants2.expoConfig;
3297
+ const constants = safeInterop(requireModule, "expo-constants");
3298
+ if (constants) {
3299
+ const expoConfig = constants.expoConfig;
3199
3300
  if (expoConfig && typeof expoConfig === "object") {
3200
3301
  const fromExpoConfig = coerceVersion(expoConfig.version);
3201
3302
  if (fromExpoConfig) return fromExpoConfig;
3202
3303
  }
3203
- const fromNative = coerceVersion(constants2.nativeAppVersion);
3304
+ const fromNative = coerceVersion(constants.nativeAppVersion);
3204
3305
  if (fromNative) return fromNative;
3205
3306
  }
3206
3307
  const application = safeInterop(requireModule, "expo-application");
@@ -3223,7 +3324,7 @@ var runtimeRequire3 = (moduleName) => {
3223
3324
  if (moduleName !== "expo-device") return void 0;
3224
3325
  if (typeof __require !== "function") return void 0;
3225
3326
  try {
3226
- return __require("expo-device");
3327
+ return require("expo-device");
3227
3328
  } catch {
3228
3329
  return void 0;
3229
3330
  }
@@ -3265,7 +3366,7 @@ var deriveFormFactor = (iosIdiom, width, height) => {
3265
3366
  return void 0;
3266
3367
  };
3267
3368
  var collectDeviceContext = () => {
3268
- var _a2;
3369
+ var _a;
3269
3370
  const ctx = { platform: reactNative.Platform.OS };
3270
3371
  try {
3271
3372
  const version = reactNative.Platform.Version;
@@ -3274,26 +3375,26 @@ var collectDeviceContext = () => {
3274
3375
  }
3275
3376
  } catch {
3276
3377
  }
3277
- let constants2 = {};
3378
+ let constants = {};
3278
3379
  try {
3279
- constants2 = (_a2 = reactNative.Platform.constants) != null ? _a2 : {};
3380
+ constants = (_a = reactNative.Platform.constants) != null ? _a : {};
3280
3381
  } catch {
3281
- constants2 = {};
3382
+ constants = {};
3282
3383
  }
3283
3384
  let iosIdiom;
3284
3385
  try {
3285
3386
  if (reactNative.Platform.OS === "android") {
3286
- const brand = constants2.Brand;
3287
- const model = constants2.Model;
3288
- const release = constants2.Release;
3387
+ const brand = constants.Brand;
3388
+ const model = constants.Model;
3389
+ const release = constants.Release;
3289
3390
  if (typeof brand === "string" && brand) ctx.brand = brand;
3290
3391
  if (typeof model === "string" && model) ctx.model = model;
3291
3392
  if (release !== void 0 && release !== null && String(release)) {
3292
3393
  ctx.osVersion = String(release);
3293
3394
  }
3294
3395
  } else if (reactNative.Platform.OS === "ios") {
3295
- const osVersion = constants2.osVersion;
3296
- const idiom = constants2.interfaceIdiom;
3396
+ const osVersion = constants.osVersion;
3397
+ const idiom = constants.interfaceIdiom;
3297
3398
  if (osVersion !== void 0 && osVersion !== null && String(osVersion)) {
3298
3399
  ctx.osVersion = String(osVersion);
3299
3400
  }
@@ -3344,11 +3445,11 @@ var provenanceRegistry = () => {
3344
3445
  };
3345
3446
  var provenanceKey = (space, scope) => `${space}:${scope != null ? scope : "default"}`;
3346
3447
  var resolveIdentity = (input) => {
3347
- var _a2;
3448
+ var _a;
3348
3449
  if (typeof input.value !== "string") return void 0;
3349
3450
  const value = input.value.trim();
3350
3451
  if (!value) return void 0;
3351
- const durable = (_a2 = input.durable) != null ? _a2 : input.source === "host";
3452
+ const durable = (_a = input.durable) != null ? _a : input.source === "host";
3352
3453
  if (input.source === "host") {
3353
3454
  provenanceRegistry().host.set(provenanceKey(input.space, input.scope), value);
3354
3455
  }
@@ -3382,8 +3483,8 @@ var startHydration = (registry, appId, storage, minted) => {
3382
3483
  if (existing) return existing;
3383
3484
  const slot = deviceIdStorageKey(appId);
3384
3485
  const degraded = () => {
3385
- var _a2;
3386
- return { value: (_a2 = registry.keys.get(appId)) != null ? _a2 : minted, durable: false };
3486
+ var _a;
3487
+ return { value: (_a = registry.keys.get(appId)) != null ? _a : minted, durable: false };
3387
3488
  };
3388
3489
  const adopted = (value) => ({ value, durable: true });
3389
3490
  let run;
@@ -3396,8 +3497,8 @@ var startHydration = (registry, appId, storage, minted) => {
3396
3497
  }
3397
3498
  return Promise.resolve(storage.setItem(slot, minted)).then(
3398
3499
  () => {
3399
- var _a2;
3400
- return adopted((_a2 = registry.keys.get(appId)) != null ? _a2 : minted);
3500
+ var _a;
3501
+ return adopted((_a = registry.keys.get(appId)) != null ? _a : minted);
3401
3502
  },
3402
3503
  degraded
3403
3504
  );
@@ -3407,17 +3508,17 @@ var startHydration = (registry, appId, storage, minted) => {
3407
3508
  }
3408
3509
  registry.pending.set(appId, run);
3409
3510
  void run.then((outcome) => {
3410
- var _a2;
3511
+ var _a;
3411
3512
  if (outcome.durable) return;
3412
- (_a2 = registry.pending) == null ? void 0 : _a2.delete(appId);
3513
+ (_a = registry.pending) == null ? void 0 : _a.delete(appId);
3413
3514
  registry.hydrating.delete(appId);
3414
3515
  });
3415
3516
  return run;
3416
3517
  };
3417
3518
  var resolveAutoDeviceKey = (opts = {}) => {
3418
- var _a2, _b;
3519
+ var _a, _b;
3419
3520
  const registry = autoDeviceKeyRegistry();
3420
- const appId = (_a2 = opts.appId) != null ? _a2 : "default";
3521
+ const appId = (_a = opts.appId) != null ? _a : "default";
3421
3522
  let id = registry.keys.get(appId);
3422
3523
  if (!id) {
3423
3524
  id = mintDeviceId();
@@ -3431,13 +3532,13 @@ var resolveAutoDeviceKey = (opts = {}) => {
3431
3532
  return (_b = registry.keys.get(appId)) != null ? _b : id;
3432
3533
  };
3433
3534
  var hydrateAutoDeviceKey = async (opts = {}) => {
3434
- var _a2, _b;
3435
- return (_b = (_a2 = await hydrateDeviceIdentity(opts)) == null ? void 0 : _a2.value) != null ? _b : resolveAutoDeviceKey(opts);
3535
+ var _a, _b;
3536
+ return (_b = (_a = await hydrateDeviceIdentity(opts)) == null ? void 0 : _a.value) != null ? _b : resolveAutoDeviceKey(opts);
3436
3537
  };
3437
3538
  var hydrateDeviceIdentity = async (opts = {}) => {
3438
- var _a2, _b, _c;
3539
+ var _a, _b, _c;
3439
3540
  const id = resolveAutoDeviceKey(opts);
3440
- const appId = (_a2 = opts.appId) != null ? _a2 : "default";
3541
+ const appId = (_a = opts.appId) != null ? _a : "default";
3441
3542
  const record = (value, durable) => resolveIdentity({ value, space: "device", source: "auto", durable, scope: appId });
3442
3543
  if (!opts.storage) return record(id, false);
3443
3544
  const registry = autoDeviceKeyRegistry();
@@ -3447,20 +3548,11 @@ var hydrateDeviceIdentity = async (opts = {}) => {
3447
3548
  return record(outcome.value, outcome.durable);
3448
3549
  };
3449
3550
  var resetAutoDeviceKeys = () => {
3450
- var _a2;
3551
+ var _a;
3451
3552
  const registry = autoDeviceKeyRegistry();
3452
3553
  registry.keys.clear();
3453
3554
  registry.hydrating.clear();
3454
- (_a2 = registry.pending) == null ? void 0 : _a2.clear();
3455
- };
3456
-
3457
- // src/utils/warnInDev.ts
3458
- var warnInDev = (message) => {
3459
- if (typeof __DEV__ !== "undefined" && __DEV__ && typeof console !== "undefined" && console.warn) {
3460
- console.warn(message);
3461
- return true;
3462
- }
3463
- return false;
3555
+ (_a = registry.pending) == null ? void 0 : _a.clear();
3464
3556
  };
3465
3557
 
3466
3558
  // src/analytics/currentSession.ts
@@ -3562,9 +3654,9 @@ var sanitizeUserId = (raw) => {
3562
3654
  };
3563
3655
  var looksLikeEmail = (value) => typeof value === "string" && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value.trim());
3564
3656
  var identifyOnboarding = async (opts) => {
3565
- var _a2, _b, _c;
3657
+ var _a, _b, _c;
3566
3658
  const userId = sanitizeUserId(opts.userId);
3567
- if (!userId || !((_a2 = opts.config) == null ? void 0 : _a2.serverUrl)) return false;
3659
+ if (!userId || !((_a = opts.config) == null ? void 0 : _a.serverUrl)) return false;
3568
3660
  let contextId = ((_b = opts.contextId) == null ? void 0 : _b.trim()) || void 0;
3569
3661
  if (!contextId && opts.storage) {
3570
3662
  const key = (_c = opts.persistKey) != null ? _c : opts.appId ? sessionStorageKey(opts.appId) : void 0;
@@ -3635,11 +3727,11 @@ var clearUserContext = async (opts = {}) => {
3635
3727
  }
3636
3728
  };
3637
3729
  var activationJoinContext = (deviceKey) => {
3638
- var _a2;
3639
- return (_a2 = resolveUserContext({ deviceKey }).userContext) != null ? _a2 : {};
3730
+ var _a;
3731
+ return (_a = resolveUserContext({ deviceKey }).userContext) != null ? _a : {};
3640
3732
  };
3641
3733
  var resolveUserContext = (ctx = {}, opts = {}) => {
3642
- var _a2;
3734
+ var _a;
3643
3735
  const result = {};
3644
3736
  const bucket = {};
3645
3737
  const userId = sanitizeUserId(ctx.userId);
@@ -3649,7 +3741,7 @@ var resolveUserContext = (ctx = {}, opts = {}) => {
3649
3741
  result.deviceKey = deviceKey;
3650
3742
  bucket.device_key = deviceKey;
3651
3743
  }
3652
- const appVersion = (_a2 = cleanString(ctx.appVersion)) != null ? _a2 : cleanString(opts.autoAppVersion);
3744
+ const appVersion = (_a = cleanString(ctx.appVersion)) != null ? _a : cleanString(opts.autoAppVersion);
3653
3745
  if (appVersion) {
3654
3746
  result.appVersion = appVersion;
3655
3747
  bucket.app_version = appVersion;
@@ -3667,6 +3759,43 @@ var resolveUserContext = (ctx = {}, opts = {}) => {
3667
3759
  if (Object.keys(bucket).length > 0) result.userContext = bucket;
3668
3760
  return result;
3669
3761
  };
3762
+
3763
+ // src/permissions/permissionMemory.ts
3764
+ var permissionStorageKey = (appId) => `wireai:permissions:${appId}`;
3765
+ var readSettledPermissions = (raw, sessionId) => {
3766
+ if (!raw || !sessionId) return [];
3767
+ try {
3768
+ const parsed = JSON.parse(raw);
3769
+ if (typeof parsed !== "object" || parsed === null) return [];
3770
+ const record = parsed;
3771
+ if (record.sessionId !== sessionId || !Array.isArray(record.ids)) return [];
3772
+ return record.ids.filter((id) => typeof id === "string" && id.length > 0);
3773
+ } catch {
3774
+ return [];
3775
+ }
3776
+ };
3777
+ var loadSettledPermissions = async (storage, key, sessionId) => {
3778
+ try {
3779
+ return readSettledPermissions(await withTimeout(storage.getItem(key), READ_TIMEOUT_MS), sessionId);
3780
+ } catch {
3781
+ return [];
3782
+ }
3783
+ };
3784
+ var saveSettledPermissions = (storage, key, sessionId, ids) => {
3785
+ try {
3786
+ const record = { sessionId, ids: [...ids] };
3787
+ void storage.setItem(key, JSON.stringify(record)).catch(() => {
3788
+ });
3789
+ } catch {
3790
+ }
3791
+ };
3792
+ var clearSettledPermissions = (storage, key) => {
3793
+ try {
3794
+ void storage.removeItem(key).catch(() => {
3795
+ });
3796
+ } catch {
3797
+ }
3798
+ };
3670
3799
  var AUTO_JOIN_HYDRATION_TIMEOUT_MS = 1500;
3671
3800
  var WireOnboarding = ({
3672
3801
  config,
@@ -3691,20 +3820,21 @@ var WireOnboarding = ({
3691
3820
  retainSessionOnComplete,
3692
3821
  userContext,
3693
3822
  userId,
3694
- autoJoinKey = true
3823
+ autoJoinKey = true,
3824
+ permissionScreens
3695
3825
  }) => {
3696
- var _a2, _b, _c, _d, _e;
3697
- const boundUserId = React18.useMemo(() => sanitizeUserId(userId), [userId]);
3698
- const device = React18.useMemo(() => {
3826
+ var _a, _b, _c, _d, _e, _f;
3827
+ const boundUserId = React19.useMemo(() => sanitizeUserId(userId), [userId]);
3828
+ const device = React19.useMemo(() => {
3699
3829
  const collected = collectDeviceContext();
3700
3830
  return config.appVersion ? { ...collected, appVersion: config.appVersion } : collected;
3701
3831
  }, [config.appVersion]);
3702
3832
  const userContextKey = userContext ? JSON.stringify(userContext) : "";
3703
- const userContextStable = React18.useMemo(
3833
+ const userContextStable = React19.useMemo(
3704
3834
  () => userContextKey ? JSON.parse(userContextKey) : void 0,
3705
3835
  [userContextKey]
3706
3836
  );
3707
- const hostJoinIdentity = React18.useMemo(
3837
+ const hostJoinIdentity = React19.useMemo(
3708
3838
  () => resolveIdentity({
3709
3839
  value: userContextStable == null ? void 0 : userContextStable.device_key,
3710
3840
  space: "device",
@@ -3716,8 +3846,8 @@ var WireOnboarding = ({
3716
3846
  const missingJoinKey = !hostJoinIdentity;
3717
3847
  const autoJoinPossible = Boolean(storage) && autoJoinKey !== false;
3718
3848
  const wantsAutoJoin = missingJoinKey && autoJoinPossible;
3719
- const [autoJoinOutcome, setAutoJoinOutcome] = React18.useState(void 0);
3720
- React18.useEffect(() => {
3849
+ const [autoJoinOutcome, setAutoJoinOutcome] = React19.useState(void 0);
3850
+ React19.useEffect(() => {
3721
3851
  if (!wantsAutoJoin || autoJoinOutcome !== void 0 || !storage) return;
3722
3852
  let cancelled = false;
3723
3853
  const timer = setTimeout(() => {
@@ -3735,24 +3865,24 @@ var WireOnboarding = ({
3735
3865
  clearTimeout(timer);
3736
3866
  };
3737
3867
  }, [wantsAutoJoin, autoJoinOutcome, config.appId, storage]);
3738
- const injectedJoinKey = wantsAutoJoin ? (_a2 = autoJoinOutcome == null ? void 0 : autoJoinOutcome.key) != null ? _a2 : void 0 : void 0;
3868
+ const injectedJoinKey = wantsAutoJoin ? (_a = autoJoinOutcome == null ? void 0 : autoJoinOutcome.key) != null ? _a : void 0 : void 0;
3739
3869
  const autoJoinPending = wantsAutoJoin && autoJoinOutcome === void 0;
3740
- const effectiveUserContext = React18.useMemo(
3870
+ const effectiveUserContext = React19.useMemo(
3741
3871
  () => injectedJoinKey ? { ...userContextStable, ...activationJoinContext(injectedJoinKey) } : userContextStable,
3742
3872
  [userContextStable, injectedJoinKey]
3743
3873
  );
3744
- const frozenWireContextRef = React18.useRef(null);
3874
+ const frozenWireContextRef = React19.useRef(null);
3745
3875
  if (!autoJoinPending && frozenWireContextRef.current === null) {
3746
3876
  frozenWireContextRef.current = effectiveUserContext ? JSON.stringify(effectiveUserContext) : "";
3747
3877
  }
3748
3878
  const startupUserContextKey = (_b = frozenWireContextRef.current) != null ? _b : "";
3749
- const startupUserContext = React18.useMemo(
3879
+ const startupUserContext = React19.useMemo(
3750
3880
  () => startupUserContextKey ? JSON.parse(startupUserContextKey) : void 0,
3751
3881
  [startupUserContextKey]
3752
3882
  );
3753
3883
  const warnMissingJoinKey = missingJoinKey && !injectedJoinKey && !autoJoinPending;
3754
3884
  const autoJoinReason = autoJoinKey === false ? "you passed autoJoinKey={false}." : !storage ? "it got no `storage` prop, and without persistence its key would be different on every launch, which corrupts min_sessions instead of merely leaving the join empty." : (autoJoinOutcome == null ? void 0 : autoJoinOutcome.key) === null && autoJoinOutcome.reason === "not-durable" ? "your `storage` adapter answered but could not persist the key (the read or the write failed), so the key would be different on every launch, which corrupts min_sessions instead of merely leaving the join empty. Check the adapter you passed as `storage`." : "your `storage` adapter did not answer in time.";
3755
- React18.useEffect(() => {
3885
+ React19.useEffect(() => {
3756
3886
  if (!warnMissingJoinKey) return;
3757
3887
  warnInDev(
3758
3888
  "[wireai] <WireOnboarding> got no user_context.device_key, so this onboarding session can never be joined to the app's later events and the `activated` funnel will read zero. Pass userContext={activationJoinContext(deviceKey)} \u2014 and if your app owns no device id, userContext={activationJoinContext(resolveAutoDeviceKey({ appId, storage }))} returns the SAME id the analytics side stamps. Never hand-write userContext={{ deviceKey }}. The kit did NOT auto-inject its own key here because " + autoJoinReason
@@ -3760,28 +3890,29 @@ var WireOnboarding = ({
3760
3890
  }, [warnMissingJoinKey, autoJoinReason]);
3761
3891
  const hostKeyElsewhere = missingJoinKey ? hostIdentity("device", config.appId) : void 0;
3762
3892
  const warnHostKeyElsewhere = Boolean(hostKeyElsewhere) && Boolean(injectedJoinKey);
3763
- React18.useEffect(() => {
3893
+ React19.useEffect(() => {
3764
3894
  if (!warnHostKeyElsewhere) return;
3765
3895
  warnInDev(
3766
3896
  `[wireai] <WireOnboarding> got no user_context.device_key, so the kit injected its own \u2014 but another surface in this app was constructed WITH a host-supplied device key ("${hostKeyElsewhere}"). Your app events carry that id and this onboarding session carries the kit's, so the two land in disjoint id spaces and the \`activated\` funnel still reads zero. Pass the SAME id here: userContext={activationJoinContext(deviceKey)}.`
3767
3897
  );
3768
3898
  }, [warnHostKeyElsewhere, hostKeyElsewhere]);
3769
3899
  const misspelledJoinKey = missingJoinKey && typeof (userContextStable == null ? void 0 : userContextStable.deviceKey) === "string" && Boolean(userContextStable.deviceKey.trim());
3770
- React18.useEffect(() => {
3900
+ React19.useEffect(() => {
3771
3901
  if (!misspelledJoinKey || !injectedJoinKey) return;
3772
3902
  warnInDev(
3773
3903
  "[wireai] <WireOnboarding> got userContext={{ deviceKey }}, which is NOT the wire key \u2014 the server's device lookup reads `device_key`. The kit auto-injected its own device_key so this session is at least self-consistent, but your app's own events carry YOUR id, so the two still will not join and the `activated` funnel stays zero. Pass userContext={activationJoinContext(deviceKey)} instead."
3774
3904
  );
3775
3905
  }, [misspelledJoinKey, injectedJoinKey]);
3776
- const clientContext = React18.useMemo(
3906
+ const clientContext = React19.useMemo(
3777
3907
  () => ({ device, userContext: effectiveUserContext, userId: boundUserId }),
3778
3908
  [device, effectiveUserContext, boundUserId]
3779
3909
  );
3780
- const [session, setSession] = React18.useState(
3910
+ const [session, setSession] = React19.useState(
3781
3911
  () => storage ? null : { id: makeSessionId(), resumed: false }
3782
3912
  );
3783
3913
  const storageKey = persistKey != null ? persistKey : sessionStorageKey(config.appId);
3784
- React18.useEffect(() => {
3914
+ const permissionsKey = permissionStorageKey(config.appId);
3915
+ React19.useEffect(() => {
3785
3916
  if (!storage || session) return;
3786
3917
  let cancelled = false;
3787
3918
  void loadPersistedSession(storage, storageKey, sessionTtlMs).then((loaded) => {
@@ -3791,20 +3922,51 @@ var WireOnboarding = ({
3791
3922
  cancelled = true;
3792
3923
  };
3793
3924
  }, [storage, storageKey, sessionTtlMs, session]);
3794
- const handleComplete = React18.useCallback(
3925
+ const handleComplete = React19.useCallback(
3795
3926
  (result) => {
3796
- if (storage && shouldClearOnComplete(retainSessionOnComplete)) clearPersistedSession(storage, storageKey);
3927
+ if (storage && shouldClearOnComplete(retainSessionOnComplete)) {
3928
+ clearPersistedSession(storage, storageKey);
3929
+ clearSettledPermissions(storage, permissionsKey);
3930
+ }
3797
3931
  onComplete(result);
3798
3932
  },
3799
- [storage, storageKey, retainSessionOnComplete, onComplete]
3933
+ [storage, storageKey, permissionsKey, retainSessionOnComplete, onComplete]
3800
3934
  );
3801
3935
  const sessionId = (_c = session == null ? void 0 : session.id) != null ? _c : "";
3802
- const startupUserIdRef = React18.useRef(boundUserId);
3936
+ const wantsPermissionMemory = Boolean(storage) && ((_d = permissionScreens == null ? void 0 : permissionScreens.length) != null ? _d : 0) > 0;
3937
+ const [settledPermissions, setSettledPermissions] = React19.useState(void 0);
3938
+ React19.useEffect(() => {
3939
+ if (!wantsPermissionMemory || !storage || !sessionId || settledPermissions !== void 0) return;
3940
+ let cancelled = false;
3941
+ void loadSettledPermissions(storage, permissionsKey, sessionId).then((ids) => {
3942
+ if (!cancelled) setSettledPermissions(ids);
3943
+ });
3944
+ return () => {
3945
+ cancelled = true;
3946
+ };
3947
+ }, [wantsPermissionMemory, storage, permissionsKey, sessionId, settledPermissions]);
3948
+ const settledPermissionsRef = React19.useRef(void 0);
3949
+ settledPermissionsRef.current = settledPermissions;
3950
+ const handlePermissionSettled = React19.useCallback(
3951
+ (id) => {
3952
+ var _a2;
3953
+ const current = (_a2 = settledPermissionsRef.current) != null ? _a2 : [];
3954
+ if (current.includes(id)) return;
3955
+ const next = [...current, id];
3956
+ settledPermissionsRef.current = next;
3957
+ setSettledPermissions(next);
3958
+ if (storage && wantsPermissionMemory && sessionId) {
3959
+ saveSettledPermissions(storage, permissionsKey, sessionId, next);
3960
+ }
3961
+ },
3962
+ [storage, wantsPermissionMemory, permissionsKey, sessionId]
3963
+ );
3964
+ const startupUserIdRef = React19.useRef(boundUserId);
3803
3965
  const metadataKey = config.metadata ? JSON.stringify(config.metadata) : "";
3804
- const metadataStable = React18.useMemo(() => metadataKey ? JSON.parse(metadataKey) : void 0, [metadataKey]);
3966
+ const metadataStable = React19.useMemo(() => metadataKey ? JSON.parse(metadataKey) : void 0, [metadataKey]);
3805
3967
  const componentNamesKey = (components != null ? components : onboardingComponents).map((c) => c.name).join(",");
3806
- const componentsStable = React18.useMemo(() => components != null ? components : onboardingComponents, [componentNamesKey]);
3807
- const llm = React18.useMemo(() => {
3968
+ const componentsStable = React19.useMemo(() => components != null ? components : onboardingComponents, [componentNamesKey]);
3969
+ const llm = React19.useMemo(() => {
3808
3970
  const baseUrl = config.serverUrl.replace(/\/$/, "");
3809
3971
  return {
3810
3972
  provider: "a2a",
@@ -3834,13 +3996,13 @@ var WireOnboarding = ({
3834
3996
  timeoutMs: 6e4
3835
3997
  };
3836
3998
  }, [config.serverUrl, config.appId, config.apiKey, metadataStable, sessionId, componentsStable, device, startupUserContext]);
3837
- const reportTarget = React18.useMemo(
3999
+ const reportTarget = React19.useMemo(
3838
4000
  () => ({ serverUrl: config.serverUrl, apiKey: config.apiKey }),
3839
4001
  [config.serverUrl, config.apiKey]
3840
4002
  );
3841
- const identifiedRef = React18.useRef(void 0);
3842
- const identifySeededRef = React18.useRef(false);
3843
- React18.useEffect(() => {
4003
+ const identifiedRef = React19.useRef(void 0);
4004
+ const identifySeededRef = React19.useRef(false);
4005
+ React19.useEffect(() => {
3844
4006
  if (!session || !sessionId) return;
3845
4007
  if (!identifySeededRef.current) {
3846
4008
  identifySeededRef.current = true;
@@ -3859,8 +4021,8 @@ var WireOnboarding = ({
3859
4021
  return /* @__PURE__ */ jsxRuntime.jsx(OnboardingThemeProvider, { theme, children: /* @__PURE__ */ jsxRuntime.jsx(
3860
4022
  LoadingScreen,
3861
4023
  {
3862
- title: (_d = copy == null ? void 0 : copy.restoringTitle) != null ? _d : DEFAULT_COPY.restoringTitle,
3863
- hint: (_e = copy == null ? void 0 : copy.restoringHint) != null ? _e : DEFAULT_COPY.restoringHint
4024
+ title: (_e = copy == null ? void 0 : copy.restoringTitle) != null ? _e : DEFAULT_COPY.restoringTitle,
4025
+ hint: (_f = copy == null ? void 0 : copy.restoringHint) != null ? _f : DEFAULT_COPY.restoringHint
3864
4026
  }
3865
4027
  ) });
3866
4028
  }
@@ -3881,7 +4043,11 @@ var WireOnboarding = ({
3881
4043
  reportTarget,
3882
4044
  sessionId,
3883
4045
  resumed: session.resumed,
3884
- clientContext
4046
+ clientContext,
4047
+ permissionScreens,
4048
+ settledPermissions,
4049
+ permissionsPending: wantsPermissionMemory && settledPermissions === void 0,
4050
+ onPermissionSettled: handlePermissionSettled
3885
4051
  }
3886
4052
  ) }) }) }) });
3887
4053
  };
@@ -3895,16 +4061,16 @@ function DemoOnboarding({
3895
4061
  onComplete,
3896
4062
  renderTrigger
3897
4063
  }) {
3898
- const [open, setOpen] = React18.useState(false);
3899
- const [runId, setRunId] = React18.useState(0);
3900
- const t = React18.useMemo(() => mergeTheme(theme != null ? theme : {}), [theme]);
3901
- const styles17 = React18.useMemo(() => makeStyles5(t), [t]);
3902
- const openDemo = React18.useCallback(() => {
4064
+ const [open, setOpen] = React19.useState(false);
4065
+ const [runId, setRunId] = React19.useState(0);
4066
+ const t = React19.useMemo(() => mergeTheme(theme != null ? theme : {}), [theme]);
4067
+ const styles18 = React19.useMemo(() => makeStyles5(t), [t]);
4068
+ const openDemo = React19.useCallback(() => {
3903
4069
  setRunId((n) => n + 1);
3904
4070
  setOpen(true);
3905
4071
  }, []);
3906
- const close = React18.useCallback(() => setOpen(false), []);
3907
- const finish = React18.useCallback(
4072
+ const close = React19.useCallback(() => setOpen(false), []);
4073
+ const finish = React19.useCallback(
3908
4074
  (result) => {
3909
4075
  onComplete == null ? void 0 : onComplete(result);
3910
4076
  setOpen(false);
@@ -3912,7 +4078,7 @@ function DemoOnboarding({
3912
4078
  [onComplete]
3913
4079
  );
3914
4080
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3915
- renderTrigger ? renderTrigger(openDemo) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { onPress: openDemo, style: styles17.trigger, accessibilityRole: "button", children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles17.triggerText, children: label }) }),
4081
+ renderTrigger ? renderTrigger(openDemo) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { onPress: openDemo, style: styles18.trigger, accessibilityRole: "button", children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles18.triggerText, children: label }) }),
3916
4082
  /* @__PURE__ */ jsxRuntime.jsx(
3917
4083
  reactNative.Modal,
3918
4084
  {
@@ -3920,7 +4086,7 @@ function DemoOnboarding({
3920
4086
  animationType: "slide",
3921
4087
  presentationStyle: "fullScreen",
3922
4088
  onRequestClose: close,
3923
- children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles17.modal, children: config ? /* @__PURE__ */ jsxRuntime.jsx(
4089
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles18.modal, children: config ? /* @__PURE__ */ jsxRuntime.jsx(
3924
4090
  WireOnboarding,
3925
4091
  {
3926
4092
  config,
@@ -3933,10 +4099,10 @@ function DemoOnboarding({
3933
4099
  onError: close
3934
4100
  },
3935
4101
  runId
3936
- ) : /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles17.notice, children: [
3937
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles17.noticeTitle, children: "Wire AI not configured" }),
3938
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles17.noticeBody, children: "Set the Wire AI key + server URL (e.g. via wireConfigFromEnv) to demo the AI onboarding here." }),
3939
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { onPress: close, style: styles17.trigger, accessibilityRole: "button", children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles17.triggerText, children: "Close" }) })
4102
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles18.notice, children: [
4103
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles18.noticeTitle, children: "Wire AI not configured" }),
4104
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles18.noticeBody, children: "Set the Wire AI key + server URL (e.g. via wireConfigFromEnv) to demo the AI onboarding here." }),
4105
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { onPress: close, style: styles18.trigger, accessibilityRole: "button", children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles18.triggerText, children: "Close" }) })
3940
4106
  ] }) })
3941
4107
  }
3942
4108
  )
@@ -4022,13 +4188,13 @@ var CENTERED_MODAL_ANIM = {
4022
4188
  /** Peak dim of the backdrop at progress 1. */
4023
4189
  backdropOpacity: 0.55
4024
4190
  };
4025
- var _CenteredModal = React18.forwardRef(
4191
+ var _CenteredModal = React19.forwardRef(
4026
4192
  ({ theme, dismissible = true, onDismiss, children }, ref) => {
4027
- const progress = React18.useRef(new reactNative.Animated.Value(0)).current;
4028
- const [open, setOpen] = React18.useState(true);
4029
- const closingRef = React18.useRef(false);
4193
+ const progress = React19.useRef(new reactNative.Animated.Value(0)).current;
4194
+ const [open, setOpen] = React19.useState(true);
4195
+ const closingRef = React19.useRef(false);
4030
4196
  const reduced = useReducedMotion();
4031
- React18.useEffect(() => {
4197
+ React19.useEffect(() => {
4032
4198
  const anim = reduced ? reactNative.Animated.timing(progress, {
4033
4199
  toValue: 1,
4034
4200
  duration: REDUCED_FADE_MS,
@@ -4042,7 +4208,7 @@ var _CenteredModal = React18.forwardRef(
4042
4208
  anim.start();
4043
4209
  return () => anim.stop();
4044
4210
  }, [progress, reduced]);
4045
- const close = React18.useCallback(() => {
4211
+ const close = React19.useCallback(() => {
4046
4212
  if (closingRef.current) return;
4047
4213
  closingRef.current = true;
4048
4214
  reactNative.Animated.timing(progress, {
@@ -4055,15 +4221,15 @@ var _CenteredModal = React18.forwardRef(
4055
4221
  onDismiss();
4056
4222
  });
4057
4223
  }, [progress, onDismiss, reduced]);
4058
- React18.useImperativeHandle(ref, () => ({ close }), [close]);
4059
- React18.useEffect(() => {
4224
+ React19.useImperativeHandle(ref, () => ({ close }), [close]);
4225
+ React19.useEffect(() => {
4060
4226
  const sub = reactNative.BackHandler.addEventListener("hardwareBackPress", () => {
4061
4227
  if (dismissible) close();
4062
4228
  return true;
4063
4229
  });
4064
4230
  return () => sub.remove();
4065
4231
  }, [dismissible, close]);
4066
- const onBackdrop = React18.useCallback(() => {
4232
+ const onBackdrop = React19.useCallback(() => {
4067
4233
  if (dismissible) close();
4068
4234
  }, [dismissible, close]);
4069
4235
  const backdropOpacity = progress.interpolate({
@@ -4086,25 +4252,25 @@ var _CenteredModal = React18.forwardRef(
4086
4252
  /* @__PURE__ */ jsxRuntime.jsx(
4087
4253
  reactNative.Animated.View,
4088
4254
  {
4089
- style: [styles16.backdrop, { opacity: backdropOpacity }],
4255
+ style: [styles17.backdrop, { opacity: backdropOpacity }],
4090
4256
  pointerEvents: "none"
4091
4257
  }
4092
4258
  ),
4093
4259
  /* @__PURE__ */ jsxRuntime.jsx(
4094
4260
  reactNative.Pressable,
4095
4261
  {
4096
- style: styles16.fill,
4262
+ style: styles17.fill,
4097
4263
  onPress: onBackdrop,
4098
4264
  accessibilityRole: "button",
4099
4265
  accessibilityLabel: "Dismiss",
4100
- children: /* @__PURE__ */ jsxRuntime.jsx(reactNativeSafeAreaContext.SafeAreaView, { style: styles16.center, pointerEvents: "box-none", children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { onPress: () => {
4101
- }, style: styles16.cardWrap, children: /* @__PURE__ */ jsxRuntime.jsx(
4266
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactNativeSafeAreaContext.SafeAreaView, { style: styles17.center, pointerEvents: "box-none", children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Pressable, { onPress: () => {
4267
+ }, style: styles17.cardWrap, children: /* @__PURE__ */ jsxRuntime.jsx(
4102
4268
  reactNative.Animated.View,
4103
4269
  {
4104
4270
  accessibilityViewIsModal: true,
4105
4271
  accessibilityRole: "alert",
4106
4272
  style: [
4107
- styles16.card,
4273
+ styles17.card,
4108
4274
  {
4109
4275
  backgroundColor: theme.colors.surface,
4110
4276
  borderRadius: theme.radius.lg,
@@ -4125,8 +4291,8 @@ var _CenteredModal = React18.forwardRef(
4125
4291
  }
4126
4292
  );
4127
4293
  _CenteredModal.displayName = "CenteredModal";
4128
- var CenteredModal = React18__default.default.memo(_CenteredModal);
4129
- var styles16 = reactNative.StyleSheet.create({
4294
+ var CenteredModal = React19__default.default.memo(_CenteredModal);
4295
+ var styles17 = reactNative.StyleSheet.create({
4130
4296
  fill: { flex: 1 },
4131
4297
  backdrop: { position: "absolute", top: 0, left: 0, right: 0, bottom: 0, backgroundColor: "#000" },
4132
4298
  center: { flex: 1, alignItems: "center", justifyContent: "center", padding: 24 },
@@ -4159,10 +4325,10 @@ var readEnabled = (raw) => {
4159
4325
  return { enabled: true };
4160
4326
  };
4161
4327
  var parseWireFeatures = (raw) => {
4162
- var _a2;
4328
+ var _a;
4163
4329
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) return defaultWireFeatures;
4164
4330
  const src = raw;
4165
- const modeRaw = (_a2 = src.onboarding) == null ? void 0 : _a2.mode;
4331
+ const modeRaw = (_a = src.onboarding) == null ? void 0 : _a.mode;
4166
4332
  const mode = typeof modeRaw === "string" && VALID_MODES.includes(modeRaw) ? modeRaw : "ai";
4167
4333
  return {
4168
4334
  coachmarks: readEnabled(src.coachmarks),
@@ -4219,10 +4385,10 @@ var writeCachedFeatures = (storage, key, features) => {
4219
4385
  // src/features/fetchWireFeatures.ts
4220
4386
  var DEFAULT_TIMEOUT_MS = 4e3;
4221
4387
  var failOpen = async (storage, key) => {
4222
- var _a2;
4388
+ var _a;
4223
4389
  if (!storage) return defaultWireFeatures;
4224
4390
  const cached2 = await readCachedFeatures(storage, key);
4225
- return (_a2 = cached2 == null ? void 0 : cached2.features) != null ? _a2 : defaultWireFeatures;
4391
+ return (_a = cached2 == null ? void 0 : cached2.features) != null ? _a : defaultWireFeatures;
4226
4392
  };
4227
4393
  var fetchWithTimeout = async (url, apiKey, timeoutMs) => {
4228
4394
  const controller = typeof AbortController !== "undefined" ? new AbortController() : void 0;
@@ -4255,16 +4421,16 @@ var fetchWireFeatures = async (config) => {
4255
4421
  }
4256
4422
  };
4257
4423
  var useStableValue = (value, isEqual) => {
4258
- const ref = React18.useRef(value);
4424
+ const ref = React19.useRef(value);
4259
4425
  if (!isEqual(ref.current, value)) ref.current = value;
4260
4426
  return ref.current;
4261
4427
  };
4262
4428
  var sameFetchKey = (a, b) => (a == null ? void 0 : a.serverUrl) === (b == null ? void 0 : b.serverUrl) && (a == null ? void 0 : a.apiKey) === (b == null ? void 0 : b.apiKey) && (a == null ? void 0 : a.appId) === (b == null ? void 0 : b.appId);
4263
4429
  var useWireFeatures = (config) => {
4264
4430
  const stableConfig = useStableValue(config, sameFetchKey);
4265
- const [flags, setFlags] = React18.useState(defaultWireFeatures);
4431
+ const [flags, setFlags] = React19.useState(defaultWireFeatures);
4266
4432
  const canFetch = !!(stableConfig == null ? void 0 : stableConfig.serverUrl) && !!(stableConfig == null ? void 0 : stableConfig.apiKey);
4267
- React18.useEffect(() => {
4433
+ React19.useEffect(() => {
4268
4434
  if (!canFetch) {
4269
4435
  setFlags((prev) => featuresEqual(prev, defaultWireFeatures) ? prev : defaultWireFeatures);
4270
4436
  return;
@@ -4281,9 +4447,9 @@ var useWireFeatures = (config) => {
4281
4447
  return flags;
4282
4448
  };
4283
4449
  var CONTEXT_SYMBOL = /* @__PURE__ */ Symbol.for("wireai.features.context");
4284
- var globalObj = global;
4450
+ var globalObj = globalThis;
4285
4451
  if (!globalObj[CONTEXT_SYMBOL]) {
4286
- globalObj[CONTEXT_SYMBOL] = React18.createContext(null);
4452
+ globalObj[CONTEXT_SYMBOL] = React19.createContext(null);
4287
4453
  }
4288
4454
  var WireFeaturesContext = globalObj[CONTEXT_SYMBOL];
4289
4455
  var WireFeaturesProvider = ({
@@ -4293,17 +4459,17 @@ var WireFeaturesProvider = ({
4293
4459
  }) => {
4294
4460
  const fetched = useWireFeatures(flags ? void 0 : config);
4295
4461
  const value = flags != null ? flags : fetched;
4296
- const stable = React18.useMemo(() => value, [value]);
4462
+ const stable = React19.useMemo(() => value, [value]);
4297
4463
  return /* @__PURE__ */ jsxRuntime.jsx(WireFeaturesContext.Provider, { value: stable, children });
4298
4464
  };
4299
4465
  WireFeaturesProvider.displayName = "WireFeaturesProvider";
4300
- var useWireFeaturesContext = () => React18.useContext(WireFeaturesContext);
4466
+ var useWireFeaturesContext = () => React19.useContext(WireFeaturesContext);
4301
4467
  var useResolvedFeatures = (options) => {
4302
- var _a2, _b, _c;
4468
+ var _a, _b, _c;
4303
4469
  const ctx = useWireFeaturesContext();
4304
4470
  const shouldFetch = !(options == null ? void 0 : options.flags) && ctx == null;
4305
4471
  const fetched = useWireFeatures(shouldFetch ? options == null ? void 0 : options.config : void 0);
4306
- return (_c = (_b = (_a2 = options == null ? void 0 : options.flags) != null ? _a2 : ctx) != null ? _b : fetched) != null ? _c : defaultWireFeatures;
4472
+ return (_c = (_b = (_a = options == null ? void 0 : options.flags) != null ? _a : ctx) != null ? _b : fetched) != null ? _c : defaultWireFeatures;
4307
4473
  };
4308
4474
 
4309
4475
  // src/config/wireConfigFromEnv.ts
@@ -4313,8 +4479,8 @@ var WIRE_ENV_VARS = [
4313
4479
  "EXPO_PUBLIC_WIREAI_APP_ID"
4314
4480
  ];
4315
4481
  var wireConfigFromEnv = (overrides) => {
4316
- var _a2, _b, _c, _d, _e;
4317
- const apiKey = (_a2 = overrides == null ? void 0 : overrides.apiKey) != null ? _a2 : process.env.EXPO_PUBLIC_WIREAI_API_KEY;
4482
+ var _a, _b, _c, _d, _e;
4483
+ const apiKey = (_a = overrides == null ? void 0 : overrides.apiKey) != null ? _a : process.env.EXPO_PUBLIC_WIREAI_API_KEY;
4318
4484
  const serverUrl = (_b = overrides == null ? void 0 : overrides.serverUrl) != null ? _b : process.env.EXPO_PUBLIC_WIREAI_SERVER_URL;
4319
4485
  const appId = (_d = (_c = overrides == null ? void 0 : overrides.appId) != null ? _c : process.env.EXPO_PUBLIC_WIREAI_APP_ID) != null ? _d : "default";
4320
4486
  if (!apiKey || !serverUrl) {
@@ -4339,10 +4505,10 @@ var wireConfigFromEnv = (overrides) => {
4339
4505
 
4340
4506
  // src/config/onboardingFlag.ts
4341
4507
  var isOnboardingEnabled = (opts) => {
4342
- var _a2;
4508
+ var _a;
4343
4509
  const transportPresent = wireConfigFromEnv(opts == null ? void 0 : opts.config) !== null;
4344
4510
  if (!transportPresent) return false;
4345
- return (_a2 = opts == null ? void 0 : opts.remote) != null ? _a2 : true;
4511
+ return (_a = opts == null ? void 0 : opts.remote) != null ? _a : true;
4346
4512
  };
4347
4513
 
4348
4514
  // src/analytics/analyticsEvent.ts
@@ -4377,6 +4543,11 @@ var toAnalyticsEvent = (event) => {
4377
4543
  };
4378
4544
  case "fallback":
4379
4545
  return { name: WIRE_ONBOARDING_EVENTS.fallback, params: { reason: event.reason } };
4546
+ case "permission":
4547
+ return {
4548
+ name: permissionEventName(event.stage),
4549
+ params: permissionEventProps(event.permission, event.status)
4550
+ };
4380
4551
  default: {
4381
4552
  const _exhaustive = event;
4382
4553
  return _exhaustive;
@@ -4432,9 +4603,9 @@ var resetActivationRevalidation = () => {
4432
4603
  // src/activation/wireActivation.ts
4433
4604
  var clean = cleanString;
4434
4605
  var createWireActivation = (config) => {
4435
- var _a2, _b;
4606
+ var _a, _b;
4436
4607
  const target = { serverUrl: config.serverUrl, apiKey: config.apiKey };
4437
- const explicitDeviceKey = (_b = clean(config.deviceKey)) != null ? _b : clean((_a2 = config.userContext) == null ? void 0 : _a2.deviceKey);
4608
+ const explicitDeviceKey = (_b = clean(config.deviceKey)) != null ? _b : clean((_a = config.userContext) == null ? void 0 : _a.deviceKey);
4438
4609
  resolveIdentity({
4439
4610
  value: explicitDeviceKey,
4440
4611
  space: "device",
@@ -4449,11 +4620,11 @@ var createWireActivation = (config) => {
4449
4620
  if (!explicitDeviceKey) resolveAutoDeviceKey(autoDeviceKeyOptions);
4450
4621
  const detectedAppVersion = detectAppVersion();
4451
4622
  const applyContext = (event) => {
4452
- var _a3, _b2, _c;
4623
+ var _a2, _b2, _c;
4453
4624
  const resolved = resolveUserContext(
4454
4625
  // `??` is lazy on purpose: an explicit key must never even touch the auto registry.
4455
4626
  {
4456
- ...(_a3 = config.userContext) != null ? _a3 : {},
4627
+ ...(_a2 = config.userContext) != null ? _a2 : {},
4457
4628
  deviceKey: explicitDeviceKey != null ? explicitDeviceKey : resolveAutoDeviceKey(autoDeviceKeyOptions)
4458
4629
  },
4459
4630
  { autoAppVersion: (_b2 = config.appVersion) != null ? _b2 : detectedAppVersion }
@@ -4463,13 +4634,13 @@ var createWireActivation = (config) => {
4463
4634
  }
4464
4635
  if (resolved.userId && !event.user_id) event.user_id = resolved.userId;
4465
4636
  };
4466
- const track = async (name2, meta) => {
4467
- if (!clean(name2)) return false;
4637
+ const track = async (name, meta) => {
4638
+ if (!clean(name)) return false;
4468
4639
  const sessionId = ensureCurrentSessionId();
4469
4640
  const event = {
4470
4641
  event_type: "app_event",
4471
4642
  session_id: sessionId,
4472
- question_key: name2
4643
+ question_key: name
4473
4644
  };
4474
4645
  if (meta && Object.keys(meta).length > 0) event.meta = JSON.stringify(meta);
4475
4646
  applyContext(event);
@@ -4486,21 +4657,21 @@ var createWireActivation = (config) => {
4486
4657
  getRevalidationVersion: getActivationRevalidationVersion
4487
4658
  };
4488
4659
  };
4489
- var useActivationRevalidation = () => React18.useSyncExternalStore(
4660
+ var useActivationRevalidation = () => React19.useSyncExternalStore(
4490
4661
  subscribeActivationRevalidation,
4491
4662
  getActivationRevalidationVersion,
4492
4663
  getActivationRevalidationVersion
4493
4664
  );
4494
4665
  var useWireActivation = (config) => {
4495
- var _a2;
4496
- const ref = React18.useRef(void 0);
4497
- const prevKeys = React18.useRef("");
4666
+ var _a;
4667
+ const ref = React19.useRef(void 0);
4668
+ const prevKeys = React19.useRef("");
4498
4669
  const currentKeys = [
4499
4670
  config.serverUrl,
4500
4671
  config.apiKey,
4501
4672
  config.appId,
4502
4673
  config.deviceKey,
4503
- (_a2 = config.userContext) == null ? void 0 : _a2.deviceKey
4674
+ (_a = config.userContext) == null ? void 0 : _a.deviceKey
4504
4675
  ].join("|");
4505
4676
  if (!ref.current || prevKeys.current !== currentKeys) {
4506
4677
  prevKeys.current = currentKeys;
@@ -4526,8 +4697,8 @@ var WIRE_PURCHASE_EVENTS = {
4526
4697
  var PLAN_TIER_CONTEXT_KEY = "plan_tier";
4527
4698
  var asRecord = (value) => typeof value === "object" && value !== null ? value : void 0;
4528
4699
  var activeEntitlement = (info, entitlementId) => {
4529
- var _a2, _b;
4530
- const active = asRecord((_b = asRecord((_a2 = asRecord(info)) == null ? void 0 : _a2.entitlements)) == null ? void 0 : _b.active);
4700
+ var _a, _b;
4701
+ const active = asRecord((_b = asRecord((_a = asRecord(info)) == null ? void 0 : _a.entitlements)) == null ? void 0 : _b.active);
4531
4702
  const found = active == null ? void 0 : active[entitlementId];
4532
4703
  const entitlement = asRecord(found);
4533
4704
  if (!entitlement) return void 0;
@@ -4600,19 +4771,19 @@ var createRevenueCatBridge = (config) => {
4600
4771
  }
4601
4772
  };
4602
4773
  const syncPlanTier = (customerInfo) => {
4603
- var _a2;
4774
+ var _a;
4604
4775
  const tier = resolvePlanTier(customerInfo, entitlementId);
4605
4776
  if (!writeTier) return tier;
4606
4777
  try {
4607
- (_a2 = analytics.setUserContext) == null ? void 0 : _a2.call(analytics, { extra: { [PLAN_TIER_CONTEXT_KEY]: tier } });
4778
+ (_a = analytics.setUserContext) == null ? void 0 : _a.call(analytics, { extra: { [PLAN_TIER_CONTEXT_KEY]: tier } });
4608
4779
  } catch {
4609
4780
  }
4610
4781
  return tier;
4611
4782
  };
4612
4783
  const paywallShown = (offering, props) => {
4613
- var _a2, _b;
4784
+ var _a, _b;
4614
4785
  const shown = {
4615
- packages_count: (_b = (_a2 = offering == null ? void 0 : offering.availablePackages) == null ? void 0 : _a2.length) != null ? _b : 0
4786
+ packages_count: (_b = (_a = offering == null ? void 0 : offering.availablePackages) == null ? void 0 : _a.length) != null ? _b : 0
4616
4787
  };
4617
4788
  if (typeof (offering == null ? void 0 : offering.identifier) === "string") shown.offering_id = offering.identifier;
4618
4789
  emit(WIRE_PURCHASE_EVENTS.paywallShown, { ...shown, ...props });
@@ -4666,10 +4837,10 @@ var resetSessionStartGuard = () => {
4666
4837
  _emitted.clear();
4667
4838
  };
4668
4839
  var reportSessionStart = (opts) => {
4669
- var _a2;
4840
+ var _a;
4670
4841
  const target = opts.target;
4671
4842
  if (!opts.sink && !(target == null ? void 0 : target.serverUrl)) return;
4672
- const sessionId = (_a2 = opts.sessionId) != null ? _a2 : makeSessionId();
4843
+ const sessionId = (_a = opts.sessionId) != null ? _a : makeSessionId();
4673
4844
  setCurrentSessionId(sessionId);
4674
4845
  if (opts.once !== false) {
4675
4846
  if (_emitted.has(sessionId)) return;
@@ -4719,9 +4890,9 @@ var reportSessionStart = (opts) => {
4719
4890
  };
4720
4891
  var BACKGROUND_SESSION_MS = 30 * 60 * 1e3;
4721
4892
  var useSessionStart = (config, options = {}) => {
4722
- const latest = React18.useRef({ config, options });
4893
+ const latest = React19.useRef({ config, options });
4723
4894
  latest.current = { config, options };
4724
- React18.useEffect(() => {
4895
+ React19.useEffect(() => {
4725
4896
  const resolveDeviceKey = (cfg, opts) => {
4726
4897
  const host = typeof opts.deviceKey === "string" && opts.deviceKey.trim() ? opts.deviceKey : void 0;
4727
4898
  if (host) return host;
@@ -4729,11 +4900,11 @@ var useSessionStart = (config, options = {}) => {
4729
4900
  return resolveAutoDeviceKey({ appId: cfg.appId, storage: cfg.storage });
4730
4901
  };
4731
4902
  const fire = () => {
4732
- var _a2, _b;
4903
+ var _a, _b;
4733
4904
  const { config: cfg, options: opts } = latest.current;
4734
4905
  if (!(cfg == null ? void 0 : cfg.serverUrl)) return;
4735
4906
  if (opts.enabled === false) return;
4736
- const target = { serverUrl: cfg.serverUrl, apiKey: (_a2 = cfg.apiKey) != null ? _a2 : "" };
4907
+ const target = { serverUrl: cfg.serverUrl, apiKey: (_a = cfg.apiKey) != null ? _a : "" };
4737
4908
  const device = collectDeviceContext();
4738
4909
  if (cfg.appVersion) device.appVersion = cfg.appVersion;
4739
4910
  reportSessionStart({
@@ -4784,7 +4955,7 @@ var resetFirstOpenLatch = () => {
4784
4955
  _firstOpenLatched.clear();
4785
4956
  };
4786
4957
  var buildLifecycleEvent = (questionKey, opts) => {
4787
- var _a2;
4958
+ var _a;
4788
4959
  const userContext = {};
4789
4960
  if (opts.deviceKey) userContext.device_key = opts.deviceKey;
4790
4961
  if (typeof opts.sessionCount === "number" && Number.isFinite(opts.sessionCount)) {
@@ -4796,7 +4967,7 @@ var buildLifecycleEvent = (questionKey, opts) => {
4796
4967
  const event = {
4797
4968
  event_type: "app_event",
4798
4969
  question_key: questionKey,
4799
- session_id: (_a2 = opts.sessionId) != null ? _a2 : makeSessionId()
4970
+ session_id: (_a = opts.sessionId) != null ? _a : makeSessionId()
4800
4971
  };
4801
4972
  const userId = sanitizeUserId(opts.userId);
4802
4973
  if (userId) event.user_id = userId;
@@ -4822,8 +4993,8 @@ var emitFirstOpen = (opts) => {
4822
4993
  routeLifecycleEvent(buildLifecycleEvent(FIRST_OPEN_EVENT, opts), opts);
4823
4994
  };
4824
4995
  var reportFirstOpen = (opts) => {
4825
- var _a2;
4826
- const appId = (_a2 = opts.appId) != null ? _a2 : "default";
4996
+ var _a;
4997
+ const appId = (_a = opts.appId) != null ? _a : "default";
4827
4998
  if (_firstOpenLatched.has(appId)) return;
4828
4999
  _firstOpenLatched.add(appId);
4829
5000
  const storage = opts.storage;
@@ -4848,8 +5019,8 @@ var reportFirstOpen = (opts) => {
4848
5019
  })();
4849
5020
  };
4850
5021
  var wireLifecycleEvents = (opts) => {
4851
- var _a2;
4852
- const sessionId = (_a2 = opts.sessionId) != null ? _a2 : makeSessionId();
5022
+ var _a;
5023
+ const sessionId = (_a = opts.sessionId) != null ? _a : makeSessionId();
4853
5024
  reportFirstOpen({ ...opts, sessionId });
4854
5025
  reportSessionStart({
4855
5026
  target: opts.target,
@@ -4898,10 +5069,11 @@ var claimQueueKey = (preferred, explicit) => {
4898
5069
  );
4899
5070
  return key;
4900
5071
  };
5072
+ var READ_TIMED_OUT = /* @__PURE__ */ Symbol("wireai:storage-read-timeout");
4901
5073
  var withTimeout4 = (p, ms) => {
4902
5074
  let timer;
4903
5075
  const timeout = new Promise((resolve) => {
4904
- timer = setTimeout(() => resolve(void 0), ms);
5076
+ timer = setTimeout(() => resolve(READ_TIMED_OUT), ms);
4905
5077
  });
4906
5078
  return Promise.race([p, timeout]).finally(() => clearTimeout(timer));
4907
5079
  };
@@ -4926,10 +5098,10 @@ var parsePersisted2 = (raw) => {
4926
5098
  }
4927
5099
  };
4928
5100
  var createEventQueue = (options) => {
4929
- var _a2, _b, _c, _d, _e, _f, _g;
5101
+ var _a, _b, _c, _d, _e, _f, _g;
4930
5102
  const target = options.target;
4931
5103
  const storage = options.storage;
4932
- const defaultKey = (_b = options.storageKey) != null ? _b : `wireai:evtq:${(_a2 = options.appId) != null ? _a2 : "default"}`;
5104
+ const defaultKey = (_b = options.storageKey) != null ? _b : `wireai:evtq:${(_a = options.appId) != null ? _a : "default"}`;
4933
5105
  const key = storage ? claimQueueKey(defaultKey, options.storageKey !== void 0) : defaultKey;
4934
5106
  const maxSize = (_c = options.maxSize) != null ? _c : DEFAULTS.maxSize;
4935
5107
  const batchSize = (_d = options.batchSize) != null ? _d : DEFAULTS.batchSize;
@@ -4941,6 +5113,7 @@ var createEventQueue = (options) => {
4941
5113
  let flushing = false;
4942
5114
  let attempt = 0;
4943
5115
  let retryTimer;
5116
+ let backlogUnread = false;
4944
5117
  const resolveEnvelope = () => {
4945
5118
  try {
4946
5119
  return typeof options.envelope === "function" ? options.envelope() : options.envelope;
@@ -4949,14 +5122,14 @@ var createEventQueue = (options) => {
4949
5122
  }
4950
5123
  };
4951
5124
  const stamp = (event) => {
4952
- var _a3;
5125
+ var _a2;
4953
5126
  const env = resolveEnvelope();
4954
5127
  const stamped = { ...event };
4955
5128
  if (stamped.ts === void 0) stamped.ts = Date.now();
4956
5129
  if (!env) return stamped;
4957
5130
  if (!stamped.device && env.device) stamped.device = env.device;
4958
5131
  if (!stamped.session_id && env.sessionId) stamped.session_id = env.sessionId;
4959
- const uc = { ...(_a3 = stamped.user_context) != null ? _a3 : {} };
5132
+ const uc = { ...(_a2 = stamped.user_context) != null ? _a2 : {} };
4960
5133
  if (env.appVersion && uc.app_version === void 0) uc.app_version = env.appVersion;
4961
5134
  if (env.appBuild && uc.app_build === void 0) uc.app_build = env.appBuild;
4962
5135
  if (env.networkType && uc.network_type === void 0) uc.network_type = env.networkType;
@@ -4965,6 +5138,7 @@ var createEventQueue = (options) => {
4965
5138
  };
4966
5139
  const persist = () => {
4967
5140
  if (!storage) return;
5141
+ if (backlogUnread) return;
4968
5142
  try {
4969
5143
  if (pending.length === 0) {
4970
5144
  void storage.removeItem(key).catch(() => {
@@ -4987,23 +5161,44 @@ var createEventQueue = (options) => {
4987
5161
  return `__nosig_${nextId}_${Math.random()}`;
4988
5162
  }
4989
5163
  };
5164
+ const mergePersisted = (persistedItems) => {
5165
+ if (persistedItems.length === 0) return;
5166
+ const seen = new Set(pending.map((item) => item.sig));
5167
+ const restored = [];
5168
+ for (const persisted of persistedItems) {
5169
+ const sig = safeSig(persisted.event);
5170
+ if (seen.has(sig)) continue;
5171
+ seen.add(sig);
5172
+ restored.push({ id: nextId++, event: persisted.event, sig });
5173
+ }
5174
+ if (restored.length === 0) return;
5175
+ pending = [...restored, ...pending];
5176
+ enforceSizeCap();
5177
+ persist();
5178
+ };
4990
5179
  const loadPromise = (async () => {
4991
5180
  if (!storage) return;
4992
5181
  try {
4993
- const persistedItems = parsePersisted2(await withTimeout4(storage.getItem(key), READ_TIMEOUT_MS4));
4994
- if (persistedItems.length === 0) return;
4995
- const events = [...persistedItems.map((p) => p.event), ...pending.map((p) => p.event)];
4996
- pending = [];
4997
- nextId = 0;
4998
- const seen = /* @__PURE__ */ new Set();
4999
- for (const event of events) {
5000
- const sig = safeSig(event);
5001
- if (seen.has(sig)) continue;
5002
- seen.add(sig);
5003
- pending.push({ id: nextId++, event, sig });
5182
+ const read = Promise.resolve(storage.getItem(key));
5183
+ read.catch(() => {
5184
+ });
5185
+ const raced = await withTimeout4(read, READ_TIMEOUT_MS4);
5186
+ if (raced === READ_TIMED_OUT) {
5187
+ backlogUnread = true;
5188
+ warnInDev(
5189
+ `[wireai] the persisted analytics backlog at "${key}" took longer than ${READ_TIMEOUT_MS4}ms to read, so the queue started without it. The stored events are NOT discarded: writes are held back until the read lands, and the backlog is merged in then. If you see this on every cold start, your storage adapter is too slow to be on the launch path.`
5190
+ );
5191
+ void read.then((late) => {
5192
+ backlogUnread = false;
5193
+ mergePersisted(parsePersisted2(late));
5194
+ flush();
5195
+ }).catch(() => {
5196
+ backlogUnread = false;
5197
+ persist();
5198
+ });
5199
+ return;
5004
5200
  }
5005
- enforceSizeCap();
5006
- persist();
5201
+ mergePersisted(parsePersisted2(raced));
5007
5202
  } catch {
5008
5203
  }
5009
5204
  })();
@@ -5095,18 +5290,18 @@ var createEventQueue = (options) => {
5095
5290
 
5096
5291
  // src/session-analytics/useLifecycleEvents.ts
5097
5292
  var useLifecycleEvents = (config, options = {}) => {
5098
- const latest = React18.useRef({ config, options });
5293
+ const latest = React19.useRef({ config, options });
5099
5294
  latest.current = { config, options };
5100
- const queueRef = React18.useRef(void 0);
5101
- React18.useEffect(() => {
5295
+ const queueRef = React19.useRef(void 0);
5296
+ React19.useEffect(() => {
5102
5297
  const resolveSink = () => {
5103
- var _a2, _b;
5298
+ var _a, _b;
5104
5299
  const { config: cfg, options: opts } = latest.current;
5105
5300
  if (opts.sink) return opts.sink;
5106
5301
  if (!(cfg == null ? void 0 : cfg.serverUrl)) return void 0;
5107
5302
  if (!queueRef.current) {
5108
5303
  queueRef.current = createEventQueue({
5109
- target: { serverUrl: cfg.serverUrl, apiKey: (_a2 = cfg.apiKey) != null ? _a2 : "" },
5304
+ target: { serverUrl: cfg.serverUrl, apiKey: (_a = cfg.apiKey) != null ? _a : "" },
5110
5305
  storage: cfg.storage,
5111
5306
  // Dedicated key so the hook's internal queue never collides with a host's main queue.
5112
5307
  storageKey: `wireai:evtq:lifecycle:${(_b = cfg.appId) != null ? _b : "default"}`,
@@ -5116,24 +5311,24 @@ var useLifecycleEvents = (config, options = {}) => {
5116
5311
  return queueRef.current.enqueue;
5117
5312
  };
5118
5313
  const targetOf = (cfg) => {
5119
- var _a2;
5120
- return (cfg == null ? void 0 : cfg.serverUrl) ? { serverUrl: cfg.serverUrl, apiKey: (_a2 = cfg.apiKey) != null ? _a2 : "" } : void 0;
5314
+ var _a;
5315
+ return (cfg == null ? void 0 : cfg.serverUrl) ? { serverUrl: cfg.serverUrl, apiKey: (_a = cfg.apiKey) != null ? _a : "" } : void 0;
5121
5316
  };
5122
5317
  const resolveDeviceKey = (cfg, opts) => {
5123
- var _a2;
5124
- const host = (_a2 = resolveIdentity({
5318
+ var _a;
5319
+ const host = (_a = resolveIdentity({
5125
5320
  value: opts.deviceKey,
5126
5321
  space: "device",
5127
5322
  source: "host",
5128
5323
  scope: cfg == null ? void 0 : cfg.appId
5129
- })) == null ? void 0 : _a2.value;
5324
+ })) == null ? void 0 : _a.value;
5130
5325
  if (host) return host;
5131
5326
  if (!(cfg == null ? void 0 : cfg.storage)) return void 0;
5132
5327
  return resolveAutoDeviceKey({ appId: cfg.appId, storage: cfg.storage });
5133
5328
  };
5134
5329
  const mountOpenSessionId = makeSessionId();
5135
5330
  const fireSession = (sessionId) => {
5136
- var _a2;
5331
+ var _a;
5137
5332
  const { config: cfg, options: opts } = latest.current;
5138
5333
  if (opts.enabled === false) return;
5139
5334
  if (!(cfg == null ? void 0 : cfg.serverUrl) && !opts.sink) return;
@@ -5146,14 +5341,14 @@ var useLifecycleEvents = (config, options = {}) => {
5146
5341
  userId: opts.userId,
5147
5342
  deviceKey: resolveDeviceKey(cfg, opts),
5148
5343
  sessionCount: opts.sessionCount,
5149
- appVersion: (_a2 = cfg == null ? void 0 : cfg.appVersion) != null ? _a2 : device.appVersion,
5344
+ appVersion: (_a = cfg == null ? void 0 : cfg.appVersion) != null ? _a : device.appVersion,
5150
5345
  platform: reactNative.Platform.OS,
5151
5346
  device,
5152
5347
  meta: opts.meta
5153
5348
  });
5154
5349
  };
5155
5350
  const fireMountOpen = () => {
5156
- var _a2;
5351
+ var _a;
5157
5352
  fireSession(mountOpenSessionId);
5158
5353
  const { config: cfg, options: opts } = latest.current;
5159
5354
  if (opts.enabled === false) return;
@@ -5168,7 +5363,7 @@ var useLifecycleEvents = (config, options = {}) => {
5168
5363
  userId: opts.userId,
5169
5364
  deviceKey: resolveDeviceKey(cfg, opts),
5170
5365
  sessionCount: opts.sessionCount,
5171
- appVersion: (_a2 = cfg == null ? void 0 : cfg.appVersion) != null ? _a2 : device.appVersion,
5366
+ appVersion: (_a = cfg == null ? void 0 : cfg.appVersion) != null ? _a : device.appVersion,
5172
5367
  platform: reactNative.Platform.OS,
5173
5368
  device,
5174
5369
  meta: opts.meta
@@ -5213,22 +5408,29 @@ exports.CenteredModal = CenteredModal;
5213
5408
  exports.ChipSelectCard = ChipSelectCard;
5214
5409
  exports.CompletionView = CompletionView;
5215
5410
  exports.DEFAULT_FEATURES_TTL_MS = DEFAULT_FEATURES_TTL_MS;
5411
+ exports.DEFAULT_PERMISSION_COPY = DEFAULT_PERMISSION_COPY;
5412
+ exports.DEFAULT_PERMISSION_PLACEMENT = DEFAULT_PERMISSION_PLACEMENT;
5216
5413
  exports.DEFAULT_SESSION_TTL_MS = DEFAULT_SESSION_TTL_MS;
5217
5414
  exports.DemoOnboarding = DemoOnboarding;
5218
5415
  exports.EXTRA_KEY_PREFIX = EXTRA_KEY_PREFIX;
5219
5416
  exports.ErrorBlock = ErrorBlock;
5220
5417
  exports.FIRST_OPEN_EVENT = FIRST_OPEN_EVENT;
5418
+ exports.GENERIC_PERMISSION_COPY = GENERIC_PERMISSION_COPY;
5221
5419
  exports.IconRegistryProvider = IconRegistryProvider;
5222
5420
  exports.IllustrationProvider = IllustrationProvider;
5223
5421
  exports.InterstitialCard = InterstitialCard;
5224
5422
  exports.LoadingBlock = LoadingBlock;
5225
5423
  exports.LoadingScreen = LoadingScreen;
5424
+ exports.NOTIFICATIONS_PERMISSION_COPY = NOTIFICATIONS_PERMISSION_COPY;
5226
5425
  exports.NumberStepperCard = NumberStepperCard;
5227
5426
  exports.OnboardingButton = Button;
5228
5427
  exports.OnboardingFlow = OnboardingFlow;
5229
5428
  exports.OnboardingScaffold = OnboardingScaffold;
5230
5429
  exports.OnboardingThemeProvider = OnboardingThemeProvider;
5430
+ exports.PERMISSION_CARD_NAME = PERMISSION_CARD_NAME;
5231
5431
  exports.PLAN_TIER_CONTEXT_KEY = PLAN_TIER_CONTEXT_KEY;
5432
+ exports.PermissionCard = PermissionCard;
5433
+ exports.PermissionCardView = PermissionCardView;
5232
5434
  exports.SESSION_STARTED_EVENT = SESSION_STARTED_EVENT;
5233
5435
  exports.SelectionCard = SelectionCard;
5234
5436
  exports.StatusCard = StatusCard;
@@ -5239,6 +5441,7 @@ exports.WIRE_ENV_VARS = WIRE_ENV_VARS;
5239
5441
  exports.WIRE_ICON_GLYPHS = WIRE_ICON_GLYPHS;
5240
5442
  exports.WIRE_ICON_NAMES = WIRE_ICON_NAMES;
5241
5443
  exports.WIRE_ONBOARDING_EVENTS = WIRE_ONBOARDING_EVENTS;
5444
+ exports.WIRE_PERMISSION_EVENTS = WIRE_PERMISSION_EVENTS;
5242
5445
  exports.WIRE_PURCHASE_EVENTS = WIRE_PURCHASE_EVENTS;
5243
5446
  exports.WireFeaturesProvider = WireFeaturesProvider;
5244
5447
  exports.WireIcon = WireIcon;
@@ -5250,6 +5453,7 @@ exports.attributionMetadata = attributionMetadata;
5250
5453
  exports.bumpActivationRevalidation = bumpActivationRevalidation;
5251
5454
  exports.clearPersistedSession = clearPersistedSession;
5252
5455
  exports.clearPiiFromContext = clearPiiFromContext;
5456
+ exports.clearSettledPermissions = clearSettledPermissions;
5253
5457
  exports.clearUserContext = clearUserContext;
5254
5458
  exports.collectDeviceContext = collectDeviceContext;
5255
5459
  exports.createRevenueCatBridge = createRevenueCatBridge;
@@ -5278,9 +5482,11 @@ exports.hydrateDeviceIdentity = hydrateDeviceIdentity;
5278
5482
  exports.identifyOnboarding = identifyOnboarding;
5279
5483
  exports.isFeaturesFresh = isFeaturesFresh;
5280
5484
  exports.isOnboardingEnabled = isOnboardingEnabled;
5485
+ exports.isPermissionDue = isPermissionDue;
5281
5486
  exports.isUserCancelled = isUserCancelled;
5282
5487
  exports.isWireScalar = isWireScalar;
5283
5488
  exports.loadPersistedSession = loadPersistedSession;
5489
+ exports.loadSettledPermissions = loadSettledPermissions;
5284
5490
  exports.looksLikeEmail = looksLikeEmail;
5285
5491
  exports.lookupIconGlyph = lookupIconGlyph;
5286
5492
  exports.makeSessionId = makeSessionId;
@@ -5288,11 +5494,18 @@ exports.mergeTheme = mergeTheme;
5288
5494
  exports.mintDeviceId = mintDeviceId;
5289
5495
  exports.motionSpec = motionSpec_exports;
5290
5496
  exports.namespaceExtra = namespaceExtra;
5497
+ exports.normalizeAfterCard = normalizeAfterCard;
5498
+ exports.normalizePermissionStatus = normalizePermissionStatus;
5291
5499
  exports.onboardingComponents = onboardingComponents;
5292
5500
  exports.parseWireFeatures = parseWireFeatures;
5293
5501
  exports.peekPersistedSession = peekPersistedSession;
5502
+ exports.permissionEventName = permissionEventName;
5503
+ exports.permissionEventProps = permissionEventProps;
5504
+ exports.permissionScreenId = permissionScreenId;
5505
+ exports.permissionStorageKey = permissionStorageKey;
5294
5506
  exports.readCachedFeatures = readCachedFeatures;
5295
5507
  exports.readProgress = readProgress;
5508
+ exports.readSettledPermissions = readSettledPermissions;
5296
5509
  exports.reportClientEvent = reportClientEvent;
5297
5510
  exports.reportClientEventAwait = reportClientEventAwait;
5298
5511
  exports.reportClientEvents = reportClientEvents;
@@ -5307,10 +5520,13 @@ exports.resetIdentityProvenance = resetIdentityProvenance;
5307
5520
  exports.resetSessionStartGuard = resetSessionStartGuard;
5308
5521
  exports.resolveAutoDeviceKey = resolveAutoDeviceKey;
5309
5522
  exports.resolveIdentity = resolveIdentity;
5523
+ exports.resolvePermissionCopy = resolvePermissionCopy;
5310
5524
  exports.resolvePlanTier = resolvePlanTier;
5311
5525
  exports.resolveUserContext = resolveUserContext;
5312
5526
  exports.sanitizeUserId = sanitizeUserId;
5313
5527
  exports.savePersistedSession = savePersistedSession;
5528
+ exports.saveSettledPermissions = saveSettledPermissions;
5529
+ exports.selectDuePermissionScreen = selectDuePermissionScreen;
5314
5530
  exports.sessionStorageKey = sessionStorageKey;
5315
5531
  exports.setCurrentSessionId = setCurrentSessionId;
5316
5532
  exports.subscribeActivationRevalidation = subscribeActivationRevalidation;