@wireai/activation 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/AGENTS.md +73 -0
  2. package/CHANGELOG.md +572 -0
  3. package/INTEGRATION_PROMPT.md +55 -0
  4. package/LICENSE +21 -0
  5. package/README.md +923 -0
  6. package/dist/CenteredModal-C3qQBHsA.d.mts +38 -0
  7. package/dist/CenteredModal-Cdgns6--.d.ts +38 -0
  8. package/dist/coachmarks/index.d.mts +352 -0
  9. package/dist/coachmarks/index.d.ts +352 -0
  10. package/dist/coachmarks/index.js +944 -0
  11. package/dist/coachmarks/index.js.map +1 -0
  12. package/dist/coachmarks/index.mjs +918 -0
  13. package/dist/coachmarks/index.mjs.map +1 -0
  14. package/dist/decision-CFvGY6nP.d.ts +228 -0
  15. package/dist/decision-Cl8OFYzu.d.mts +228 -0
  16. package/dist/index.d.mts +1390 -0
  17. package/dist/index.d.ts +1390 -0
  18. package/dist/index.js +3260 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/index.mjs +3186 -0
  21. package/dist/index.mjs.map +1 -0
  22. package/dist/questionnaire/index.d.mts +249 -0
  23. package/dist/questionnaire/index.d.ts +249 -0
  24. package/dist/questionnaire/index.js +922 -0
  25. package/dist/questionnaire/index.js.map +1 -0
  26. package/dist/questionnaire/index.mjs +906 -0
  27. package/dist/questionnaire/index.mjs.map +1 -0
  28. package/dist/reviews/index.d.mts +213 -0
  29. package/dist/reviews/index.d.ts +213 -0
  30. package/dist/reviews/index.js +1001 -0
  31. package/dist/reviews/index.js.map +1 -0
  32. package/dist/reviews/index.mjs +971 -0
  33. package/dist/reviews/index.mjs.map +1 -0
  34. package/dist/showcase/index.d.mts +119 -0
  35. package/dist/showcase/index.d.ts +119 -0
  36. package/dist/showcase/index.js +660 -0
  37. package/dist/showcase/index.js.map +1 -0
  38. package/dist/showcase/index.mjs +651 -0
  39. package/dist/showcase/index.mjs.map +1 -0
  40. package/dist/types-BKfpdZzX.d.mts +84 -0
  41. package/dist/types-BKfpdZzX.d.ts +84 -0
  42. package/dist/types-CMuOexw0.d.mts +116 -0
  43. package/dist/types-CMuOexw0.d.ts +116 -0
  44. package/dist/types-GL_hQ0TN.d.mts +46 -0
  45. package/dist/types-GL_hQ0TN.d.ts +46 -0
  46. package/llms.txt +36 -0
  47. package/metro/index.d.ts +32 -0
  48. package/metro/index.js +83 -0
  49. package/package.json +171 -0
  50. package/src/DemoOnboarding.tsx +133 -0
  51. package/src/OnboardingFlow.tsx +504 -0
  52. package/src/WireOnboarding.tsx +248 -0
  53. package/src/analytics/analyticsEvent.ts +69 -0
  54. package/src/analytics/reportClientEvent.ts +119 -0
  55. package/src/analytics/sendPreview.ts +73 -0
  56. package/src/attribution/attribution.ts +46 -0
  57. package/src/cards/ChipSelectCard.tsx +265 -0
  58. package/src/cards/InterstitialCard.tsx +299 -0
  59. package/src/cards/NumberStepperCard.tsx +121 -0
  60. package/src/cards/SelectionCard.tsx +270 -0
  61. package/src/cards/StatusCard.tsx +170 -0
  62. package/src/cards/TextInputCard.tsx +147 -0
  63. package/src/cards/index.ts +23 -0
  64. package/src/coachmarks/CoachmarkOverlayHost.tsx +44 -0
  65. package/src/coachmarks/CoachmarkProvider.tsx +108 -0
  66. package/src/coachmarks/GestureHint.tsx +331 -0
  67. package/src/coachmarks/SpotlightOverlay.tsx +254 -0
  68. package/src/coachmarks/coachmarkAnchorRegistry.ts +70 -0
  69. package/src/coachmarks/coachmarkOverlayStore.ts +63 -0
  70. package/src/coachmarks/index.ts +62 -0
  71. package/src/coachmarks/runtime.ts +103 -0
  72. package/src/coachmarks/selectTourSteps.ts +45 -0
  73. package/src/coachmarks/types.ts +58 -0
  74. package/src/coachmarks/useCoachmarkAnchor.ts +49 -0
  75. package/src/coachmarks/useCoachmarkTour.ts +200 -0
  76. package/src/components/AnimatedSparkle.tsx +101 -0
  77. package/src/components/Button.tsx +129 -0
  78. package/src/components/CardHandoff.tsx +212 -0
  79. package/src/components/CardLayout.tsx +103 -0
  80. package/src/components/CenteredModal.tsx +184 -0
  81. package/src/components/CompletionView.tsx +186 -0
  82. package/src/components/DoneBlock.tsx +37 -0
  83. package/src/components/ErrorBlock.tsx +42 -0
  84. package/src/components/Illustration.tsx +36 -0
  85. package/src/components/LoadingBlock.tsx +182 -0
  86. package/src/components/LoadingScreen.tsx +35 -0
  87. package/src/components/OnboardingScaffold.tsx +105 -0
  88. package/src/components/StepProgress.tsx +85 -0
  89. package/src/components/loaderChrome.ts +28 -0
  90. package/src/config/onboardingFlag.ts +39 -0
  91. package/src/config/wireConfigFromEnv.ts +52 -0
  92. package/src/device/deviceContext.ts +158 -0
  93. package/src/features/WireFeaturesProvider.tsx +81 -0
  94. package/src/features/cache.ts +92 -0
  95. package/src/features/defaults.ts +67 -0
  96. package/src/features/fetchWireFeatures.ts +86 -0
  97. package/src/features/index.ts +39 -0
  98. package/src/features/types.ts +61 -0
  99. package/src/features/useWireFeatures.ts +57 -0
  100. package/src/identity/userIdentity.ts +100 -0
  101. package/src/illustrations/defaultIllustrations.tsx +99 -0
  102. package/src/index.ts +159 -0
  103. package/src/motion/centeredModalMotion.ts +45 -0
  104. package/src/motion/motionSpec.ts +133 -0
  105. package/src/motion/useReducedMotion.ts +43 -0
  106. package/src/questionnaire/QuestionnaireGate.tsx +235 -0
  107. package/src/questionnaire/decision.ts +63 -0
  108. package/src/questionnaire/index.ts +54 -0
  109. package/src/questionnaire/runtime.ts +20 -0
  110. package/src/questionnaire/transport.ts +83 -0
  111. package/src/questionnaire/types.ts +182 -0
  112. package/src/questionnaire/useQuestionnaireGate.ts +159 -0
  113. package/src/reviews/ReviewGate.tsx +316 -0
  114. package/src/reviews/ReviewModal.tsx +13 -0
  115. package/src/reviews/decision.ts +120 -0
  116. package/src/reviews/equality.ts +54 -0
  117. package/src/reviews/expo-store-review.d.ts +16 -0
  118. package/src/reviews/feedbackForm.ts +41 -0
  119. package/src/reviews/index.ts +78 -0
  120. package/src/reviews/presentation.ts +29 -0
  121. package/src/reviews/runtime.ts +45 -0
  122. package/src/reviews/storeReview.ts +62 -0
  123. package/src/reviews/transport.ts +89 -0
  124. package/src/reviews/types.ts +172 -0
  125. package/src/reviews/useReviewGate.ts +137 -0
  126. package/src/session/persistedSession.ts +143 -0
  127. package/src/session-analytics/index.ts +18 -0
  128. package/src/session-analytics/reportSessionStart.ts +144 -0
  129. package/src/session-analytics/useSessionStart.ts +114 -0
  130. package/src/showcase/FeatureShowcase.tsx +233 -0
  131. package/src/showcase/index.ts +19 -0
  132. package/src/showcase/selectShowcaseSlides.ts +45 -0
  133. package/src/showcase/showcaseColors.ts +91 -0
  134. package/src/showcase/types.ts +74 -0
  135. package/src/theme/ThemeContext.tsx +38 -0
  136. package/src/theme/defaultTheme.ts +70 -0
  137. package/src/theme/themeFromBrand.ts +93 -0
  138. package/src/theme/types.ts +88 -0
  139. package/src/theme/typography.ts +46 -0
  140. package/src/types.ts +215 -0
  141. package/src/utils/deriveAnswers.ts +43 -0
  142. package/src/utils/readProgress.ts +33 -0
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Shared types for the coachmark / guided-tour overlay engine.
3
+ *
4
+ * Lifted from the Myelino app engine and made kit-generic: the app supplies the
5
+ * anchors + catalog + copy, the kit owns the animation/measure/queue mechanics.
6
+ * Dependency-free on purpose so the type surface can be imported anywhere.
7
+ */
8
+ /** The animated hand/gesture shown over or beside a highlighted target. */
9
+ type GestureKind = "swipe_up" | "swipe_down" | "swipe_left" | "swipe_right" | "tap" | "double_tap" | "pinch" | "zoom" | "none";
10
+ /** Which side of the highlighted target the tooltip pill sits on. */
11
+ type Placement = "top" | "bottom" | "left" | "right" | "center";
12
+ /** A measured on-screen rectangle (window coordinates from measureInWindow). */
13
+ interface TargetRect {
14
+ x: number;
15
+ y: number;
16
+ width: number;
17
+ height: number;
18
+ }
19
+ /**
20
+ * The host-injected gate storage. SYNCHRONOUS on purpose — a coachmark's
21
+ * "seen?" gate must resolve during render with no async flash (otherwise a ring
22
+ * momentarily paints before the read resolves and says "already seen"). MMKV's
23
+ * `getString`/`set` fit as-is; an in-memory `Map` wrapper works for tests. This
24
+ * is deliberately separate from the async `WireOnboardingStorage` used for
25
+ * session persistence.
26
+ */
27
+ interface CoachmarkStorage {
28
+ getItem(key: string): string | null;
29
+ setItem(key: string, value: string): void;
30
+ }
31
+ /**
32
+ * One step in a guided tour. `anchorId` is looked up in the anchor registry at
33
+ * fire time; omit it for a centered, target-less hint (e.g. a scroll gesture).
34
+ * `id` doubles as the analytics name for the step.
35
+ */
36
+ interface CoachmarkStep {
37
+ id: string;
38
+ message: string;
39
+ gesture?: GestureKind;
40
+ placement?: Placement;
41
+ anchorId?: string;
42
+ /** Extra action when the step is engaged (e.g. navigate to another tab). */
43
+ onEngage?: () => void;
44
+ }
45
+
46
+ export type { CoachmarkStorage as C, GestureKind as G, Placement as P, TargetRect as T, CoachmarkStep as a };
package/llms.txt ADDED
@@ -0,0 +1,36 @@
1
+ # wireai-onboarding
2
+
3
+ > A drop-in AI onboarding kit for React Native / Expo. One component, `<WireOnboarding>`, runs an
4
+ > entire backend-orchestrated, AI-driven onboarding flow (themed cards, progress bar, a mid-flow
5
+ > value screen, per-step validation, completion recap). It sits on top of the open-source
6
+ > [`wireai-rn`](https://github.com/chohra-med/wireai-rn) SDK and talks to a multi-tenant Wire AI
7
+ > backend over A2A. MIT licensed.
8
+
9
+ ## What an agent needs to know
10
+
11
+ - **Install:** `npm install wireai-onboarding wireai-rn` (react + react-native are peers).
12
+ - **Metro (required):** `module.exports = withWireOnboarding(getDefaultConfig(__dirname))` from `wireai-onboarding/metro` — pins one copy of react/react-native/wireai-rn/zod (prevents the dual-React crash).
13
+ - **Two secrets:** an app `apiKey` (a `wai_…` key that resolves the tenant server-side) and the backend `serverUrl`. Created in the getwireai console or via the backend's `register_<app>.py`. Nothing renders without both.
14
+ - **Render:** drop `<WireOnboarding config={wireConfigFromEnv({ appId })} theme={...} onComplete={persist} fallbackFlow={<YourStaticOnboarding/>} />` into the signup flow.
15
+ - **Capture is deterministic** — `onComplete` returns `{ answers, raw }`; no second LLM call. Persist all answers through the app's normal profile-update path.
16
+ - **It can never break onboarding** — pass `fallbackFlow` (your existing static onboarding) so a backend error/timeout degrades instead of dead-ending.
17
+
18
+ ## Helpers (the reusable substrate)
19
+
20
+ - `wireConfigFromEnv({ appId, metadata? })` → config or null (reads `EXPO_PUBLIC_WIREAI_API_KEY`/`_SERVER_URL`/`_APP_ID`).
21
+ - `isOnboardingEnabled({ remote? })` → the standard gate (transport present: apiKey + serverUrl). No on/off env flag; `remote` is an optional kill-switch the app passes in.
22
+ - `WIRE_ONBOARDING_EVENTS` + `toAnalyticsEvent(event)` → canonical `wire_onboarding_*` funnel names; log via the app's own analytics.
23
+ - `attributionMetadata(a)` → shape install/ad attribution into `config.metadata` (forwarded to the agent).
24
+ - `reportClientEvent` / `makeSessionId` → report device-only funnel events to `POST /v1/events`. `<WireOnboarding>` does this automatically: `dropped` on unmount-without-complete, `client_fallback` on degrade-to-static. Hosts must not double-report fallback.
25
+ - `deriveAnswers(messages)`, `themeFromBrand({ primary })`, `defaultIllustrations`, `DemoOnboarding` (dev/QA, no account).
26
+
27
+ ## Files
28
+
29
+ - [README.md](./README.md): full usage, props table, theming, distribution.
30
+ - [INTEGRATION_PROMPT.md](./INTEGRATION_PROMPT.md): a copy-paste prompt to make an AI agent (Claude Code) integrate this into an RN/Expo app end to end.
31
+ - [AGENTS.md](./AGENTS.md): the ordered integration brief for agents.
32
+ - [CHANGELOG.md](./CHANGELOG.md): version history.
33
+
34
+ ## Fastest path
35
+
36
+ Run the `wire-rn-integration` Claude skill — it detects the app's conventions, wires Metro, derives a theme, scaffolds the screen, wires a fallback, and type-checks. Otherwise use INTEGRATION_PROMPT.md.
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Type declarations for the `wireai-onboarding/metro` helper. This module runs in
3
+ * Node inside metro.config.js, so the config is loosely typed (`any`-ish) to avoid
4
+ * coupling to a specific Metro/Expo version.
5
+ */
6
+
7
+ export interface WithWireOnboardingOptions {
8
+ /**
9
+ * The app dir whose `node_modules` the single-instance deps
10
+ * (react / react-native / wireai-rn / zod) resolve against. Default: process.cwd().
11
+ */
12
+ appRoot?: string;
13
+ /**
14
+ * Absolute path to the kit's `src/` dir. When set, the `wireai-onboarding`
15
+ * specifier maps here (source consumption) and the dir is added to watchFolders.
16
+ * When omitted, the kit resolves from the installed package.
17
+ */
18
+ source?: string;
19
+ }
20
+
21
+ /**
22
+ * Non-destructively wire the wireai-onboarding kit into an existing Metro config:
23
+ * pins single-instance deps to the app's copy, optionally maps the kit specifier
24
+ * to source + watches it. Mutates and returns the same config object.
25
+ */
26
+ export function withWireOnboarding<TConfig extends Record<string, any>>(
27
+ config: TConfig,
28
+ options?: WithWireOnboardingOptions,
29
+ ): TConfig;
30
+
31
+ /** The deps pinned to the app's single copy. */
32
+ export const SINGLE_INSTANCE_DEPS: readonly ['react', 'react-native', 'wireai-rn', 'zod'];
package/metro/index.js ADDED
@@ -0,0 +1,83 @@
1
+ /**
2
+ * withWireOnboarding — one-call Metro wiring for the wireai-onboarding kit.
3
+ *
4
+ * This file runs in Node inside your app's `metro.config.js` (it is NOT React
5
+ * Native code), so it's plain CommonJS with zero RN imports.
6
+ *
7
+ * It folds the ~25-line hand-written block (pin single-instance deps + map the
8
+ * kit specifier + watch source) into one non-destructive wrapper around your
9
+ * existing Metro config object.
10
+ *
11
+ * // External app (kit installed from git/tarball):
12
+ * const { getDefaultConfig } = require('expo/metro-config');
13
+ * const { withWireOnboarding } = require('wireai-onboarding/metro');
14
+ * module.exports = withWireOnboarding(getDefaultConfig(__dirname));
15
+ *
16
+ * // Source consumer (monorepo app, kit resolved from its src/):
17
+ * const path = require('path');
18
+ * module.exports = withWireOnboarding(getDefaultConfig(__dirname), {
19
+ * source: path.join(__dirname, '../../getwire_ai/wireai-onboarding/src'),
20
+ * });
21
+ *
22
+ * @param {object} config An existing Metro config object (e.g. from getDefaultConfig).
23
+ * @param {object} [options]
24
+ * @param {string} [options.appRoot=process.cwd()] The app dir whose node_modules
25
+ * the single-instance deps (react/react-native/wireai-rn/zod) resolve against.
26
+ * @param {string} [options.source] Absolute path to the kit's `src/` dir. When
27
+ * set, the kit specifier maps here (source consumption) and it's added to
28
+ * watchFolders; when omitted, the kit resolves from the installed package.
29
+ * @returns {object} The same config object, mutated in place and returned.
30
+ */
31
+ 'use strict';
32
+
33
+ const path = require('path');
34
+
35
+ /** Deps that MUST resolve to the app's single copy (a 2nd React instance crashes RN). */
36
+ const SINGLE_INSTANCE_DEPS = ['react', 'react-native', 'wireai-rn', 'zod'];
37
+
38
+ function withWireOnboarding(config, options) {
39
+ const opts = options || {};
40
+ const appRoot = opts.appRoot || process.cwd();
41
+ const source = opts.source;
42
+
43
+ if (!config || typeof config !== 'object') {
44
+ throw new Error('withWireOnboarding(config): expected a Metro config object.');
45
+ }
46
+
47
+ const appNodeModules = path.resolve(appRoot, 'node_modules');
48
+
49
+ // ── extraNodeModules: pin single-instance deps + map the kit specifier ──────
50
+ const resolver = config.resolver || (config.resolver = {});
51
+ const existingExtra = resolver.extraNodeModules || {};
52
+
53
+ const pinned = {};
54
+ for (const dep of SINGLE_INSTANCE_DEPS) {
55
+ pinned[dep] = path.join(appNodeModules, dep);
56
+ }
57
+
58
+ // Source consumption maps the bare specifier to the kit src; otherwise leave it
59
+ // to the installed package (don't set it, so normal node_modules resolution wins).
60
+ const kitMapping = source ? { '@wireai/activation': source } : {};
61
+
62
+ // Helper defaults first, then the app's own explicit entries last so anything
63
+ // the app already set wins on conflict (non-destructive).
64
+ resolver.extraNodeModules = {
65
+ ...pinned,
66
+ ...kitMapping,
67
+ ...existingExtra,
68
+ };
69
+
70
+ // ── watchFolders: add the kit source so Metro reads + HMRs it ───────────────
71
+ if (source) {
72
+ const existingWatch = Array.isArray(config.watchFolders) ? config.watchFolders : [];
73
+ config.watchFolders = existingWatch.includes(source)
74
+ ? existingWatch
75
+ : [...existingWatch, source];
76
+ }
77
+
78
+ return config;
79
+ }
80
+
81
+ module.exports = { withWireOnboarding };
82
+ module.exports.withWireOnboarding = withWireOnboarding;
83
+ module.exports.SINGLE_INSTANCE_DEPS = SINGLE_INSTANCE_DEPS;
package/package.json ADDED
@@ -0,0 +1,171 @@
1
+ {
2
+ "name": "@wireai/activation",
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "description": "Premium, fully-themable drop-in AI onboarding kit for React Native / Expo, on top of the open-source wireai-rn SDK.",
6
+ "author": "Malik Chohra <malik@getwireai.com>",
7
+ "license": "MIT",
8
+ "homepage": "https://github.com/chohra-med/wireai-onboarding#readme",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/chohra-med/wireai-onboarding.git"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/chohra-med/wireai-onboarding/issues"
15
+ },
16
+ "keywords": [
17
+ "react-native",
18
+ "expo",
19
+ "onboarding",
20
+ "ai",
21
+ "generative-ui",
22
+ "wireai",
23
+ "wireai-rn",
24
+ "dynamic-onboarding"
25
+ ],
26
+ "publishConfig": {
27
+ "access": "public"
28
+ },
29
+ "sideEffects": false,
30
+ "main": "dist/index.js",
31
+ "module": "dist/index.mjs",
32
+ "types": "dist/index.d.ts",
33
+ "react-native": "src/index.ts",
34
+ "exports": {
35
+ ".": {
36
+ "react-native": {
37
+ "types": "./dist/index.d.ts",
38
+ "default": "./src/index.ts"
39
+ },
40
+ "import": {
41
+ "types": "./dist/index.d.mts",
42
+ "default": "./dist/index.mjs"
43
+ },
44
+ "require": {
45
+ "types": "./dist/index.d.ts",
46
+ "default": "./dist/index.js"
47
+ }
48
+ },
49
+ "./coachmarks": {
50
+ "react-native": {
51
+ "types": "./dist/coachmarks/index.d.ts",
52
+ "default": "./src/coachmarks/index.ts"
53
+ },
54
+ "import": {
55
+ "types": "./dist/coachmarks/index.d.mts",
56
+ "default": "./dist/coachmarks/index.mjs"
57
+ },
58
+ "require": {
59
+ "types": "./dist/coachmarks/index.d.ts",
60
+ "default": "./dist/coachmarks/index.js"
61
+ }
62
+ },
63
+ "./showcase": {
64
+ "react-native": {
65
+ "types": "./dist/showcase/index.d.ts",
66
+ "default": "./src/showcase/index.ts"
67
+ },
68
+ "import": {
69
+ "types": "./dist/showcase/index.d.mts",
70
+ "default": "./dist/showcase/index.mjs"
71
+ },
72
+ "require": {
73
+ "types": "./dist/showcase/index.d.ts",
74
+ "default": "./dist/showcase/index.js"
75
+ }
76
+ },
77
+ "./reviews": {
78
+ "react-native": {
79
+ "types": "./dist/reviews/index.d.ts",
80
+ "default": "./src/reviews/index.ts"
81
+ },
82
+ "import": {
83
+ "types": "./dist/reviews/index.d.mts",
84
+ "default": "./dist/reviews/index.mjs"
85
+ },
86
+ "require": {
87
+ "types": "./dist/reviews/index.d.ts",
88
+ "default": "./dist/reviews/index.js"
89
+ }
90
+ },
91
+ "./questionnaire": {
92
+ "react-native": {
93
+ "types": "./dist/questionnaire/index.d.ts",
94
+ "default": "./src/questionnaire/index.ts"
95
+ },
96
+ "import": {
97
+ "types": "./dist/questionnaire/index.d.mts",
98
+ "default": "./dist/questionnaire/index.mjs"
99
+ },
100
+ "require": {
101
+ "types": "./dist/questionnaire/index.d.ts",
102
+ "default": "./dist/questionnaire/index.js"
103
+ }
104
+ },
105
+ "./metro": {
106
+ "types": "./metro/index.d.ts",
107
+ "default": "./metro/index.js"
108
+ },
109
+ "./package.json": "./package.json"
110
+ },
111
+ "files": [
112
+ "dist",
113
+ "src",
114
+ "!src/**/*.test.ts",
115
+ "!src/**/*.test.tsx",
116
+ "metro",
117
+ "README.md",
118
+ "AGENTS.md",
119
+ "CHANGELOG.md",
120
+ "llms.txt",
121
+ "INTEGRATION_PROMPT.md"
122
+ ],
123
+ "scripts": {
124
+ "build": "tsup",
125
+ "prepack": "npm run build",
126
+ "typecheck": "tsc --noEmit",
127
+ "test": "node --test --experimental-strip-types --import ./test/register-ts-resolver.mjs \"src/**/*.test.ts\" \"test/canary/**/*.test.tsx\"",
128
+ "pack:tarball": "npm pack"
129
+ },
130
+ "peerDependencies": {
131
+ "@blazejkustra/react-native-onboarding": ">=1",
132
+ "expo-blur": ">=13",
133
+ "expo-store-review": ">=6",
134
+ "react": ">=18.0.0",
135
+ "react-native": ">=0.73.0",
136
+ "react-native-reanimated": ">=3",
137
+ "react-native-safe-area-context": ">=4.0.0",
138
+ "wireai-rn": ">=0.2.3",
139
+ "zod": ">=3"
140
+ },
141
+ "peerDependenciesMeta": {
142
+ "zod": {
143
+ "optional": false
144
+ },
145
+ "react-native-reanimated": {
146
+ "optional": true
147
+ },
148
+ "expo-blur": {
149
+ "optional": true
150
+ },
151
+ "expo-store-review": {
152
+ "optional": true
153
+ },
154
+ "@blazejkustra/react-native-onboarding": {
155
+ "optional": true
156
+ }
157
+ },
158
+ "devDependencies": {
159
+ "@blazejkustra/react-native-onboarding": "^1.0.1",
160
+ "@types/react": "^19.0.0",
161
+ "expo-blur": "^15.0.0",
162
+ "react": "^19.2.7",
163
+ "react-native": "^0.86.0",
164
+ "react-native-reanimated": "^4.1.1",
165
+ "react-native-safe-area-context": "^4.14.1",
166
+ "react-test-renderer": "^19.2.7",
167
+ "sucrase": "^3.35.1",
168
+ "tsup": "^8.0.0",
169
+ "typescript": "^5.4.0"
170
+ }
171
+ }
@@ -0,0 +1,133 @@
1
+ /**
2
+ * DemoOnboarding — a drop-in dev/QA trigger that runs the onboarding flow ON
3
+ * DEMAND in a modal, then closes. No loop, no account, no navigation side-effects:
4
+ * re-runnable infinitely with the same logged-in user. Place it anywhere a host
5
+ * wants (e.g. a __DEV__ row in Settings). If `config` is null (the app hasn't
6
+ * integrated Wire AI yet), it shows a hint instead of the flow.
7
+ *
8
+ * {__DEV__ && (
9
+ * <DemoOnboarding config={wireConfigFromEnv({ appId: "my-app" })} theme={myTheme} />
10
+ * )}
11
+ *
12
+ * It does NOT persist by default (a demo, not a real run) — pass `onComplete` if
13
+ * you want to observe the captured answers.
14
+ */
15
+ import { useCallback, useMemo, useState, type ReactNode } from "react";
16
+ import { Modal, Pressable, StyleSheet, Text, View } from "react-native";
17
+
18
+ import { WireOnboarding } from "./WireOnboarding";
19
+ import { mergeTheme } from "./theme/defaultTheme";
20
+ import type { OnboardingTheme } from "./theme/types";
21
+ import type { OnboardingResult, WireOnboardingConfig, WireOnboardingProps } from "./types";
22
+
23
+ export type DemoOnboardingProps = {
24
+ /** The Wire AI config, or `null` when the app hasn't integrated Wire AI yet. */
25
+ config: WireOnboardingConfig | null;
26
+ theme?: WireOnboardingProps["theme"];
27
+ illustrations?: WireOnboardingProps["illustrations"];
28
+ validators?: WireOnboardingProps["validators"];
29
+ copy?: WireOnboardingProps["copy"];
30
+ /** Trigger button label. Default "Demo onboarding". */
31
+ label?: string;
32
+ /** Observe the demo result. By default the demo just closes (no persistence). */
33
+ onComplete?: (result: OnboardingResult) => void;
34
+ /** Render your own trigger instead of the default button: `(open) => <YourButton onPress={open} />`. */
35
+ renderTrigger?: (open: () => void) => ReactNode;
36
+ };
37
+
38
+ export function DemoOnboarding({
39
+ config,
40
+ theme,
41
+ illustrations,
42
+ validators,
43
+ copy,
44
+ label = "Demo onboarding",
45
+ onComplete,
46
+ renderTrigger,
47
+ }: DemoOnboardingProps) {
48
+ const [open, setOpen] = useState(false);
49
+ const [runId, setRunId] = useState(0);
50
+ const t = useMemo(() => mergeTheme(theme ?? {}), [theme]);
51
+ const styles = useMemo(() => makeStyles(t), [t]);
52
+
53
+ const openDemo = useCallback(() => {
54
+ setRunId((n) => n + 1); // remount WireOnboarding → a fresh A2A session each open
55
+ setOpen(true);
56
+ }, []);
57
+ const close = useCallback(() => setOpen(false), []);
58
+ const finish = useCallback(
59
+ (result: OnboardingResult) => {
60
+ onComplete?.(result);
61
+ setOpen(false);
62
+ },
63
+ [onComplete],
64
+ );
65
+
66
+ return (
67
+ <>
68
+ {renderTrigger ? (
69
+ renderTrigger(openDemo)
70
+ ) : (
71
+ <Pressable onPress={openDemo} style={styles.trigger} accessibilityRole="button">
72
+ <Text style={styles.triggerText}>{label}</Text>
73
+ </Pressable>
74
+ )}
75
+
76
+ <Modal
77
+ visible={open}
78
+ animationType="slide"
79
+ presentationStyle="fullScreen"
80
+ onRequestClose={close}
81
+ >
82
+ <View style={styles.modal}>
83
+ {config ? (
84
+ <WireOnboarding
85
+ key={runId}
86
+ config={config}
87
+ theme={theme}
88
+ illustrations={illustrations}
89
+ validators={validators}
90
+ copy={copy}
91
+ onComplete={finish}
92
+ onSkip={close}
93
+ onError={close}
94
+ />
95
+ ) : (
96
+ <View style={styles.notice}>
97
+ <Text style={styles.noticeTitle}>Wire AI not configured</Text>
98
+ <Text style={styles.noticeBody}>
99
+ Set the Wire AI key + server URL (e.g. via wireConfigFromEnv) to demo the AI
100
+ onboarding here.
101
+ </Text>
102
+ <Pressable onPress={close} style={styles.trigger} accessibilityRole="button">
103
+ <Text style={styles.triggerText}>Close</Text>
104
+ </Pressable>
105
+ </View>
106
+ )}
107
+ </View>
108
+ </Modal>
109
+ </>
110
+ );
111
+ }
112
+
113
+ function makeStyles(t: OnboardingTheme) {
114
+ return StyleSheet.create({
115
+ trigger: {
116
+ backgroundColor: t.colors.primary,
117
+ borderRadius: t.button.radius,
118
+ paddingVertical: 14,
119
+ paddingHorizontal: 20,
120
+ alignItems: "center",
121
+ },
122
+ triggerText: { color: t.colors.onPrimary, fontFamily: t.fonts.medium, fontSize: 16 },
123
+ modal: { flex: 1, backgroundColor: t.colors.background },
124
+ notice: { flex: 1, alignItems: "center", justifyContent: "center", padding: 24, gap: 12 },
125
+ noticeTitle: { color: t.colors.text, fontFamily: t.fonts.bold, fontSize: 20 },
126
+ noticeBody: {
127
+ color: t.colors.textMuted,
128
+ fontFamily: t.fonts.regular,
129
+ fontSize: 15,
130
+ textAlign: "center",
131
+ },
132
+ });
133
+ }