@wireai/activation 0.14.2 → 0.14.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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,77 @@
3
3
  All notable changes to `@wireai/activation` (formerly `wireai-onboarding`).
4
4
  Historical entries below the rename keep the old package name on purpose.
5
5
 
6
+ ## [0.14.3] - 2026-08-18
7
+
8
+ **PATCH: no public surface gained, lost or renamed a member.** `SpotlightOverlay`, `GestureHint`
9
+ and `FeatureShowcase` keep their props and their exports; `react-native-reanimated` and
10
+ `@blazejkustra/react-native-onboarding` stay OPTIONAL peers. This finishes the class 0.14.2 started
11
+ with `expo-blur`: **no source under `src/` statically value-imports an optional peer any more.**
12
+
13
+ ### Fixed
14
+
15
+ - **The coachmarks subpath no longer force-imports its optional peer `react-native-reanimated`.**
16
+ `SpotlightOverlay` and `GestureHint` both did `import Animated, { … } from
17
+ "react-native-reanimated"` at module scope, and `coachmarks/index.ts` re-exports them with no
18
+ wildcard escape — so importing ANYTHING from `@wireai/activation/coachmarks`, even
19
+ `setCoachmarkStorage`, dragged the peer in, and `@wireai/activation/showcase` inherited the same
20
+ edge through `GestureHint`. A host that took "optional" at its word hit a Metro resolution
21
+ failure. The peer is now resolved through a guarded lazy require with a string-literal specifier
22
+ inside a try/catch (`src/coachmarks/reanimated.ts`, the same shape as `icons/expoIcons.ts` /
23
+ `coachmarks/expoBlur.ts`), so neither subpath carries a static top-level import of it.
24
+ - **Degradation, by design:** without the peer both surfaces render STATICALLY at their resting
25
+ frame. The spotlight keeps its ring, tooltip, copy, scrim/frost and gestures, with no fade-in
26
+ and no pulse; the gesture glyph keeps its capsules, ring, dot and motion track, at full opacity
27
+ and no travel. Nothing moves; everything renders. Reduce Motion is still honoured — the kit's
28
+ own `AccessibilityInfo`-backed hook stands in for the peer's `useReducedMotion`.
29
+ - The accessor re-exports the peer's own names (`useAnimatedStyle`, `useAnimatedProps`,
30
+ `withTiming`, …) and the call sites keep calling them unqualified, because reanimated's Babel
31
+ plugin decides what to workletize by the callee NAME. Hosts that HAVE the peer therefore get
32
+ byte-for-byte the same worklets, on the UI thread, as before.
33
+
34
+ - **The showcase subpath no longer force-imports its optional peer
35
+ `@blazejkustra/react-native-onboarding`.** `FeatureShowcase` imported the pager's default export
36
+ at module scope, so importing anything from `@wireai/activation/showcase` — even
37
+ `selectShowcaseSlides` — required a peer npm never installs. It is now resolved through a guarded
38
+ lazy require (`src/showcase/blazejOnboarding.ts`); only the TYPES are still imported statically,
39
+ which every toolchain erases.
40
+ - **Degradation, by design:** absent, the showcase renders null and calls `onDone` from an effect
41
+ — the SAME path the feature kill switch already takes — so the host's flow always advances
42
+ rather than stranding the user on a blank screen. The seen gate is deliberately NOT written, so
43
+ installing the peer later still plays the deck once.
44
+
45
+ - **A rule file prescribed the inverted optional-require contract, for the second time.**
46
+ `ai_rules/context_map.md` told the next author that the `expoIcons` callee "MUST be a local `req`,
47
+ never a literal `require`" — verbatim the `const req = require; req(name)` shape that
48
+ `icons/expoIcons.ts` autopsies as the 0.8.0 red-box bug (aliasing hides the call from Metro's
49
+ static collector, so the module never enters the bundle). 0.14.2 corrected this claim in
50
+ `frequent_rules.md`; this was its second copy, and it was still live. Rewritten to the one correct
51
+ contract, citing `src/icons/expoIcons.ts:98`.
52
+
53
+ ### Changed
54
+
55
+ - **`metro/index.js` `OPTIONAL_MODULES` now lists both modules**, and `tsup.config.ts` drops them
56
+ from its static-peer list (`external` is derived from the union, so they stay external, and the
57
+ CJS keep-literal-require hook now covers them). The helper's "a statically-imported module must NOT be
58
+ stubbed" note named exactly these two; it is rewritten, because the kit now has NO
59
+ statically-imported optional peer — everything it imports statically is a REQUIRED peer whose
60
+ absence must stay a hard build error.
61
+ - **Docs that promised the old behaviour are corrected in the same wave**, both directions: the
62
+ coachmarks and showcase barrel docs and the README said importing a subpath "pulls in" these
63
+ peers (no longer true), while the README/AGENTS peer tables already claimed every optional peer
64
+ "sits behind a guarded lazy require" (not true until now).
65
+
66
+ ### Added
67
+
68
+ - **A whole-class ratchet in `test/canary/metroResolution.test.tsx`:** every file under `src/` is
69
+ scanned for a static value-import of ANY module in `OPTIONAL_MODULES`, with a positive control
70
+ proving the scanner catches default, named, wrapped multi-line, bare and subpath imports while
71
+ ignoring `import type` and the guarded require itself. Each optional peer so far has arrived as
72
+ the same defect; a new one now inherits the check for free. Plus per-module collection tests
73
+ (Metro marks both new specifiers `isOptional`) and component-level canaries for both peers in
74
+ both directions, including the absent-reanimated/present-blur combination that pins the frost at
75
+ full intensity.
76
+
6
77
  ## [0.14.2] - 2026-08-18
7
78
 
8
79
  **PATCH: no public surface gained, lost or renamed a member.** `SpotlightOverlay` keeps its props
package/README.md CHANGED
@@ -801,8 +801,12 @@ consuming app, since the kit has no local TS install).
801
801
 
802
802
  The kit ships a performance-first guided-tour engine on a **subpath** so the main
803
803
  barrel stays dependency-free — import it from `@wireai/activation/coachmarks`. It
804
- pulls in two **optional** peers only when you use it: `react-native-reanimated`
805
- (UI-thread gesture + ring animation) and `expo-blur` (the frosted spotlight).
804
+ can USE two **optional** peers, and needs neither: `react-native-reanimated`
805
+ (UI-thread gesture + ring animation) and `expo-blur` (the frosted spotlight). Both
806
+ sit behind a guarded lazy require, so importing the subpath never pulls them into
807
+ your bundle. Without reanimated the tour renders statically (the ring, tooltip,
808
+ copy and gestures are unchanged — nothing animates); without expo-blur the frost
809
+ becomes a plain dimmed scrim.
806
810
  Source consumers get it through the existing `@wireai/activation/*` tsconfig path
807
811
  mapping; installed apps resolve the subpath export.
808
812
 
@@ -922,6 +926,12 @@ theme colors, and gates once through the same storage as the tours
922
926
  (`wire_showcase_<id>_seen`; `isTestingCoachmark` bypasses it). If already seen it
923
927
  renders nothing and calls `onDone` from an effect.
924
928
 
929
+ The pager is a guarded lazy require, so importing `@wireai/activation/showcase`
930
+ does not pull it into your bundle. If you never install it the showcase renders
931
+ nothing and calls `onDone` — your flow advances exactly as if the deck had been
932
+ seen — and the seen gate is NOT written, so installing the peer later still plays
933
+ the deck once.
934
+
925
935
  **Recommended placement (v0.3.0+): AFTER `onComplete`, as a personalized value
926
936
  bridge.** The user just told you what they want in `result.answers`, so the
927
937
  terminal recap is the highest-value slot to show the 2-3 slides that answer it,
@@ -245,10 +245,18 @@ declare const selectTourSteps: <T extends {
245
245
  }>(catalog: T[], selection?: string[]) => T[];
246
246
 
247
247
  /**
248
- * Abstract gesture pictogram shown beside a coachmark tooltip. Pure reanimated
249
- * every animation runs on the UI thread (shared values + worklets), never
250
- * setState, so it costs nothing on the JS thread. Honors Reduce Motion by
251
- * dropping the motion loop and rendering the glyph static.
248
+ * Abstract gesture pictogram shown beside a coachmark tooltip. With the OPTIONAL
249
+ * `react-native-reanimated` peer installed every animation runs on the UI thread
250
+ * (shared values + worklets), never setState, so it costs nothing on the JS
251
+ * thread. Honors Reduce Motion by dropping the motion loop and rendering the
252
+ * glyph static.
253
+ *
254
+ * The peer is reached through the guarded lazy require in `reanimated.ts`, never
255
+ * a static import, so `@wireai/activation/coachmarks` (and `/showcase`, which
256
+ * reaches this component through FeatureShowcase) builds on a host that never
257
+ * installed it. Absent, the glyph renders STATICALLY at its resting frame — the
258
+ * same capsules, ring, dot and motion track, at full opacity and no travel, with
259
+ * one render instead of a UI-thread loop. The pictogram never disappears.
252
260
  *
253
261
  * NO drawn-hand anatomy. Swipes render the standard two-finger swipe glyph: two
254
262
  * rounded vertical capsules (an abstract index + middle finger, slightly
@@ -245,10 +245,18 @@ declare const selectTourSteps: <T extends {
245
245
  }>(catalog: T[], selection?: string[]) => T[];
246
246
 
247
247
  /**
248
- * Abstract gesture pictogram shown beside a coachmark tooltip. Pure reanimated
249
- * every animation runs on the UI thread (shared values + worklets), never
250
- * setState, so it costs nothing on the JS thread. Honors Reduce Motion by
251
- * dropping the motion loop and rendering the glyph static.
248
+ * Abstract gesture pictogram shown beside a coachmark tooltip. With the OPTIONAL
249
+ * `react-native-reanimated` peer installed every animation runs on the UI thread
250
+ * (shared values + worklets), never setState, so it costs nothing on the JS
251
+ * thread. Honors Reduce Motion by dropping the motion loop and rendering the
252
+ * glyph static.
253
+ *
254
+ * The peer is reached through the guarded lazy require in `reanimated.ts`, never
255
+ * a static import, so `@wireai/activation/coachmarks` (and `/showcase`, which
256
+ * reaches this component through FeatureShowcase) builds on a host that never
257
+ * installed it. Absent, the glyph renders STATICALLY at its resting frame — the
258
+ * same capsules, ring, dot and motion track, at full opacity and no travel, with
259
+ * one render instead of a UI-thread loop. The pictogram never disappears.
252
260
  *
253
261
  * NO drawn-hand anatomy. Swipes render the standard two-finger swipe glyph: two
254
262
  * rounded vertical capsules (an abstract index + middle finger, slightly
@@ -3,12 +3,10 @@
3
3
  var React3 = require('react');
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
5
  var reactNative = require('react-native');
6
- var Animated = require('react-native-reanimated');
7
6
 
8
7
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
8
 
10
9
  var React3__default = /*#__PURE__*/_interopDefault(React3);
11
- var Animated__default = /*#__PURE__*/_interopDefault(Animated);
12
10
 
13
11
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
14
12
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
@@ -94,8 +92,8 @@ var DEFAULT_TIMEOUT_MS = 4e3;
94
92
  var failOpen = async (storage, key) => {
95
93
  var _a;
96
94
  if (!storage) return defaultWireFeatures;
97
- const cached2 = await readCachedFeatures(storage, key);
98
- return (_a = cached2 == null ? void 0 : cached2.features) != null ? _a : defaultWireFeatures;
95
+ const cached3 = await readCachedFeatures(storage, key);
96
+ return (_a = cached3 == null ? void 0 : cached3.features) != null ? _a : defaultWireFeatures;
99
97
  };
100
98
  var fetchWithTimeout = async (url, apiKey, timeoutMs) => {
101
99
  const controller = typeof AbortController !== "undefined" ? new AbortController() : void 0;
@@ -374,6 +372,157 @@ var resolveBlurView = (requireModule = runtimeRequire) => {
374
372
  return void 0;
375
373
  }
376
374
  };
375
+ var lastKnown = false;
376
+ var useReducedMotion = () => {
377
+ const [reduced, setReduced] = React3.useState(lastKnown);
378
+ React3.useEffect(() => {
379
+ let mounted = true;
380
+ reactNative.AccessibilityInfo.isReduceMotionEnabled().then((value) => {
381
+ lastKnown = value;
382
+ if (mounted) setReduced(value);
383
+ }).catch(() => {
384
+ });
385
+ const sub = reactNative.AccessibilityInfo.addEventListener("reduceMotionChanged", (value) => {
386
+ lastKnown = value;
387
+ setReduced(value);
388
+ });
389
+ return () => {
390
+ mounted = false;
391
+ sub == null ? void 0 : sub.remove();
392
+ };
393
+ }, []);
394
+ return reduced;
395
+ };
396
+
397
+ // src/coachmarks/reanimated.ts
398
+ var runtimeRequire2 = (moduleName) => {
399
+ if (moduleName !== "react-native-reanimated") return void 0;
400
+ if (typeof __require !== "function") return void 0;
401
+ try {
402
+ return require("react-native-reanimated");
403
+ } catch {
404
+ return void 0;
405
+ }
406
+ };
407
+ var useStaticSharedValue = (initial) => {
408
+ const [, bump] = React3.useReducer((n) => n + 1, 0);
409
+ const box = React3.useRef(null);
410
+ if (box.current === null) {
411
+ let current2 = initial;
412
+ box.current = {
413
+ get value() {
414
+ return current2;
415
+ },
416
+ set value(next) {
417
+ if (Object.is(next, current2)) return;
418
+ current2 = next;
419
+ bump();
420
+ }
421
+ };
422
+ }
423
+ return box.current;
424
+ };
425
+ var runWorklet = (factory, fallback) => {
426
+ var _a;
427
+ try {
428
+ return (_a = factory()) != null ? _a : fallback;
429
+ } catch {
430
+ return fallback;
431
+ }
432
+ };
433
+ var StaticAnimatedView = React3.forwardRef(function StaticAnimatedView2({ entering: _entering, exiting: _exiting, children, ...rest }, ref) {
434
+ return React3.createElement(reactNative.View, { ...rest, ref }, children);
435
+ });
436
+ var staticCreateAnimatedComponent = (component) => function StaticAnimatedComponent({ animatedProps, ...rest }) {
437
+ return React3.createElement(component, { ...rest, ...animatedProps });
438
+ };
439
+ var staticApi = {
440
+ present: false,
441
+ View: StaticAnimatedView,
442
+ createAnimatedComponent: staticCreateAnimatedComponent,
443
+ useSharedValue: useStaticSharedValue,
444
+ useAnimatedStyle: (factory) => runWorklet(factory, {}),
445
+ useAnimatedProps: (factory) => runWorklet(factory, {}),
446
+ useReducedMotion,
447
+ withTiming: (toValue) => toValue,
448
+ withRepeat: (animation) => animation,
449
+ withSequence: (...animations) => {
450
+ var _a;
451
+ return (_a = animations[animations.length - 1]) != null ? _a : 0;
452
+ },
453
+ withDelay: (_ms, animation) => animation,
454
+ fadeIn: () => void 0,
455
+ fadeOut: () => void 0
456
+ };
457
+ var isFunction = (value) => typeof value === "function";
458
+ var isComponent = (value) => typeof value === "function" || !!value && typeof value === "object" && "$$typeof" in value;
459
+ var fromModule = (mod) => {
460
+ var _a, _b;
461
+ if (!mod || typeof mod !== "object") return void 0;
462
+ const ns = mod;
463
+ const def = ns.default && typeof ns.default === "object" ? ns.default : void 0;
464
+ const pick = (name) => {
465
+ var _a2;
466
+ return (_a2 = ns[name]) != null ? _a2 : def == null ? void 0 : def[name];
467
+ };
468
+ const view = (_a = def == null ? void 0 : def.View) != null ? _a : ns.View;
469
+ const createAnimated = (_b = def == null ? void 0 : def.createAnimatedComponent) != null ? _b : ns.createAnimatedComponent;
470
+ const hooks = {
471
+ useSharedValue: pick("useSharedValue"),
472
+ useAnimatedStyle: pick("useAnimatedStyle"),
473
+ useAnimatedProps: pick("useAnimatedProps"),
474
+ useReducedMotion: pick("useReducedMotion"),
475
+ withTiming: pick("withTiming"),
476
+ withRepeat: pick("withRepeat"),
477
+ withSequence: pick("withSequence"),
478
+ withDelay: pick("withDelay")
479
+ };
480
+ const fadeIn = pick("FadeIn");
481
+ const fadeOut = pick("FadeOut");
482
+ if (!isComponent(view) || !isFunction(createAnimated)) return void 0;
483
+ if (Object.values(hooks).some((member) => !isFunction(member))) return void 0;
484
+ if (!fadeIn || !fadeOut) return void 0;
485
+ const api2 = {
486
+ present: true,
487
+ View: view,
488
+ createAnimatedComponent: createAnimated,
489
+ ...hooks,
490
+ fadeIn: (durationMs) => fadeIn.duration(durationMs),
491
+ fadeOut: (durationMs) => fadeOut.duration(durationMs)
492
+ };
493
+ return api2;
494
+ };
495
+ var cached2;
496
+ var resolveReanimated = (requireModule = runtimeRequire2) => {
497
+ var _a;
498
+ try {
499
+ if (cached2 === void 0 || requireModule !== runtimeRequire2) {
500
+ const resolved = (_a = fromModule(requireModule("react-native-reanimated"))) != null ? _a : staticApi;
501
+ if (requireModule === runtimeRequire2) cached2 = resolved;
502
+ return resolved;
503
+ }
504
+ return cached2;
505
+ } catch {
506
+ return staticApi;
507
+ }
508
+ };
509
+ var api = () => resolveReanimated();
510
+ var Animated = {
511
+ get View() {
512
+ return api().View;
513
+ },
514
+ createAnimatedComponent: (component) => api().createAnimatedComponent(component)
515
+ };
516
+ var FadeIn = { duration: (durationMs) => api().fadeIn(durationMs) };
517
+ var FadeOut = { duration: (durationMs) => api().fadeOut(durationMs) };
518
+ var useSharedValue = (initial) => api().useSharedValue(initial);
519
+ var useAnimatedStyle = (factory) => api().useAnimatedStyle(factory);
520
+ var useAnimatedProps = (factory) => api().useAnimatedProps(factory);
521
+ var useReducedMotion2 = () => api().useReducedMotion();
522
+ var withTiming = (toValue, config) => api().withTiming(toValue, config);
523
+ var withRepeat = (animation, count, reverse) => api().withRepeat(animation, count, reverse);
524
+ var withSequence = (...animations) => api().withSequence(...animations);
525
+ var withDelay = (ms, animation) => api().withDelay(ms, animation);
377
526
  var SWIPE_MS = 700;
378
527
  var TAP_MS = 320;
379
528
  var SPREAD_MS = 620;
@@ -431,29 +580,29 @@ var GestureHintComponent = ({
431
580
  renderHand
432
581
  }) => {
433
582
  var _a;
434
- const translate = Animated.useSharedValue(0);
435
- const opacity = Animated.useSharedValue(1);
436
- const pulse = Animated.useSharedValue(1);
437
- const spread = Animated.useSharedValue(0);
438
- const reduced = Animated.useReducedMotion();
583
+ const translate = useSharedValue(0);
584
+ const opacity = useSharedValue(1);
585
+ const pulse = useSharedValue(1);
586
+ const spread = useSharedValue(0);
587
+ const reduced = useReducedMotion2();
439
588
  const isSwipe = type === "swipe_up" || type === "swipe_down" || type === "swipe_left" || type === "swipe_right";
440
589
  const isSpread = type === "pinch" || type === "zoom";
441
590
  const isTap = type === "tap" || type === "double_tap";
442
591
  React3.useEffect(() => {
443
592
  if (type === "none" || reduced) return void 0;
444
593
  if (isTap) {
445
- pulse.value = Animated.withRepeat(
446
- Animated.withSequence(
447
- Animated.withTiming(0.82, { duration: TAP_MS }),
448
- Animated.withTiming(1, { duration: TAP_MS })
594
+ pulse.value = withRepeat(
595
+ withSequence(
596
+ withTiming(0.82, { duration: TAP_MS }),
597
+ withTiming(1, { duration: TAP_MS })
449
598
  ),
450
599
  -1,
451
600
  false
452
601
  );
453
- opacity.value = Animated.withRepeat(
454
- Animated.withSequence(
455
- Animated.withTiming(0.55, { duration: TAP_MS }),
456
- Animated.withTiming(1, { duration: TAP_MS })
602
+ opacity.value = withRepeat(
603
+ withSequence(
604
+ withTiming(0.55, { duration: TAP_MS }),
605
+ withTiming(1, { duration: TAP_MS })
457
606
  ),
458
607
  -1,
459
608
  false
@@ -461,48 +610,48 @@ var GestureHintComponent = ({
461
610
  return void 0;
462
611
  }
463
612
  if (isSpread) {
464
- spread.value = Animated.withRepeat(
465
- Animated.withSequence(
466
- Animated.withTiming(size * 0.26, { duration: SPREAD_MS }),
467
- Animated.withTiming(0, { duration: SPREAD_MS })
613
+ spread.value = withRepeat(
614
+ withSequence(
615
+ withTiming(size * 0.26, { duration: SPREAD_MS }),
616
+ withTiming(0, { duration: SPREAD_MS })
468
617
  ),
469
618
  -1,
470
619
  false
471
620
  );
472
621
  return void 0;
473
622
  }
474
- translate.value = Animated.withRepeat(
475
- Animated.withSequence(
476
- Animated.withTiming(0, { duration: 0 }),
477
- Animated.withTiming(-26, { duration: SWIPE_MS }),
478
- Animated.withDelay(140, Animated.withTiming(0, { duration: 0 }))
623
+ translate.value = withRepeat(
624
+ withSequence(
625
+ withTiming(0, { duration: 0 }),
626
+ withTiming(-26, { duration: SWIPE_MS }),
627
+ withDelay(140, withTiming(0, { duration: 0 }))
479
628
  ),
480
629
  -1,
481
630
  false
482
631
  );
483
- opacity.value = Animated.withRepeat(
484
- Animated.withSequence(
485
- Animated.withTiming(1, { duration: SWIPE_MS / 2 }),
486
- Animated.withTiming(0, { duration: SWIPE_MS / 2 }),
487
- Animated.withDelay(140, Animated.withTiming(1, { duration: 0 }))
632
+ opacity.value = withRepeat(
633
+ withSequence(
634
+ withTiming(1, { duration: SWIPE_MS / 2 }),
635
+ withTiming(0, { duration: SWIPE_MS / 2 }),
636
+ withDelay(140, withTiming(1, { duration: 0 }))
488
637
  ),
489
638
  -1,
490
639
  false
491
640
  );
492
641
  return void 0;
493
642
  }, [type, reduced, isTap, isSpread, translate, opacity, pulse, spread, size]);
494
- const swipeAnimatedStyle = Animated.useAnimatedStyle(() => ({
643
+ const swipeAnimatedStyle = useAnimatedStyle(() => ({
495
644
  opacity: opacity.value,
496
645
  transform: [{ translateY: translate.value }]
497
646
  }));
498
- const tapAnimatedStyle = Animated.useAnimatedStyle(() => ({
647
+ const tapAnimatedStyle = useAnimatedStyle(() => ({
499
648
  opacity: opacity.value,
500
649
  transform: [{ scale: pulse.value }]
501
650
  }));
502
- const spreadLeftStyle = Animated.useAnimatedStyle(() => ({
651
+ const spreadLeftStyle = useAnimatedStyle(() => ({
503
652
  transform: [{ translateX: -spread.value }]
504
653
  }));
505
- const spreadRightStyle = Animated.useAnimatedStyle(() => ({
654
+ const spreadRightStyle = useAnimatedStyle(() => ({
506
655
  transform: [{ translateX: spread.value }]
507
656
  }));
508
657
  if (type === "none") return null;
@@ -514,7 +663,7 @@ var GestureHintComponent = ({
514
663
  style: { transform: [{ rotate: (_a = GLYPH_ROTATION[type]) != null ? _a : "0deg" }] },
515
664
  pointerEvents: "none",
516
665
  children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles.swipeStack, children: [
517
- /* @__PURE__ */ jsxRuntime.jsx(Animated__default.default.View, { style: swipeAnimatedStyle, pointerEvents: "none", children: customGlyph != null ? customGlyph : /* @__PURE__ */ jsxRuntime.jsx(FingerPair, { size, color }) }),
666
+ /* @__PURE__ */ jsxRuntime.jsx(Animated.View, { style: swipeAnimatedStyle, pointerEvents: "none", children: customGlyph != null ? customGlyph : /* @__PURE__ */ jsxRuntime.jsx(FingerPair, { size, color }) }),
518
667
  /* @__PURE__ */ jsxRuntime.jsx(MotionTrack, { size, color })
519
668
  ] })
520
669
  }
@@ -529,16 +678,16 @@ var GestureHintComponent = ({
529
678
  backgroundColor: color
530
679
  };
531
680
  return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles.spreadRow, { gap: size * 0.12 }], pointerEvents: "none", children: [
532
- /* @__PURE__ */ jsxRuntime.jsx(Animated__default.default.View, { style: spreadLeftStyle, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: capsule }) }),
533
- /* @__PURE__ */ jsxRuntime.jsx(Animated__default.default.View, { style: spreadRightStyle, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: capsule }) })
681
+ /* @__PURE__ */ jsxRuntime.jsx(Animated.View, { style: spreadLeftStyle, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: capsule }) }),
682
+ /* @__PURE__ */ jsxRuntime.jsx(Animated.View, { style: spreadRightStyle, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: capsule }) })
534
683
  ] });
535
684
  }
536
685
  const ringSize = size * 0.86;
537
686
  const ringBorder = Math.max(2, size * 0.08);
538
687
  const dotSize = size * 0.32;
539
- return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles.tapWrap, { width: size, height: size }], pointerEvents: "none", children: customGlyph ? /* @__PURE__ */ jsxRuntime.jsx(Animated__default.default.View, { style: tapAnimatedStyle, pointerEvents: "none", children: customGlyph }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
688
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles.tapWrap, { width: size, height: size }], pointerEvents: "none", children: customGlyph ? /* @__PURE__ */ jsxRuntime.jsx(Animated.View, { style: tapAnimatedStyle, pointerEvents: "none", children: customGlyph }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
540
689
  /* @__PURE__ */ jsxRuntime.jsx(
541
- Animated__default.default.View,
690
+ Animated.View,
542
691
  {
543
692
  style: [
544
693
  styles.tapRing,
@@ -605,35 +754,35 @@ var SpotlightOverlayComponent = ({
605
754
  accentTextColor
606
755
  }) => {
607
756
  const { height: screenH } = reactNative.useWindowDimensions();
608
- const reduced = Animated.useReducedMotion();
757
+ const reduced = useReducedMotion2();
609
758
  const theme = useOnboardingTheme();
610
759
  const accent = accentColor != null ? accentColor : theme.colors.primary;
611
760
  const accentText = accentTextColor != null ? accentTextColor : theme.colors.onPrimary;
612
761
  const AnimatedBlurView = React3.useMemo(() => {
613
762
  const BlurView = resolveBlurView();
614
- return BlurView ? Animated__default.default.createAnimatedComponent(BlurView) : null;
763
+ return BlurView ? Animated.createAnimatedComponent(BlurView) : null;
615
764
  }, []);
616
765
  const showGesture = gesture !== "none" && gesture !== "tap" && gesture !== "double_tap";
617
- const intensity = Animated.useSharedValue(0);
618
- const ringScale = Animated.useSharedValue(1);
766
+ const intensity = useSharedValue(0);
767
+ const ringScale = useSharedValue(1);
619
768
  React3.useEffect(() => {
620
- intensity.value = Animated.withTiming(BLUR_INTENSITY, { duration: FADE_MS });
769
+ intensity.value = withTiming(BLUR_INTENSITY, { duration: FADE_MS });
621
770
  }, [intensity]);
622
771
  React3.useEffect(() => {
623
772
  if (reduced) return;
624
- ringScale.value = Animated.withRepeat(
625
- Animated.withSequence(
626
- Animated.withTiming(1.06, { duration: 700 }),
627
- Animated.withTiming(1, { duration: 700 })
773
+ ringScale.value = withRepeat(
774
+ withSequence(
775
+ withTiming(1.06, { duration: 700 }),
776
+ withTiming(1, { duration: 700 })
628
777
  ),
629
778
  -1,
630
779
  true
631
780
  );
632
781
  }, [reduced, ringScale]);
633
- const blurAnimatedProps = Animated.useAnimatedProps(() => ({
782
+ const blurAnimatedProps = useAnimatedProps(() => ({
634
783
  intensity: intensity.value
635
784
  }));
636
- const ringAnimatedStyle = Animated.useAnimatedStyle(() => ({
785
+ const ringAnimatedStyle = useAnimatedStyle(() => ({
637
786
  transform: [{ scale: ringScale.value }]
638
787
  }));
639
788
  const geometry = React3.useMemo(() => {
@@ -655,11 +804,11 @@ var SpotlightOverlayComponent = ({
655
804
  };
656
805
  }, [targetRect, placement, screenH]);
657
806
  return /* @__PURE__ */ jsxRuntime.jsxs(
658
- Animated__default.default.View,
807
+ Animated.View,
659
808
  {
660
809
  style: [reactNative.StyleSheet.absoluteFill, styles2.root],
661
- entering: Animated.FadeIn.duration(FADE_MS),
662
- exiting: Animated.FadeOut.duration(FADE_MS),
810
+ entering: FadeIn.duration(FADE_MS),
811
+ exiting: FadeOut.duration(FADE_MS),
663
812
  children: [
664
813
  AnimatedBlurView ? /* @__PURE__ */ jsxRuntime.jsx(
665
814
  AnimatedBlurView,
@@ -672,7 +821,7 @@ var SpotlightOverlayComponent = ({
672
821
  // `expo-blur` absent → a plain dimmed scrim stands in for the frost. Same dark backdrop
673
822
  // the tour reads against, so the ring, tooltip and gestures behave identically.
674
823
  /* @__PURE__ */ jsxRuntime.jsx(
675
- Animated__default.default.View,
824
+ Animated.View,
676
825
  {
677
826
  style: [reactNative.StyleSheet.absoluteFill, styles2.scrim],
678
827
  pointerEvents: "none"
@@ -689,7 +838,7 @@ var SpotlightOverlayComponent = ({
689
838
  }
690
839
  ),
691
840
  geometry && /* @__PURE__ */ jsxRuntime.jsx(
692
- Animated__default.default.View,
841
+ Animated.View,
693
842
  {
694
843
  style: [
695
844
  styles2.ring,