@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.
- package/AGENTS.md +73 -0
- package/CHANGELOG.md +572 -0
- package/INTEGRATION_PROMPT.md +55 -0
- package/LICENSE +21 -0
- package/README.md +923 -0
- package/dist/CenteredModal-C3qQBHsA.d.mts +38 -0
- package/dist/CenteredModal-Cdgns6--.d.ts +38 -0
- package/dist/coachmarks/index.d.mts +352 -0
- package/dist/coachmarks/index.d.ts +352 -0
- package/dist/coachmarks/index.js +944 -0
- package/dist/coachmarks/index.js.map +1 -0
- package/dist/coachmarks/index.mjs +918 -0
- package/dist/coachmarks/index.mjs.map +1 -0
- package/dist/decision-CFvGY6nP.d.ts +228 -0
- package/dist/decision-Cl8OFYzu.d.mts +228 -0
- package/dist/index.d.mts +1390 -0
- package/dist/index.d.ts +1390 -0
- package/dist/index.js +3260 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +3186 -0
- package/dist/index.mjs.map +1 -0
- package/dist/questionnaire/index.d.mts +249 -0
- package/dist/questionnaire/index.d.ts +249 -0
- package/dist/questionnaire/index.js +922 -0
- package/dist/questionnaire/index.js.map +1 -0
- package/dist/questionnaire/index.mjs +906 -0
- package/dist/questionnaire/index.mjs.map +1 -0
- package/dist/reviews/index.d.mts +213 -0
- package/dist/reviews/index.d.ts +213 -0
- package/dist/reviews/index.js +1001 -0
- package/dist/reviews/index.js.map +1 -0
- package/dist/reviews/index.mjs +971 -0
- package/dist/reviews/index.mjs.map +1 -0
- package/dist/showcase/index.d.mts +119 -0
- package/dist/showcase/index.d.ts +119 -0
- package/dist/showcase/index.js +660 -0
- package/dist/showcase/index.js.map +1 -0
- package/dist/showcase/index.mjs +651 -0
- package/dist/showcase/index.mjs.map +1 -0
- package/dist/types-BKfpdZzX.d.mts +84 -0
- package/dist/types-BKfpdZzX.d.ts +84 -0
- package/dist/types-CMuOexw0.d.mts +116 -0
- package/dist/types-CMuOexw0.d.ts +116 -0
- package/dist/types-GL_hQ0TN.d.mts +46 -0
- package/dist/types-GL_hQ0TN.d.ts +46 -0
- package/llms.txt +36 -0
- package/metro/index.d.ts +32 -0
- package/metro/index.js +83 -0
- package/package.json +171 -0
- package/src/DemoOnboarding.tsx +133 -0
- package/src/OnboardingFlow.tsx +504 -0
- package/src/WireOnboarding.tsx +248 -0
- package/src/analytics/analyticsEvent.ts +69 -0
- package/src/analytics/reportClientEvent.ts +119 -0
- package/src/analytics/sendPreview.ts +73 -0
- package/src/attribution/attribution.ts +46 -0
- package/src/cards/ChipSelectCard.tsx +265 -0
- package/src/cards/InterstitialCard.tsx +299 -0
- package/src/cards/NumberStepperCard.tsx +121 -0
- package/src/cards/SelectionCard.tsx +270 -0
- package/src/cards/StatusCard.tsx +170 -0
- package/src/cards/TextInputCard.tsx +147 -0
- package/src/cards/index.ts +23 -0
- package/src/coachmarks/CoachmarkOverlayHost.tsx +44 -0
- package/src/coachmarks/CoachmarkProvider.tsx +108 -0
- package/src/coachmarks/GestureHint.tsx +331 -0
- package/src/coachmarks/SpotlightOverlay.tsx +254 -0
- package/src/coachmarks/coachmarkAnchorRegistry.ts +70 -0
- package/src/coachmarks/coachmarkOverlayStore.ts +63 -0
- package/src/coachmarks/index.ts +62 -0
- package/src/coachmarks/runtime.ts +103 -0
- package/src/coachmarks/selectTourSteps.ts +45 -0
- package/src/coachmarks/types.ts +58 -0
- package/src/coachmarks/useCoachmarkAnchor.ts +49 -0
- package/src/coachmarks/useCoachmarkTour.ts +200 -0
- package/src/components/AnimatedSparkle.tsx +101 -0
- package/src/components/Button.tsx +129 -0
- package/src/components/CardHandoff.tsx +212 -0
- package/src/components/CardLayout.tsx +103 -0
- package/src/components/CenteredModal.tsx +184 -0
- package/src/components/CompletionView.tsx +186 -0
- package/src/components/DoneBlock.tsx +37 -0
- package/src/components/ErrorBlock.tsx +42 -0
- package/src/components/Illustration.tsx +36 -0
- package/src/components/LoadingBlock.tsx +182 -0
- package/src/components/LoadingScreen.tsx +35 -0
- package/src/components/OnboardingScaffold.tsx +105 -0
- package/src/components/StepProgress.tsx +85 -0
- package/src/components/loaderChrome.ts +28 -0
- package/src/config/onboardingFlag.ts +39 -0
- package/src/config/wireConfigFromEnv.ts +52 -0
- package/src/device/deviceContext.ts +158 -0
- package/src/features/WireFeaturesProvider.tsx +81 -0
- package/src/features/cache.ts +92 -0
- package/src/features/defaults.ts +67 -0
- package/src/features/fetchWireFeatures.ts +86 -0
- package/src/features/index.ts +39 -0
- package/src/features/types.ts +61 -0
- package/src/features/useWireFeatures.ts +57 -0
- package/src/identity/userIdentity.ts +100 -0
- package/src/illustrations/defaultIllustrations.tsx +99 -0
- package/src/index.ts +159 -0
- package/src/motion/centeredModalMotion.ts +45 -0
- package/src/motion/motionSpec.ts +133 -0
- package/src/motion/useReducedMotion.ts +43 -0
- package/src/questionnaire/QuestionnaireGate.tsx +235 -0
- package/src/questionnaire/decision.ts +63 -0
- package/src/questionnaire/index.ts +54 -0
- package/src/questionnaire/runtime.ts +20 -0
- package/src/questionnaire/transport.ts +83 -0
- package/src/questionnaire/types.ts +182 -0
- package/src/questionnaire/useQuestionnaireGate.ts +159 -0
- package/src/reviews/ReviewGate.tsx +316 -0
- package/src/reviews/ReviewModal.tsx +13 -0
- package/src/reviews/decision.ts +120 -0
- package/src/reviews/equality.ts +54 -0
- package/src/reviews/expo-store-review.d.ts +16 -0
- package/src/reviews/feedbackForm.ts +41 -0
- package/src/reviews/index.ts +78 -0
- package/src/reviews/presentation.ts +29 -0
- package/src/reviews/runtime.ts +45 -0
- package/src/reviews/storeReview.ts +62 -0
- package/src/reviews/transport.ts +89 -0
- package/src/reviews/types.ts +172 -0
- package/src/reviews/useReviewGate.ts +137 -0
- package/src/session/persistedSession.ts +143 -0
- package/src/session-analytics/index.ts +18 -0
- package/src/session-analytics/reportSessionStart.ts +144 -0
- package/src/session-analytics/useSessionStart.ts +114 -0
- package/src/showcase/FeatureShowcase.tsx +233 -0
- package/src/showcase/index.ts +19 -0
- package/src/showcase/selectShowcaseSlides.ts +45 -0
- package/src/showcase/showcaseColors.ts +91 -0
- package/src/showcase/types.ts +74 -0
- package/src/theme/ThemeContext.tsx +38 -0
- package/src/theme/defaultTheme.ts +70 -0
- package/src/theme/themeFromBrand.ts +93 -0
- package/src/theme/types.ts +88 -0
- package/src/theme/typography.ts +46 -0
- package/src/types.ts +215 -0
- package/src/utils/deriveAnswers.ts +43 -0
- package/src/utils/readProgress.ts +33 -0
|
@@ -0,0 +1,660 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var Onboarding = require('@blazejkustra/react-native-onboarding');
|
|
4
|
+
var React = require('react');
|
|
5
|
+
var reactNative = require('react-native');
|
|
6
|
+
var Animated = require('react-native-reanimated');
|
|
7
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
8
|
+
|
|
9
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
10
|
+
|
|
11
|
+
var Onboarding__default = /*#__PURE__*/_interopDefault(Onboarding);
|
|
12
|
+
var React__default = /*#__PURE__*/_interopDefault(React);
|
|
13
|
+
var Animated__default = /*#__PURE__*/_interopDefault(Animated);
|
|
14
|
+
|
|
15
|
+
// src/showcase/FeatureShowcase.tsx
|
|
16
|
+
var SWIPE_MS = 700;
|
|
17
|
+
var TAP_MS = 320;
|
|
18
|
+
var SPREAD_MS = 620;
|
|
19
|
+
var GLYPH_ROTATION = {
|
|
20
|
+
swipe_up: "0deg",
|
|
21
|
+
swipe_down: "180deg",
|
|
22
|
+
swipe_left: "-90deg",
|
|
23
|
+
swipe_right: "90deg"
|
|
24
|
+
};
|
|
25
|
+
var FingerPair = ({ size, color }) => {
|
|
26
|
+
const { row, index, middle } = React.useMemo(() => {
|
|
27
|
+
const w = size * 0.2;
|
|
28
|
+
const r = w / 2;
|
|
29
|
+
return {
|
|
30
|
+
row: { flexDirection: "row", alignItems: "flex-end", gap: size * 0.08 },
|
|
31
|
+
index: { width: w, height: size * 0.54, borderRadius: r, backgroundColor: color },
|
|
32
|
+
middle: { width: w, height: size * 0.44, borderRadius: r, backgroundColor: color }
|
|
33
|
+
};
|
|
34
|
+
}, [size, color]);
|
|
35
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: row, pointerEvents: "none", children: [
|
|
36
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: index }),
|
|
37
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: middle })
|
|
38
|
+
] });
|
|
39
|
+
};
|
|
40
|
+
var MotionTrack = ({ size, color }) => {
|
|
41
|
+
const { wrap, tick0, tick1 } = React.useMemo(() => {
|
|
42
|
+
const h = Math.max(2, size * 0.05);
|
|
43
|
+
return {
|
|
44
|
+
wrap: { alignItems: "center", marginTop: size * 0.1, gap: size * 0.06 },
|
|
45
|
+
tick0: {
|
|
46
|
+
width: size * 0.3,
|
|
47
|
+
height: h,
|
|
48
|
+
borderRadius: h / 2,
|
|
49
|
+
backgroundColor: color,
|
|
50
|
+
opacity: 0.4
|
|
51
|
+
},
|
|
52
|
+
tick1: {
|
|
53
|
+
width: size * 0.18,
|
|
54
|
+
height: h,
|
|
55
|
+
borderRadius: h / 2,
|
|
56
|
+
backgroundColor: color,
|
|
57
|
+
opacity: 0.16
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}, [size, color]);
|
|
61
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: wrap, pointerEvents: "none", children: [
|
|
62
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: tick0 }),
|
|
63
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: tick1 })
|
|
64
|
+
] });
|
|
65
|
+
};
|
|
66
|
+
var GestureHintComponent = ({
|
|
67
|
+
type,
|
|
68
|
+
size = 34,
|
|
69
|
+
color = "white",
|
|
70
|
+
renderHand
|
|
71
|
+
}) => {
|
|
72
|
+
var _a;
|
|
73
|
+
const translate = Animated.useSharedValue(0);
|
|
74
|
+
const opacity = Animated.useSharedValue(1);
|
|
75
|
+
const pulse = Animated.useSharedValue(1);
|
|
76
|
+
const spread = Animated.useSharedValue(0);
|
|
77
|
+
const reduced = Animated.useReducedMotion();
|
|
78
|
+
const isSwipe = type === "swipe_up" || type === "swipe_down" || type === "swipe_left" || type === "swipe_right";
|
|
79
|
+
const isSpread = type === "pinch" || type === "zoom";
|
|
80
|
+
const isTap = type === "tap" || type === "double_tap";
|
|
81
|
+
React.useEffect(() => {
|
|
82
|
+
if (type === "none" || reduced) return void 0;
|
|
83
|
+
if (isTap) {
|
|
84
|
+
pulse.value = Animated.withRepeat(
|
|
85
|
+
Animated.withSequence(
|
|
86
|
+
Animated.withTiming(0.82, { duration: TAP_MS }),
|
|
87
|
+
Animated.withTiming(1, { duration: TAP_MS })
|
|
88
|
+
),
|
|
89
|
+
-1,
|
|
90
|
+
false
|
|
91
|
+
);
|
|
92
|
+
opacity.value = Animated.withRepeat(
|
|
93
|
+
Animated.withSequence(
|
|
94
|
+
Animated.withTiming(0.55, { duration: TAP_MS }),
|
|
95
|
+
Animated.withTiming(1, { duration: TAP_MS })
|
|
96
|
+
),
|
|
97
|
+
-1,
|
|
98
|
+
false
|
|
99
|
+
);
|
|
100
|
+
return void 0;
|
|
101
|
+
}
|
|
102
|
+
if (isSpread) {
|
|
103
|
+
spread.value = Animated.withRepeat(
|
|
104
|
+
Animated.withSequence(
|
|
105
|
+
Animated.withTiming(size * 0.26, { duration: SPREAD_MS }),
|
|
106
|
+
Animated.withTiming(0, { duration: SPREAD_MS })
|
|
107
|
+
),
|
|
108
|
+
-1,
|
|
109
|
+
false
|
|
110
|
+
);
|
|
111
|
+
return void 0;
|
|
112
|
+
}
|
|
113
|
+
translate.value = Animated.withRepeat(
|
|
114
|
+
Animated.withSequence(
|
|
115
|
+
Animated.withTiming(0, { duration: 0 }),
|
|
116
|
+
Animated.withTiming(-26, { duration: SWIPE_MS }),
|
|
117
|
+
Animated.withDelay(140, Animated.withTiming(0, { duration: 0 }))
|
|
118
|
+
),
|
|
119
|
+
-1,
|
|
120
|
+
false
|
|
121
|
+
);
|
|
122
|
+
opacity.value = Animated.withRepeat(
|
|
123
|
+
Animated.withSequence(
|
|
124
|
+
Animated.withTiming(1, { duration: SWIPE_MS / 2 }),
|
|
125
|
+
Animated.withTiming(0, { duration: SWIPE_MS / 2 }),
|
|
126
|
+
Animated.withDelay(140, Animated.withTiming(1, { duration: 0 }))
|
|
127
|
+
),
|
|
128
|
+
-1,
|
|
129
|
+
false
|
|
130
|
+
);
|
|
131
|
+
return void 0;
|
|
132
|
+
}, [type, reduced, isTap, isSpread, translate, opacity, pulse, spread, size]);
|
|
133
|
+
const swipeAnimatedStyle = Animated.useAnimatedStyle(() => ({
|
|
134
|
+
opacity: opacity.value,
|
|
135
|
+
transform: [{ translateY: translate.value }]
|
|
136
|
+
}));
|
|
137
|
+
const tapAnimatedStyle = Animated.useAnimatedStyle(() => ({
|
|
138
|
+
opacity: opacity.value,
|
|
139
|
+
transform: [{ scale: pulse.value }]
|
|
140
|
+
}));
|
|
141
|
+
const spreadLeftStyle = Animated.useAnimatedStyle(() => ({
|
|
142
|
+
transform: [{ translateX: -spread.value }]
|
|
143
|
+
}));
|
|
144
|
+
const spreadRightStyle = Animated.useAnimatedStyle(() => ({
|
|
145
|
+
transform: [{ translateX: spread.value }]
|
|
146
|
+
}));
|
|
147
|
+
if (type === "none") return null;
|
|
148
|
+
const customGlyph = renderHand ? renderHand({ size, color }) : null;
|
|
149
|
+
if (isSwipe) {
|
|
150
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
151
|
+
reactNative.View,
|
|
152
|
+
{
|
|
153
|
+
style: { transform: [{ rotate: (_a = GLYPH_ROTATION[type]) != null ? _a : "0deg" }] },
|
|
154
|
+
pointerEvents: "none",
|
|
155
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles.swipeStack, children: [
|
|
156
|
+
/* @__PURE__ */ jsxRuntime.jsx(Animated__default.default.View, { style: swipeAnimatedStyle, pointerEvents: "none", children: customGlyph != null ? customGlyph : /* @__PURE__ */ jsxRuntime.jsx(FingerPair, { size, color }) }),
|
|
157
|
+
/* @__PURE__ */ jsxRuntime.jsx(MotionTrack, { size, color })
|
|
158
|
+
] })
|
|
159
|
+
}
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
if (isSpread) {
|
|
163
|
+
const w = size * 0.2;
|
|
164
|
+
const capsule = {
|
|
165
|
+
width: w,
|
|
166
|
+
height: size * 0.5,
|
|
167
|
+
borderRadius: w / 2,
|
|
168
|
+
backgroundColor: color
|
|
169
|
+
};
|
|
170
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles.spreadRow, { gap: size * 0.12 }], pointerEvents: "none", children: [
|
|
171
|
+
/* @__PURE__ */ jsxRuntime.jsx(Animated__default.default.View, { style: spreadLeftStyle, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: capsule }) }),
|
|
172
|
+
/* @__PURE__ */ jsxRuntime.jsx(Animated__default.default.View, { style: spreadRightStyle, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: capsule }) })
|
|
173
|
+
] });
|
|
174
|
+
}
|
|
175
|
+
const ringSize = size * 0.86;
|
|
176
|
+
const ringBorder = Math.max(2, size * 0.08);
|
|
177
|
+
const dotSize = size * 0.32;
|
|
178
|
+
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: [
|
|
179
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
180
|
+
Animated__default.default.View,
|
|
181
|
+
{
|
|
182
|
+
style: [
|
|
183
|
+
styles.tapRing,
|
|
184
|
+
{
|
|
185
|
+
width: ringSize,
|
|
186
|
+
height: ringSize,
|
|
187
|
+
borderRadius: ringSize / 2,
|
|
188
|
+
borderWidth: ringBorder,
|
|
189
|
+
borderColor: color
|
|
190
|
+
},
|
|
191
|
+
tapAnimatedStyle
|
|
192
|
+
],
|
|
193
|
+
pointerEvents: "none"
|
|
194
|
+
}
|
|
195
|
+
),
|
|
196
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
197
|
+
reactNative.View,
|
|
198
|
+
{
|
|
199
|
+
style: {
|
|
200
|
+
position: "absolute",
|
|
201
|
+
width: dotSize,
|
|
202
|
+
height: dotSize,
|
|
203
|
+
borderRadius: dotSize / 2,
|
|
204
|
+
backgroundColor: color
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
)
|
|
208
|
+
] }) });
|
|
209
|
+
};
|
|
210
|
+
var styles = reactNative.StyleSheet.create({
|
|
211
|
+
swipeStack: {
|
|
212
|
+
alignItems: "center",
|
|
213
|
+
justifyContent: "center"
|
|
214
|
+
},
|
|
215
|
+
spreadRow: {
|
|
216
|
+
flexDirection: "row",
|
|
217
|
+
alignItems: "center",
|
|
218
|
+
justifyContent: "center"
|
|
219
|
+
},
|
|
220
|
+
tapWrap: {
|
|
221
|
+
alignItems: "center",
|
|
222
|
+
justifyContent: "center"
|
|
223
|
+
},
|
|
224
|
+
tapRing: {
|
|
225
|
+
backgroundColor: "transparent"
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
var GestureHint = React__default.default.memo(GestureHintComponent);
|
|
229
|
+
GestureHint.displayName = "GestureHint";
|
|
230
|
+
|
|
231
|
+
// src/coachmarks/runtime.ts
|
|
232
|
+
var storageSingleton = null;
|
|
233
|
+
var testing = false;
|
|
234
|
+
var showcaseGateKey = (showcaseId) => `wire_showcase_${showcaseId}_seen`;
|
|
235
|
+
var SEEN_VALUE = "1";
|
|
236
|
+
var hasSeenGate = (key, storageOverride, testingOverride) => {
|
|
237
|
+
if (testingOverride != null ? testingOverride : testing) return false;
|
|
238
|
+
const storage = storageOverride != null ? storageOverride : storageSingleton;
|
|
239
|
+
if (!storage) return false;
|
|
240
|
+
try {
|
|
241
|
+
return storage.getItem(key) === SEEN_VALUE;
|
|
242
|
+
} catch {
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
var markSeenGate = (key, storageOverride, testingOverride) => {
|
|
247
|
+
if (testingOverride != null ? testingOverride : testing) return;
|
|
248
|
+
const storage = storageOverride != null ? storageOverride : storageSingleton;
|
|
249
|
+
if (!storage) return;
|
|
250
|
+
try {
|
|
251
|
+
storage.setItem(key, SEEN_VALUE);
|
|
252
|
+
} catch {
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
// src/features/defaults.ts
|
|
257
|
+
var defaultWireFeatures = Object.freeze({
|
|
258
|
+
coachmarks: Object.freeze({ enabled: true }),
|
|
259
|
+
showcase: Object.freeze({ enabled: true }),
|
|
260
|
+
onboarding: Object.freeze({ mode: "ai" }),
|
|
261
|
+
review: Object.freeze({ enabled: true }),
|
|
262
|
+
questionnaire: Object.freeze({ enabled: true }),
|
|
263
|
+
announcements: Object.freeze({ enabled: true })
|
|
264
|
+
});
|
|
265
|
+
var VALID_MODES = ["ai", "static", "off"];
|
|
266
|
+
var readEnabled = (raw) => {
|
|
267
|
+
if (raw && typeof raw === "object" && typeof raw.enabled === "boolean") {
|
|
268
|
+
return { enabled: raw.enabled };
|
|
269
|
+
}
|
|
270
|
+
return { enabled: true };
|
|
271
|
+
};
|
|
272
|
+
var parseWireFeatures = (raw) => {
|
|
273
|
+
var _a;
|
|
274
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return defaultWireFeatures;
|
|
275
|
+
const src = raw;
|
|
276
|
+
const modeRaw = (_a = src.onboarding) == null ? void 0 : _a.mode;
|
|
277
|
+
const mode = typeof modeRaw === "string" && VALID_MODES.includes(modeRaw) ? modeRaw : "ai";
|
|
278
|
+
return {
|
|
279
|
+
coachmarks: readEnabled(src.coachmarks),
|
|
280
|
+
showcase: readEnabled(src.showcase),
|
|
281
|
+
onboarding: { mode },
|
|
282
|
+
review: readEnabled(src.review),
|
|
283
|
+
questionnaire: readEnabled(src.questionnaire),
|
|
284
|
+
announcements: readEnabled(src.announcements)
|
|
285
|
+
};
|
|
286
|
+
};
|
|
287
|
+
var featuresEqual = (a, b) => a === b || a.coachmarks.enabled === b.coachmarks.enabled && a.showcase.enabled === b.showcase.enabled && a.onboarding.mode === b.onboarding.mode && a.review.enabled === b.review.enabled && a.questionnaire.enabled === b.questionnaire.enabled && a.announcements.enabled === b.announcements.enabled;
|
|
288
|
+
|
|
289
|
+
// src/features/cache.ts
|
|
290
|
+
var READ_TIMEOUT_MS = 1500;
|
|
291
|
+
var featuresCacheKey = (appId) => `wireai:features:${appId && appId.length > 0 ? appId : "default"}`;
|
|
292
|
+
var withTimeout = (p, ms) => {
|
|
293
|
+
let timer;
|
|
294
|
+
const timeout = new Promise((resolve) => {
|
|
295
|
+
timer = setTimeout(() => resolve(void 0), ms);
|
|
296
|
+
});
|
|
297
|
+
return Promise.race([p, timeout]).finally(() => clearTimeout(timer));
|
|
298
|
+
};
|
|
299
|
+
var readCachedFeatures = async (storage, key) => {
|
|
300
|
+
let raw;
|
|
301
|
+
try {
|
|
302
|
+
raw = await withTimeout(storage.getItem(key), READ_TIMEOUT_MS);
|
|
303
|
+
} catch {
|
|
304
|
+
return void 0;
|
|
305
|
+
}
|
|
306
|
+
if (!raw) return void 0;
|
|
307
|
+
try {
|
|
308
|
+
const parsed = JSON.parse(raw);
|
|
309
|
+
if (parsed && typeof parsed === "object" && typeof parsed.ts === "number") {
|
|
310
|
+
const ts = parsed.ts;
|
|
311
|
+
return {
|
|
312
|
+
features: parseWireFeatures(parsed.features),
|
|
313
|
+
ageMs: Date.now() - ts
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
} catch {
|
|
317
|
+
}
|
|
318
|
+
return void 0;
|
|
319
|
+
};
|
|
320
|
+
var writeCachedFeatures = (storage, key, features) => {
|
|
321
|
+
try {
|
|
322
|
+
void storage.setItem(key, JSON.stringify({ features, ts: Date.now() })).catch(() => {
|
|
323
|
+
});
|
|
324
|
+
} catch {
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
// src/features/fetchWireFeatures.ts
|
|
329
|
+
var DEFAULT_TIMEOUT_MS = 4e3;
|
|
330
|
+
var failOpen = async (storage, key) => {
|
|
331
|
+
var _a;
|
|
332
|
+
if (!storage) return defaultWireFeatures;
|
|
333
|
+
const cached = await readCachedFeatures(storage, key);
|
|
334
|
+
return (_a = cached == null ? void 0 : cached.features) != null ? _a : defaultWireFeatures;
|
|
335
|
+
};
|
|
336
|
+
var fetchWithTimeout = async (url, apiKey, timeoutMs) => {
|
|
337
|
+
const controller = typeof AbortController !== "undefined" ? new AbortController() : void 0;
|
|
338
|
+
const timer = setTimeout(() => controller == null ? void 0 : controller.abort(), timeoutMs);
|
|
339
|
+
try {
|
|
340
|
+
return await fetch(url, {
|
|
341
|
+
method: "GET",
|
|
342
|
+
headers: { Accept: "application/json", Authorization: `Bearer ${apiKey}` },
|
|
343
|
+
signal: controller == null ? void 0 : controller.signal
|
|
344
|
+
});
|
|
345
|
+
} finally {
|
|
346
|
+
clearTimeout(timer);
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
var fetchWireFeatures = async (config) => {
|
|
350
|
+
const key = featuresCacheKey(config == null ? void 0 : config.appId);
|
|
351
|
+
const storage = config == null ? void 0 : config.storage;
|
|
352
|
+
if (!(config == null ? void 0 : config.serverUrl) || !(config == null ? void 0 : config.apiKey)) return failOpen(storage, key);
|
|
353
|
+
const url = `${config.serverUrl.replace(/\/$/, "")}/v1/features`;
|
|
354
|
+
const timeoutMs = config.timeoutMs && config.timeoutMs > 0 ? config.timeoutMs : DEFAULT_TIMEOUT_MS;
|
|
355
|
+
try {
|
|
356
|
+
const res = await fetchWithTimeout(url, config.apiKey, timeoutMs);
|
|
357
|
+
if (!res || !res.ok) return failOpen(storage, key);
|
|
358
|
+
const json = await res.json();
|
|
359
|
+
const features = parseWireFeatures(json);
|
|
360
|
+
if (storage) writeCachedFeatures(storage, key, features);
|
|
361
|
+
return features;
|
|
362
|
+
} catch {
|
|
363
|
+
return failOpen(storage, key);
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
// src/features/useWireFeatures.ts
|
|
368
|
+
var useStableValue = (value, isEqual) => {
|
|
369
|
+
const ref = React.useRef(value);
|
|
370
|
+
if (!isEqual(ref.current, value)) ref.current = value;
|
|
371
|
+
return ref.current;
|
|
372
|
+
};
|
|
373
|
+
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);
|
|
374
|
+
var useWireFeatures = (config) => {
|
|
375
|
+
const stableConfig = useStableValue(config, sameFetchKey);
|
|
376
|
+
const [flags, setFlags] = React.useState(defaultWireFeatures);
|
|
377
|
+
const canFetch = !!(stableConfig == null ? void 0 : stableConfig.serverUrl) && !!(stableConfig == null ? void 0 : stableConfig.apiKey);
|
|
378
|
+
React.useEffect(() => {
|
|
379
|
+
if (!canFetch) {
|
|
380
|
+
setFlags((prev) => featuresEqual(prev, defaultWireFeatures) ? prev : defaultWireFeatures);
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
let alive = true;
|
|
384
|
+
void fetchWireFeatures(stableConfig).then((next) => {
|
|
385
|
+
if (!alive) return;
|
|
386
|
+
setFlags((prev) => featuresEqual(prev, next) ? prev : next);
|
|
387
|
+
});
|
|
388
|
+
return () => {
|
|
389
|
+
alive = false;
|
|
390
|
+
};
|
|
391
|
+
}, [canFetch, stableConfig]);
|
|
392
|
+
return flags;
|
|
393
|
+
};
|
|
394
|
+
var WireFeaturesContext = React.createContext(null);
|
|
395
|
+
var useWireFeaturesContext = () => React.useContext(WireFeaturesContext);
|
|
396
|
+
var useResolvedFeatures = (options) => {
|
|
397
|
+
var _a, _b, _c;
|
|
398
|
+
const ctx = useWireFeaturesContext();
|
|
399
|
+
const shouldFetch = !(options == null ? void 0 : options.flags) && ctx == null;
|
|
400
|
+
const fetched = useWireFeatures(shouldFetch ? options == null ? void 0 : options.config : void 0);
|
|
401
|
+
return (_c = (_b = (_a = options == null ? void 0 : options.flags) != null ? _a : ctx) != null ? _b : fetched) != null ? _c : defaultWireFeatures;
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
// src/theme/defaultTheme.ts
|
|
405
|
+
var defaultOnboardingTheme = {
|
|
406
|
+
colors: {
|
|
407
|
+
background: "#FFFFFF",
|
|
408
|
+
surface: "#F7F7F8",
|
|
409
|
+
text: "#101114",
|
|
410
|
+
textMuted: "#6B7280",
|
|
411
|
+
primary: "#111111",
|
|
412
|
+
onPrimary: "#FFFFFF",
|
|
413
|
+
primarySoft: "rgba(17,17,17,0.06)",
|
|
414
|
+
border: "#E4E4E7",
|
|
415
|
+
borderStrong: "#C7C7CC",
|
|
416
|
+
disabled: "#EBEBEB",
|
|
417
|
+
success: "#16A34A",
|
|
418
|
+
warning: "#D97706",
|
|
419
|
+
error: "#DC2626",
|
|
420
|
+
info: "#2563EB"
|
|
421
|
+
},
|
|
422
|
+
fonts: {
|
|
423
|
+
regular: void 0,
|
|
424
|
+
medium: void 0,
|
|
425
|
+
bold: void 0,
|
|
426
|
+
baseSize: 16,
|
|
427
|
+
scale: 1
|
|
428
|
+
},
|
|
429
|
+
radius: {
|
|
430
|
+
sm: 8,
|
|
431
|
+
md: 12,
|
|
432
|
+
lg: 16,
|
|
433
|
+
full: 9999
|
|
434
|
+
},
|
|
435
|
+
spacing: {
|
|
436
|
+
xs: 4,
|
|
437
|
+
sm: 8,
|
|
438
|
+
md: 16,
|
|
439
|
+
lg: 24,
|
|
440
|
+
xl: 32
|
|
441
|
+
},
|
|
442
|
+
// Circled (pill) CTA by default; an app can pass `button: { radius: 12 }` for
|
|
443
|
+
// a rounded rectangle, or override height/paddingX to match its design system.
|
|
444
|
+
button: {
|
|
445
|
+
radius: 9999,
|
|
446
|
+
height: 52,
|
|
447
|
+
paddingX: 24
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
var mergeTheme = (override) => {
|
|
451
|
+
return defaultOnboardingTheme;
|
|
452
|
+
};
|
|
453
|
+
var OnboardingThemeContext = React.createContext(null);
|
|
454
|
+
var useOnboardingTheme = () => {
|
|
455
|
+
const ctx = React.useContext(OnboardingThemeContext);
|
|
456
|
+
return ctx != null ? ctx : mergeTheme();
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
// src/showcase/showcaseColors.ts
|
|
460
|
+
var showcaseColorsFromTheme = (t, accent) => ({
|
|
461
|
+
background: {
|
|
462
|
+
// Brand action color → primary button bg + intro highlight + image hero panel.
|
|
463
|
+
// (The hero panel is overridden to a calm surface via the package's
|
|
464
|
+
// `background` render prop — see showcasePanelBackground — so in practice
|
|
465
|
+
// this only paints the primary button + the intro highlight word.)
|
|
466
|
+
primary: accent,
|
|
467
|
+
// Neutral screen / modal / step-panel background.
|
|
468
|
+
secondary: t.colors.background,
|
|
469
|
+
// Step-label chip background (a soft wash of the brand).
|
|
470
|
+
label: t.colors.primarySoft,
|
|
471
|
+
// Secondary (outline) button border.
|
|
472
|
+
accent
|
|
473
|
+
},
|
|
474
|
+
text: {
|
|
475
|
+
primary: t.colors.text,
|
|
476
|
+
secondary: t.colors.textMuted,
|
|
477
|
+
// Primary button label — sits on `background.primary` (the accent).
|
|
478
|
+
contrast: t.colors.onPrimary
|
|
479
|
+
}
|
|
480
|
+
});
|
|
481
|
+
var SHOWCASE_PANEL_TINT_OPACITY = 0.1;
|
|
482
|
+
var showcasePanelBackground = (t, accent) => ({
|
|
483
|
+
base: t.colors.surface,
|
|
484
|
+
tint: { color: accent, opacity: SHOWCASE_PANEL_TINT_OPACITY }
|
|
485
|
+
});
|
|
486
|
+
var TRANSPARENT_PX = {
|
|
487
|
+
uri: "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
|
|
488
|
+
};
|
|
489
|
+
var mergeThemeOver = (base, override) => {
|
|
490
|
+
if (!override) return base;
|
|
491
|
+
return {
|
|
492
|
+
colors: { ...base.colors, ...override.colors },
|
|
493
|
+
fonts: { ...base.fonts, ...override.fonts },
|
|
494
|
+
radius: { ...base.radius, ...override.radius },
|
|
495
|
+
spacing: { ...base.spacing, ...override.spacing },
|
|
496
|
+
button: { ...base.button, ...override.button }
|
|
497
|
+
};
|
|
498
|
+
};
|
|
499
|
+
var FeatureShowcase = ({
|
|
500
|
+
config,
|
|
501
|
+
onDone,
|
|
502
|
+
theme: themeOverride,
|
|
503
|
+
accentColor,
|
|
504
|
+
buttonLabel = "Next",
|
|
505
|
+
storage,
|
|
506
|
+
isTesting,
|
|
507
|
+
features,
|
|
508
|
+
featuresConfig
|
|
509
|
+
}) => {
|
|
510
|
+
var _a;
|
|
511
|
+
const active = useOnboardingTheme();
|
|
512
|
+
const t = React.useMemo(
|
|
513
|
+
() => mergeThemeOver(active, themeOverride),
|
|
514
|
+
[active, themeOverride]
|
|
515
|
+
);
|
|
516
|
+
const accent = accentColor != null ? accentColor : t.colors.primary;
|
|
517
|
+
const flags = useResolvedFeatures({ flags: features, config: featuresConfig });
|
|
518
|
+
const disabled = !flags.showcase.enabled;
|
|
519
|
+
const gateKey = showcaseGateKey(config.id);
|
|
520
|
+
const seen = React.useMemo(
|
|
521
|
+
() => hasSeenGate(gateKey, storage, isTesting),
|
|
522
|
+
[gateKey, storage, isTesting]
|
|
523
|
+
);
|
|
524
|
+
const skip = seen || disabled;
|
|
525
|
+
const doneRef = React.useRef(false);
|
|
526
|
+
const finish = React.useCallback(() => {
|
|
527
|
+
if (doneRef.current) return;
|
|
528
|
+
doneRef.current = true;
|
|
529
|
+
markSeenGate(gateKey, storage, isTesting);
|
|
530
|
+
onDone();
|
|
531
|
+
}, [gateKey, storage, isTesting, onDone]);
|
|
532
|
+
React.useEffect(() => {
|
|
533
|
+
if (skip && !doneRef.current) {
|
|
534
|
+
doneRef.current = true;
|
|
535
|
+
onDone();
|
|
536
|
+
}
|
|
537
|
+
}, [skip, onDone]);
|
|
538
|
+
const [activeIndex, setActiveIndex] = React.useState(0);
|
|
539
|
+
const steps = React.useMemo(
|
|
540
|
+
() => config.slides.map((slide) => {
|
|
541
|
+
var _a2, _b, _c;
|
|
542
|
+
return {
|
|
543
|
+
title: slide.title,
|
|
544
|
+
description: slide.description,
|
|
545
|
+
buttonLabel: (_a2 = slide.buttonLabel) != null ? _a2 : buttonLabel,
|
|
546
|
+
image: (_b = slide.image) != null ? _b : TRANSPARENT_PX,
|
|
547
|
+
position: (_c = slide.imagePosition) != null ? _c : "top"
|
|
548
|
+
};
|
|
549
|
+
}),
|
|
550
|
+
[config.slides, buttonLabel]
|
|
551
|
+
);
|
|
552
|
+
const introPanel = React.useMemo(
|
|
553
|
+
() => config.introPanel ? {
|
|
554
|
+
title: config.introPanel.title,
|
|
555
|
+
subtitle: config.introPanel.subtitle,
|
|
556
|
+
button: config.introPanel.button,
|
|
557
|
+
image: config.introPanel.image
|
|
558
|
+
} : { button: buttonLabel },
|
|
559
|
+
[config.introPanel, buttonLabel]
|
|
560
|
+
);
|
|
561
|
+
const colors = React.useMemo(
|
|
562
|
+
() => showcaseColorsFromTheme(t, accent),
|
|
563
|
+
[t, accent]
|
|
564
|
+
);
|
|
565
|
+
const panelBg = React.useMemo(() => showcasePanelBackground(t, accent), [t, accent]);
|
|
566
|
+
const renderPanelBackground = React.useCallback(
|
|
567
|
+
() => /* @__PURE__ */ jsxRuntime.jsx(
|
|
568
|
+
reactNative.View,
|
|
569
|
+
{
|
|
570
|
+
style: [
|
|
571
|
+
styles2.panelBase,
|
|
572
|
+
{ backgroundColor: panelBg.base, borderRadius: t.radius.md }
|
|
573
|
+
],
|
|
574
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
575
|
+
reactNative.View,
|
|
576
|
+
{
|
|
577
|
+
style: [
|
|
578
|
+
reactNative.StyleSheet.absoluteFill,
|
|
579
|
+
{
|
|
580
|
+
backgroundColor: panelBg.tint.color,
|
|
581
|
+
opacity: panelBg.tint.opacity
|
|
582
|
+
}
|
|
583
|
+
]
|
|
584
|
+
}
|
|
585
|
+
)
|
|
586
|
+
}
|
|
587
|
+
),
|
|
588
|
+
[panelBg, t.radius.md]
|
|
589
|
+
);
|
|
590
|
+
const fonts = React.useMemo(
|
|
591
|
+
() => ({
|
|
592
|
+
introTitle: t.fonts.bold,
|
|
593
|
+
introSubtitle: t.fonts.regular,
|
|
594
|
+
introButton: t.fonts.bold,
|
|
595
|
+
stepLabel: t.fonts.medium,
|
|
596
|
+
stepTitle: t.fonts.bold,
|
|
597
|
+
stepDescription: t.fonts.regular,
|
|
598
|
+
stepButton: t.fonts.bold,
|
|
599
|
+
primaryButton: t.fonts.bold,
|
|
600
|
+
secondaryButton: t.fonts.medium
|
|
601
|
+
}),
|
|
602
|
+
[t]
|
|
603
|
+
);
|
|
604
|
+
if (skip) return null;
|
|
605
|
+
const activeGesture = (_a = config.slides[activeIndex]) == null ? void 0 : _a.gesture;
|
|
606
|
+
const showGesture = !!activeGesture && activeGesture !== "none" && activeGesture !== "tap" && activeGesture !== "double_tap";
|
|
607
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles2.fill, children: [
|
|
608
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
609
|
+
Onboarding__default.default,
|
|
610
|
+
{
|
|
611
|
+
introPanel,
|
|
612
|
+
steps,
|
|
613
|
+
colors,
|
|
614
|
+
fonts,
|
|
615
|
+
background: renderPanelBackground,
|
|
616
|
+
onComplete: finish,
|
|
617
|
+
onSkip: finish,
|
|
618
|
+
onStepChange: setActiveIndex
|
|
619
|
+
}
|
|
620
|
+
),
|
|
621
|
+
showGesture && /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles2.gestureLayer, pointerEvents: "none", children: /* @__PURE__ */ jsxRuntime.jsx(GestureHint, { type: activeGesture, color: accent }) })
|
|
622
|
+
] });
|
|
623
|
+
};
|
|
624
|
+
FeatureShowcase.displayName = "FeatureShowcase";
|
|
625
|
+
var styles2 = reactNative.StyleSheet.create({
|
|
626
|
+
fill: { flex: 1 },
|
|
627
|
+
// Fills the package's animated background box; clips the accent tint to the
|
|
628
|
+
// rounded corners so the calm panel reads as a card, not a bleed.
|
|
629
|
+
panelBase: { flex: 1, overflow: "hidden" },
|
|
630
|
+
gestureLayer: {
|
|
631
|
+
position: "absolute",
|
|
632
|
+
top: 0,
|
|
633
|
+
left: 0,
|
|
634
|
+
right: 0,
|
|
635
|
+
bottom: 0,
|
|
636
|
+
alignItems: "center",
|
|
637
|
+
justifyContent: "flex-end",
|
|
638
|
+
paddingBottom: 140
|
|
639
|
+
}
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
// src/showcase/selectShowcaseSlides.ts
|
|
643
|
+
var selectShowcaseSlides = (catalog, selection) => {
|
|
644
|
+
if (!selection) return catalog;
|
|
645
|
+
const byId = new Map(catalog.map((item) => [item.id, item]));
|
|
646
|
+
const seen = /* @__PURE__ */ new Set();
|
|
647
|
+
const ordered = [];
|
|
648
|
+
for (const id of selection) {
|
|
649
|
+
if (seen.has(id)) continue;
|
|
650
|
+
seen.add(id);
|
|
651
|
+
const item = byId.get(id);
|
|
652
|
+
if (item) ordered.push(item);
|
|
653
|
+
}
|
|
654
|
+
return ordered;
|
|
655
|
+
};
|
|
656
|
+
|
|
657
|
+
exports.FeatureShowcase = FeatureShowcase;
|
|
658
|
+
exports.selectShowcaseSlides = selectShowcaseSlides;
|
|
659
|
+
//# sourceMappingURL=index.js.map
|
|
660
|
+
//# sourceMappingURL=index.js.map
|