@variantlab/react-native 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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/overlay/experiment-card.tsx","../src/overlay/filter.ts","../src/overlay/imperative.ts","../src/overlay/bottom-sheet.tsx","../src/overlay/floating-button.tsx","../src/overlay/search-input.tsx","../src/overlay/tabs/config.tsx","../src/overlay/tabs/context.tsx","../src/overlay/tabs/history.tsx","../src/overlay/tabs/overview.tsx","../src/overlay/theme.ts","../src/overlay/use-engine-snapshot.ts","../src/overlay/index.tsx"],"names":["jsxs","View","Pressable","jsx","Text","StyleSheet","useRef","Animated","useEffect","Modal","styles","TextInput","ScrollView","useMemo","useState","useCallback","useSyncExternalStore","useVariantLabEngine","Fragment"],"mappings":"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","file":"debug.cjs","sourcesContent":["/**\n * One experiment card in the bottom sheet.\n *\n * Each card collapses by default to keep the list scannable, expands\n * on tap to reveal a radio picker over the variants, and shows the\n * currently active variant with a badge. The card is fully controlled\n * by the parent — expansion state lives in the overlay so toggling\n * one card never re-renders the others.\n *\n * Variant rows expose a row-press handler instead of a real radio\n * widget; React Native's stock <Switch> looks out of place at the\n * sizes we want. The chosen variant gets an inline highlight ring\n * via the theme accent so it's obvious which variant is active.\n */\n\nimport type { Experiment } from \"@variantlab/core\";\nimport type { ReactElement } from \"react\";\nimport { Pressable, StyleSheet, Text, View } from \"react-native\";\nimport type { OverlayTheme } from \"./theme.js\";\n\nexport interface ExperimentCardProps {\n theme: OverlayTheme;\n experiment: Experiment;\n activeVariantId: string;\n expanded: boolean;\n onToggleExpand: () => void;\n onSelect: (variantId: string) => void;\n onReset: () => void;\n}\n\nexport function ExperimentCard({\n theme,\n experiment,\n activeVariantId,\n expanded,\n onToggleExpand,\n onSelect,\n onReset,\n}: ExperimentCardProps): ReactElement {\n return (\n <View\n style={[styles.card, { backgroundColor: theme.surface, borderColor: theme.border }]}\n testID={`variantlab-experiment-card-${experiment.id}`}\n >\n <Pressable\n onPress={onToggleExpand}\n accessibilityRole=\"button\"\n accessibilityLabel={`Experiment ${experiment.name}, current variant ${activeVariantId}`}\n style={styles.header}\n >\n <View style={styles.headerText}>\n <Text style={[styles.name, { color: theme.text }]} numberOfLines={1}>\n {experiment.name}\n </Text>\n <Text style={[styles.id, { color: theme.textMuted }]} numberOfLines={1}>\n {experiment.id}\n </Text>\n </View>\n <View style={[styles.activePill, { backgroundColor: theme.accent }]}>\n <Text style={[styles.activePillText, { color: theme.accentText }]}>\n {activeVariantId}\n </Text>\n </View>\n </Pressable>\n {expanded ? (\n <View style={styles.body}>\n {experiment.variants.map((v) => {\n const isActive = v.id === activeVariantId;\n return (\n <Pressable\n key={v.id}\n onPress={() => onSelect(v.id)}\n accessibilityRole=\"button\"\n accessibilityState={{ selected: isActive }}\n accessibilityLabel={`Set variant ${v.id}`}\n style={[\n styles.variantRow,\n {\n borderColor: isActive ? theme.accent : theme.border,\n backgroundColor: isActive ? `${theme.accent}22` : \"transparent\",\n },\n ]}\n testID={`variantlab-variant-row-${experiment.id}-${v.id}`}\n >\n <Text style={[styles.variantId, { color: theme.text }]}>{v.label ?? v.id}</Text>\n {v.description !== undefined ? (\n <Text style={[styles.variantDesc, { color: theme.textMuted }]} numberOfLines={2}>\n {v.description}\n </Text>\n ) : null}\n </Pressable>\n );\n })}\n <Pressable\n onPress={onReset}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Reset to default\"\n style={styles.resetRow}\n testID={`variantlab-reset-${experiment.id}`}\n >\n <Text style={[styles.resetText, { color: theme.textMuted }]}>Reset to default</Text>\n </Pressable>\n </View>\n ) : null}\n </View>\n );\n}\n\n/**\n * Pure helper used by both the card and the overlay's \"manual override\"\n * indicator: returns whether the active variant was forced or assigned\n * naturally. Exported so unit tests can exercise the logic without\n * spinning up a renderer.\n */\nexport function describeAssignmentSource(\n hasOverride: boolean,\n matchedTargeting: boolean,\n): \"manual override\" | \"by targeting\" | \"by default\" {\n if (hasOverride) return \"manual override\";\n if (matchedTargeting) return \"by targeting\";\n return \"by default\";\n}\n\n// `as unknown as undefined` casts work around the React Native style\n// type — the values are real strings the runtime accepts.\nconst styles = StyleSheet.create({\n card: {\n borderRadius: 10,\n borderWidth: 1,\n marginBottom: 8,\n overflow: \"hidden\" as unknown as undefined,\n },\n header: {\n flexDirection: \"row\" as unknown as undefined,\n alignItems: \"center\",\n padding: 12,\n gap: 8,\n },\n headerText: {\n flex: 1,\n flexDirection: \"column\" as unknown as undefined,\n },\n name: {\n fontSize: 14,\n fontWeight: \"600\",\n },\n id: {\n fontSize: 11,\n fontFamily: \"Menlo, monospace\",\n marginTop: 2,\n },\n activePill: {\n borderRadius: 999,\n paddingHorizontal: 10,\n paddingVertical: 4,\n },\n activePillText: {\n fontSize: 11,\n fontWeight: \"700\",\n },\n body: {\n paddingHorizontal: 12,\n paddingBottom: 12,\n gap: 6,\n },\n variantRow: {\n borderRadius: 8,\n borderWidth: 1,\n paddingHorizontal: 10,\n paddingVertical: 8,\n },\n variantId: {\n fontSize: 13,\n fontWeight: \"600\",\n },\n variantDesc: {\n fontSize: 11,\n marginTop: 2,\n },\n resetRow: {\n paddingVertical: 8,\n alignItems: \"flex-end\",\n },\n resetText: {\n fontSize: 11,\n textDecorationLine: \"underline\" as unknown as undefined,\n },\n});\n","/**\n * Pure helpers for the overlay's experiment filtering pipeline.\n *\n * The overlay applies three independent filters to the engine's\n * experiment list:\n *\n * 1. Route filter — only show experiments whose `routes` glob\n * matches the current path. Provided by core via\n * `engine.getExperiments(route)`, but the overlay also needs to\n * strip archived experiments which core does not.\n * 2. \"Hidden when archived\" — `status: \"archived\"` experiments\n * should never appear in the picker. They live in core for\n * historical inspection.\n * 3. Search query — case-insensitive substring match across the\n * id, name, and any variant id/label.\n *\n * Splitting these out into a pure module makes them trivial to unit\n * test without spinning up a renderer.\n */\nimport type { Experiment } from \"@variantlab/core\";\n\nexport function isVisibleExperiment(experiment: Experiment): boolean {\n return experiment.status !== \"archived\";\n}\n\nexport function matchesSearch(experiment: Experiment, query: string): boolean {\n if (query === \"\") return true;\n const needle = query.toLowerCase();\n if (experiment.id.toLowerCase().includes(needle)) return true;\n if (experiment.name.toLowerCase().includes(needle)) return true;\n for (const v of experiment.variants) {\n if (v.id.toLowerCase().includes(needle)) return true;\n if (v.label !== undefined && v.label.toLowerCase().includes(needle)) return true;\n }\n return false;\n}\n\nexport function filterExperiments(\n list: readonly Experiment[],\n query: string,\n): readonly Experiment[] {\n const out: Experiment[] = [];\n for (const exp of list) {\n if (!isVisibleExperiment(exp)) continue;\n if (!matchesSearch(exp, query)) continue;\n out.push(exp);\n }\n return out;\n}\n","/**\n * Imperative open / close API for the debug overlay.\n *\n * Some debug surfaces want to open the overlay from outside React —\n * a long-press on a Detox e2e harness, a custom shake handler, a\n * react-navigation menu item. We expose a tiny pub/sub so any number\n * of mounted overlays can be opened in lock-step:\n *\n * import { openDebugOverlay } from \"@variantlab/react-native/debug\";\n *\n * shakeListener.on(\"shake\", openDebugOverlay);\n *\n * The overlay registers/unregisters its `setVisible` callback in\n * `useEffect`, and the imperative helpers fan calls out to every\n * registered callback. In practice almost every app mounts a single\n * overlay so the set has size 1; the registry simply guarantees we\n * don't crash if a developer mounts two.\n */\n\nconst subscribers = new Set<(visible: boolean) => void>();\n\nexport function registerOverlay(setVisible: (visible: boolean) => void): () => void {\n subscribers.add(setVisible);\n return () => {\n subscribers.delete(setVisible);\n };\n}\n\nexport function openDebugOverlay(): void {\n for (const fn of subscribers) fn(true);\n}\n\nexport function closeDebugOverlay(): void {\n for (const fn of subscribers) fn(false);\n}\n","/**\n * Hand-rolled bottom sheet.\n *\n * Wraps the content in `<Modal>` (so it floats above the host app's\n * navigation stack) and animates in via `Animated.timing` on a value\n * representing the slide-up progress (0 = off-screen, 1 = fully open).\n *\n * We avoid `react-native-bottom-sheet` and similar third-party libs\n * because:\n *\n * 1. They are huge — typical bottom-sheet libs ship 30+ KB minified.\n * 2. They depend on `react-native-reanimated` and `react-native-gesture-handler`,\n * which we cannot reasonably make peer deps without forcing every\n * Expo user to install them.\n * 3. Our needs are tiny: tap to dismiss, fixed max height, no drag.\n *\n * Animation honours `prefers-reduced-motion` by collapsing the slide\n * to an instant fade — but RN doesn't expose a stable hook for that\n * preference, so we conservatively skip it for now and revisit when\n * `AccessibilityInfo.isReduceMotionEnabled()` is wired in (Phase 2).\n */\nimport { type ReactElement, type ReactNode, useEffect, useRef } from \"react\";\nimport { Animated, Modal, Pressable, StyleSheet, View } from \"react-native\";\nimport type { OverlayTheme } from \"./theme.js\";\n\nexport interface BottomSheetProps {\n visible: boolean;\n onRequestClose: () => void;\n theme: OverlayTheme;\n insetBottom?: number;\n children: ReactNode;\n}\n\nexport function BottomSheet({\n visible,\n onRequestClose,\n theme,\n insetBottom = 0,\n children,\n}: BottomSheetProps): ReactElement {\n const progress = useRef(new Animated.Value(0)).current;\n\n useEffect(() => {\n Animated.timing(progress, {\n toValue: visible ? 1 : 0,\n duration: 200,\n useNativeDriver: true,\n }).start();\n }, [visible, progress]);\n\n const translateY = progress.interpolate({\n inputRange: [0, 1],\n outputRange: [600, 0],\n });\n const opacity = progress.interpolate({\n inputRange: [0, 1],\n outputRange: [0, 1],\n });\n\n return (\n <Modal\n visible={visible}\n transparent\n animationType=\"none\"\n onRequestClose={onRequestClose}\n statusBarTranslucent\n >\n <View style={styles.root} pointerEvents=\"box-none\">\n <Animated.View style={[styles.scrim, { opacity }]}>\n <Pressable\n accessibilityRole=\"button\"\n accessibilityLabel=\"Dismiss debug overlay\"\n onPress={onRequestClose}\n style={StyleSheet.absoluteFill}\n testID=\"variantlab-scrim\"\n />\n </Animated.View>\n <Animated.View\n style={[\n styles.sheet,\n {\n backgroundColor: theme.background,\n borderColor: theme.border,\n paddingBottom: insetBottom + 12,\n transform: [{ translateY }],\n },\n ]}\n testID=\"variantlab-bottom-sheet\"\n >\n {children}\n </Animated.View>\n </View>\n </Modal>\n );\n}\n\nconst styles = StyleSheet.create({\n root: {\n flex: 1,\n justifyContent: \"flex-end\",\n },\n scrim: {\n position: \"absolute\",\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n backgroundColor: \"rgba(0,0,0,0.5)\",\n },\n sheet: {\n borderTopLeftRadius: 16,\n borderTopRightRadius: 16,\n borderTopWidth: 1,\n borderLeftWidth: 1,\n borderRightWidth: 1,\n paddingHorizontal: 16,\n paddingTop: 16,\n maxHeight: \"85%\" as unknown as number,\n },\n});\n","/**\n * The floating debug button.\n *\n * A 48×48 circular Pressable absolute-positioned in one of the four\n * screen corners. Renders a badge with the count of route-scoped\n * experiments so users see at a glance how many things they can\n * tweak on the current screen.\n *\n * Implementation notes:\n *\n * - We avoid `react-native-svg` and inline icons by drawing a\n * simple beaker glyph with a couple of `<View>` rectangles. The\n * icon is recognisable enough at 24px, costs nothing in bundle\n * size, and removes a peer dependency.\n * - The badge collapses when there are zero experiments — showing\n * `0` is more confusing than no badge at all.\n * - The component does not subscribe to engine events itself; the\n * parent passes `count` so a single subscription drives the whole\n * overlay.\n */\nimport type { ReactElement } from \"react\";\nimport { Pressable, StyleSheet, Text, View } from \"react-native\";\nimport type { OverlayTheme } from \"./theme.js\";\n\nexport type Corner = \"top-left\" | \"top-right\" | \"bottom-left\" | \"bottom-right\";\n\nexport interface FloatingButtonProps {\n theme: OverlayTheme;\n corner: Corner;\n offset: { x: number; y: number };\n count: number;\n onPress: () => void;\n}\n\nexport function FloatingButton({\n theme,\n corner,\n offset,\n count,\n onPress,\n}: FloatingButtonProps): ReactElement {\n const positionStyle = cornerStyle(corner, offset);\n return (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityLabel=\"Open variantlab debug overlay\"\n onPress={onPress}\n style={[styles.button, positionStyle, { backgroundColor: theme.accent }]}\n hitSlop={8}\n testID=\"variantlab-floating-button\"\n >\n <BeakerIcon color={theme.accentText} />\n {count > 0 ? (\n <View style={[styles.badge, { backgroundColor: theme.surface, borderColor: theme.accent }]}>\n <Text style={[styles.badgeText, { color: theme.text }]}>{String(count)}</Text>\n </View>\n ) : null}\n </Pressable>\n );\n}\n\nfunction BeakerIcon({ color }: { color: string }): ReactElement {\n return (\n <View style={styles.iconWrap}>\n <View style={[styles.iconNeck, { backgroundColor: color }]} />\n <View style={[styles.iconBody, { borderColor: color }]} />\n </View>\n );\n}\n\nfunction cornerStyle(corner: Corner, offset: { x: number; y: number }): Record<string, number> {\n switch (corner) {\n case \"top-left\":\n return { top: offset.y, left: offset.x };\n case \"top-right\":\n return { top: offset.y, right: offset.x };\n case \"bottom-left\":\n return { bottom: offset.y, left: offset.x };\n case \"bottom-right\":\n return { bottom: offset.y, right: offset.x };\n }\n}\n\nconst styles = StyleSheet.create({\n button: {\n position: \"absolute\" as unknown as undefined,\n width: 48,\n height: 48,\n borderRadius: 24,\n alignItems: \"center\",\n justifyContent: \"center\",\n elevation: 4,\n shadowColor: \"#000\",\n shadowOpacity: 0.3,\n shadowRadius: 6,\n shadowOffset: { width: 0, height: 2 },\n },\n badge: {\n position: \"absolute\" as unknown as undefined,\n top: -4,\n right: -4,\n minWidth: 18,\n height: 18,\n borderRadius: 9,\n paddingHorizontal: 4,\n borderWidth: 1,\n alignItems: \"center\",\n justifyContent: \"center\",\n },\n badgeText: {\n fontSize: 10,\n fontWeight: \"700\",\n },\n iconWrap: {\n width: 24,\n height: 24,\n alignItems: \"center\",\n justifyContent: \"flex-end\",\n },\n iconNeck: {\n width: 4,\n height: 6,\n marginBottom: 0,\n },\n iconBody: {\n width: 18,\n height: 14,\n borderWidth: 2,\n borderRadius: 3,\n borderTopWidth: 0,\n },\n});\n","/**\n * Tiny controlled search input used in the overlay.\n *\n * The component is intentionally dumb — it owns no state, debounces\n * nothing, and forwards every keystroke straight to the parent. The\n * parent (`<VariantDebugOverlay>`) handles debouncing through a\n * setTimeout-backed wrapper so the same logic powers both the search\n * box and the route picker.\n */\nimport type { ReactElement } from \"react\";\nimport { StyleSheet, TextInput, View } from \"react-native\";\nimport type { OverlayTheme } from \"./theme.js\";\n\nexport interface SearchInputProps {\n theme: OverlayTheme;\n value: string;\n onChange: (value: string) => void;\n placeholder?: string;\n}\n\nexport function SearchInput({\n theme,\n value,\n onChange,\n placeholder,\n}: SearchInputProps): ReactElement {\n return (\n <View style={[styles.wrapper, { backgroundColor: theme.surface, borderColor: theme.border }]}>\n <TextInput\n accessibilityLabel=\"Filter experiments\"\n value={value}\n onChangeText={onChange}\n placeholder={placeholder ?? \"Search experiments…\"}\n placeholderTextColor={theme.textMuted}\n autoCorrect={false}\n autoCapitalize=\"none\"\n style={[styles.input, { color: theme.text }]}\n testID=\"variantlab-search-input\"\n />\n </View>\n );\n}\n\nconst styles = StyleSheet.create({\n wrapper: {\n borderRadius: 8,\n borderWidth: 1,\n paddingHorizontal: 10,\n paddingVertical: 6,\n },\n input: {\n fontSize: 14,\n paddingVertical: 4,\n },\n});\n","/**\n * \"Config\" tab — high-level summary of the loaded experiments config.\n *\n * Shows the schema version, the total experiment count, the kill-\n * switch state, and a per-experiment list of (id, status, default).\n * The list is virtualised by `<ScrollView>` rather than a `<FlatList>`\n * because debug configs are tiny (the upper bound from\n * `config-format.md` is 1000 experiments and you usually have <50).\n */\n\nimport type { ExperimentsConfig } from \"@variantlab/core\";\nimport type { ReactElement } from \"react\";\nimport { ScrollView, StyleSheet, Text, View } from \"react-native\";\nimport type { OverlayTheme } from \"../theme.js\";\n\nexport interface ConfigTabProps {\n theme: OverlayTheme;\n config: ExperimentsConfig;\n}\n\nexport function ConfigTab({ theme, config }: ConfigTabProps): ReactElement {\n const enabled = config.enabled !== false;\n return (\n <ScrollView\n style={styles.scroll}\n contentContainerStyle={styles.content}\n testID=\"variantlab-config-view\"\n >\n <SummaryRow theme={theme} label=\"Version\" value={String(config.version)} />\n <SummaryRow theme={theme} label=\"Status\" value={enabled ? \"enabled\" : \"kill-switched\"} />\n <SummaryRow theme={theme} label=\"Experiments\" value={String(config.experiments.length)} />\n <View style={[styles.section, { borderColor: theme.border, backgroundColor: theme.surface }]}>\n {config.experiments.map((exp) => (\n <View key={exp.id} style={[styles.row, { borderBottomColor: theme.border }]}>\n <Text style={[styles.rowId, { color: theme.text }]} numberOfLines={1}>\n {exp.id}\n </Text>\n <Text style={[styles.rowMeta, { color: theme.textMuted }]} numberOfLines={1}>\n {exp.status ?? \"active\"} · default = {exp.default}\n </Text>\n </View>\n ))}\n </View>\n </ScrollView>\n );\n}\n\nfunction SummaryRow({\n theme,\n label,\n value,\n}: {\n theme: OverlayTheme;\n label: string;\n value: string;\n}): ReactElement {\n return (\n <View style={styles.summaryRow}>\n <Text style={[styles.summaryLabel, { color: theme.textMuted }]}>{label}</Text>\n <Text style={[styles.summaryValue, { color: theme.text }]}>{value}</Text>\n </View>\n );\n}\n\nconst styles = StyleSheet.create({\n scroll: { flexGrow: 0 },\n content: { paddingVertical: 8, gap: 8 },\n summaryRow: {\n flexDirection: \"row\" as unknown as undefined,\n justifyContent: \"space-between\",\n paddingHorizontal: 4,\n },\n summaryLabel: { fontSize: 12 },\n summaryValue: { fontSize: 12, fontWeight: \"600\" },\n section: {\n borderRadius: 8,\n borderWidth: 1,\n paddingVertical: 4,\n },\n row: {\n paddingHorizontal: 12,\n paddingVertical: 8,\n borderBottomWidth: 1,\n },\n rowId: { fontSize: 12, fontWeight: \"600\" },\n rowMeta: { fontSize: 11, marginTop: 2 },\n});\n","/**\n * \"Context\" tab — JSON-tree view of the active `VariantContext`.\n *\n * The implementation is intentionally dumb: stringify the context with\n * 2-space indentation, render it inside a monospace `<Text>` block,\n * and let the user scroll. Recursive React-tree expansion would look\n * nicer but isn't worth the bundle size for a debug surface.\n */\n\nimport type { VariantContext } from \"@variantlab/core\";\nimport type { ReactElement } from \"react\";\nimport { ScrollView, StyleSheet, Text } from \"react-native\";\nimport type { OverlayTheme } from \"../theme.js\";\n\nexport interface ContextTabProps {\n theme: OverlayTheme;\n context: VariantContext;\n}\n\nexport function ContextTab({ theme, context }: ContextTabProps): ReactElement {\n const json = stringifyContext(context);\n return (\n <ScrollView\n style={styles.scroll}\n contentContainerStyle={styles.content}\n testID=\"variantlab-context-view\"\n >\n <Text style={[styles.json, { color: theme.text }]}>{json}</Text>\n </ScrollView>\n );\n}\n\n/** Mask the userId so screenshots don't leak PII. */\nexport function stringifyContext(context: VariantContext): string {\n const masked: Record<string, unknown> = { ...context };\n if (typeof masked.userId === \"string\" && masked.userId.length > 0) {\n masked.userId = maskId(masked.userId);\n }\n return JSON.stringify(masked, null, 2);\n}\n\nfunction maskId(id: string): string {\n if (id.length <= 4) return \"***\";\n return `${id.slice(0, 2)}…${id.slice(-2)}`;\n}\n\nconst styles = StyleSheet.create({\n scroll: { flexGrow: 0 },\n content: { paddingVertical: 8 },\n json: {\n fontSize: 11,\n fontFamily: \"Menlo, monospace\",\n lineHeight: 16,\n },\n});\n","/**\n * \"History\" tab — scrollable list of recent engine events.\n *\n * Reads from `engine.getHistory()` which is backed by a fixed-size\n * ring buffer. Newest events render at the top so the user always\n * sees the last action they took. Each event renders as a single\n * row with type, experiment id, and a tiny detail blurb.\n */\n\nimport type { EngineEvent } from \"@variantlab/core\";\nimport { type ReactElement, useMemo } from \"react\";\nimport { ScrollView, StyleSheet, Text, View } from \"react-native\";\nimport type { OverlayTheme } from \"../theme.js\";\n\nexport interface HistoryTabProps {\n theme: OverlayTheme;\n events: readonly EngineEvent[];\n}\n\nexport function HistoryTab({ theme, events }: HistoryTabProps): ReactElement {\n // Each entry is tagged with a unique `seq` so React keys don't have\n // to fall back to array index. The ring buffer is append-only so a\n // monotonically incrementing counter is a perfectly stable id; we\n // reset it every time the underlying `events` reference changes.\n const ordered = useMemo(() => {\n const list: Array<{ seq: number; event: EngineEvent }> = [];\n for (let i = events.length - 1; i >= 0; i--) {\n const event = events[i];\n if (event !== undefined) list.push({ seq: i, event });\n }\n return list;\n }, [events]);\n if (ordered.length === 0) {\n return (\n <View style={styles.empty} testID=\"variantlab-history-empty\">\n <Text style={[styles.emptyText, { color: theme.textMuted }]}>No events yet.</Text>\n </View>\n );\n }\n return (\n <ScrollView\n style={styles.scroll}\n contentContainerStyle={styles.content}\n testID=\"variantlab-history-list\"\n >\n {ordered.map(({ seq, event }) => (\n <View\n key={`${seq}:${event.type}`}\n style={[styles.row, { borderColor: theme.border, backgroundColor: theme.surface }]}\n >\n <Text style={[styles.type, { color: theme.accent }]}>{event.type}</Text>\n <Text style={[styles.detail, { color: theme.text }]} numberOfLines={2}>\n {summarize(event)}\n </Text>\n </View>\n ))}\n </ScrollView>\n );\n}\n\n/** Render a single event into a one-line human-readable string. */\nexport function summarize(event: EngineEvent): string {\n switch (event.type) {\n case \"ready\":\n return `engine ready · ${event.config.experiments.length} experiments`;\n case \"assignment\":\n return `${event.experimentId} → ${event.variantId}`;\n case \"exposure\":\n return `${event.experimentId} → ${event.variantId} (exposure)`;\n case \"variantChanged\":\n return `${event.experimentId} → ${event.variantId} (${event.source})`;\n case \"rollback\":\n return `${event.experimentId} rolled back: ${event.reason}`;\n case \"configLoaded\":\n return `config reloaded · ${event.config.experiments.length} experiments`;\n case \"contextUpdated\":\n return \"context updated\";\n case \"error\":\n return `error: ${event.error.message}`;\n }\n}\n\nconst styles = StyleSheet.create({\n scroll: { flexGrow: 0 },\n content: { paddingVertical: 8, gap: 6 },\n row: {\n borderRadius: 8,\n borderWidth: 1,\n paddingHorizontal: 10,\n paddingVertical: 6,\n },\n type: {\n fontSize: 10,\n fontWeight: \"700\",\n textTransform: \"uppercase\" as unknown as undefined,\n letterSpacing: 0.5,\n },\n detail: {\n fontSize: 12,\n marginTop: 2,\n },\n empty: { paddingVertical: 32, alignItems: \"center\" },\n emptyText: { fontSize: 12 },\n});\n","/**\n * The \"Overview\" tab — the headline view of the debug overlay.\n *\n * Renders the filtered experiment list as a scrollable column of\n * `<ExperimentCard>` instances. Owns the per-card expanded state via\n * a `Set` keyed on experiment id, and surfaces an empty state when\n * no experiments match the current filters.\n */\n\nimport type { Experiment, VariantEngine } from \"@variantlab/core\";\nimport { type ReactElement, useCallback, useState } from \"react\";\nimport { ScrollView, StyleSheet, Text, View } from \"react-native\";\nimport { ExperimentCard } from \"../experiment-card.js\";\nimport type { OverlayTheme } from \"../theme.js\";\n\nexport interface OverviewTabProps {\n theme: OverlayTheme;\n engine: VariantEngine;\n experiments: readonly Experiment[];\n /** Map of experiment id → currently active variant id. */\n variantsById: Readonly<Record<string, string>>;\n}\n\nexport function OverviewTab({\n theme,\n engine,\n experiments,\n variantsById,\n}: OverviewTabProps): ReactElement {\n const [expanded, setExpanded] = useState<ReadonlySet<string>>(() => new Set());\n\n const toggle = useCallback((id: string) => {\n setExpanded((prev) => {\n const next = new Set(prev);\n if (next.has(id)) {\n next.delete(id);\n } else {\n next.add(id);\n }\n return next;\n });\n }, []);\n\n if (experiments.length === 0) {\n return (\n <View style={styles.empty} testID=\"variantlab-overview-empty\">\n <Text style={[styles.emptyTitle, { color: theme.text }]}>\n No experiments match this view.\n </Text>\n <Text style={[styles.emptyHint, { color: theme.textMuted }]}>\n Clear the search or switch to \"All experiments\".\n </Text>\n </View>\n );\n }\n\n return (\n <ScrollView\n style={styles.scroll}\n contentContainerStyle={styles.content}\n keyboardShouldPersistTaps=\"handled\"\n testID=\"variantlab-overview-list\"\n >\n {experiments.map((exp) => (\n <ExperimentCard\n key={exp.id}\n theme={theme}\n experiment={exp}\n activeVariantId={variantsById[exp.id] ?? exp.default}\n expanded={expanded.has(exp.id)}\n onToggleExpand={() => toggle(exp.id)}\n onSelect={(variantId) => engine.setVariant(exp.id, variantId, \"user\")}\n onReset={() => engine.clearVariant(exp.id)}\n />\n ))}\n </ScrollView>\n );\n}\n\nconst styles = StyleSheet.create({\n scroll: {\n flexGrow: 0,\n },\n content: {\n paddingVertical: 8,\n },\n empty: {\n paddingVertical: 32,\n alignItems: \"center\",\n gap: 4,\n },\n emptyTitle: {\n fontSize: 14,\n fontWeight: \"600\",\n },\n emptyHint: {\n fontSize: 12,\n },\n});\n","/**\n * Default colour palette for the debug overlay.\n *\n * The overlay deliberately ships a built-in dark theme rather than\n * trying to follow the host app's theme — debug surfaces look better\n * when they're visually distinct from production UI, and a fixed\n * palette lets us hand-tune contrast for WCAG AA without depending\n * on a styling library.\n *\n * Users override the palette via the `theme` prop on\n * `<VariantDebugOverlay />`, e.g.:\n *\n * <VariantDebugOverlay theme={{ accent: \"#a78bfa\" }} />\n */\n\nexport interface OverlayTheme {\n background: string;\n surface: string;\n border: string;\n text: string;\n textMuted: string;\n accent: string;\n accentText: string;\n danger: string;\n}\n\nexport const DEFAULT_THEME: OverlayTheme = {\n background: \"#0b0b10\",\n surface: \"#161620\",\n border: \"#272735\",\n text: \"#f4f4f5\",\n textMuted: \"#9ca3af\",\n accent: \"#8b5cf6\",\n accentText: \"#ffffff\",\n danger: \"#f43f5e\",\n};\n\nexport function mergeTheme(base: OverlayTheme, patch?: Partial<OverlayTheme>): OverlayTheme {\n if (patch === undefined) return base;\n return { ...base, ...patch };\n}\n","/**\n * Cross-cutting hook used by every overlay surface to keep itself in\n * sync with the engine.\n *\n * The implementation uses `React.useSyncExternalStore` so the overlay\n * is correct under Concurrent Mode and Strict Mode — it never\n * over-renders on unrelated event types and never holds a stale\n * subscription across remounts.\n *\n * Each call site provides its own selector + comparator so subscribing\n * to \"the list of all experiments on the current route\" doesn't\n * needlessly invalidate when, say, a `contextUpdated` event fires.\n */\n\nimport type { EngineEvent, VariantEngine } from \"@variantlab/core\";\nimport { useCallback, useRef, useSyncExternalStore } from \"react\";\n\n/**\n * Subscribe to a slice of engine state. The selector is invoked\n * lazily by `useSyncExternalStore`; the result is cached in a ref so\n * a stable reference is returned for unchanged snapshots.\n */\nexport function useEngineSnapshot<T>(\n engine: VariantEngine,\n select: (engine: VariantEngine) => T,\n isEqual: (a: T, b: T) => boolean = Object.is,\n): T {\n const cache = useRef<{ value: T } | null>(null);\n\n const subscribe = useCallback(\n (notify: () => void) => {\n const unsub = engine.subscribe((_event: EngineEvent) => notify());\n return unsub;\n },\n [engine],\n );\n\n const getSnapshot = useCallback((): T => {\n const next = select(engine);\n const prev = cache.current;\n if (prev !== null && isEqual(prev.value, next)) {\n return prev.value;\n }\n cache.current = { value: next };\n return next;\n }, [engine, select, isEqual]);\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n","/// <reference path=\"../types.d.ts\" />\n/**\n * `<VariantDebugOverlay />` — the entire debug overlay surface.\n *\n * This component lives in a **separate package entrypoint**\n * (`@variantlab/react-native/debug`) so production bundles never\n * import it unless the app explicitly opts in. The kickoff for\n * Phase 1 (`docs/phases/phase-1-kickoff-prompts.md` §6) calls out\n * tree-shakeability as a hard requirement and the size budget for\n * `@variantlab/react-native` excludes this entrypoint entirely.\n *\n * Architectural choices:\n *\n * - The component reads the engine from `useVariantLabEngine` (the\n * hook is re-exported through `@variantlab/react`), so callers\n * mount it inside the same `<VariantLabProvider>` they already\n * have. No extra wiring.\n * - All state lives in `useState` / `useRef` — no Context, no\n * reducer. The overlay is a leaf and never re-renders the host\n * app.\n * - Engine subscription is consolidated into a single\n * `useSyncExternalStore` call via `useEngineSnapshot`, then\n * fanned out to the tab components as plain props. This means\n * toggling between tabs has zero engine-side cost.\n * - The overlay self-disables in production by default. The\n * `forceEnable` prop is the documented escape hatch for QA\n * builds; the warning fires only in `process.env.NODE_ENV === \"production\"`\n * so jest/vitest test runs stay quiet.\n */\n\nimport type { EngineEvent, Experiment, ExperimentsConfig, VariantContext } from \"@variantlab/core\";\nimport { useVariantLabEngine } from \"@variantlab/react\";\nimport { type ReactElement, useCallback, useEffect, useMemo, useState } from \"react\";\nimport { Pressable, StyleSheet, Text, View } from \"react-native\";\nimport { BottomSheet } from \"./bottom-sheet.js\";\nimport { filterExperiments } from \"./filter.js\";\nimport { type Corner, FloatingButton } from \"./floating-button.js\";\nimport { registerOverlay } from \"./imperative.js\";\nimport { SearchInput } from \"./search-input.js\";\nimport { ConfigTab } from \"./tabs/config.js\";\nimport { ContextTab } from \"./tabs/context.js\";\nimport { HistoryTab } from \"./tabs/history.js\";\nimport { OverviewTab } from \"./tabs/overview.js\";\nimport { DEFAULT_THEME, mergeTheme, type OverlayTheme } from \"./theme.js\";\nimport { useEngineSnapshot } from \"./use-engine-snapshot.js\";\n\ntype TabName = \"overview\" | \"context\" | \"config\" | \"history\";\n\nexport interface VariantDebugOverlayProps {\n /** Force the overlay on even outside of `__DEV__`. Default: `false`. */\n readonly forceEnable?: boolean;\n /** Hide the floating button entirely (open via `openDebugOverlay()`). */\n readonly hideButton?: boolean;\n /** Floating-button corner. */\n readonly position?: Corner;\n /** Pixel offset from the chosen corner. */\n readonly offset?: { x: number; y: number };\n /** Override the colour palette. */\n readonly theme?: Partial<OverlayTheme>;\n /** Show only experiments active on the current route. Default: `true`. */\n readonly routeFilter?: boolean;\n /** Optional safe-area inset injected by the host. */\n readonly safeAreaBottom?: number;\n}\n\nexport function VariantDebugOverlay(props: VariantDebugOverlayProps): ReactElement | null {\n if (!shouldRender(props.forceEnable)) {\n if (typeof process !== \"undefined\" && process.env?.NODE_ENV === \"production\") {\n // eslint-disable-next-line no-console\n console.warn(\n \"[variantlab] VariantDebugOverlay rendered in production. \" +\n \"Pass forceEnable={true} if this is intentional.\",\n );\n }\n return null;\n }\n return <OverlayImpl {...props} />;\n}\n\n/**\n * Determines whether the overlay should render. Exported for tests.\n *\n * Order of precedence:\n * 1. Explicit `forceEnable` prop wins.\n * 2. The RN-injected `__DEV__` global is `true`.\n * 3. `process.env.NODE_ENV === \"development\"` (web fallback).\n */\nexport function shouldRender(forceEnable: boolean | undefined): boolean {\n if (forceEnable === true) return true;\n if (typeof __DEV__ !== \"undefined\" && __DEV__ === true) return true;\n if (typeof process !== \"undefined\" && process.env?.NODE_ENV === \"development\") return true;\n return false;\n}\n\nfunction OverlayImpl(props: VariantDebugOverlayProps): ReactElement {\n const engine = useVariantLabEngine();\n const theme = useMemo(() => mergeTheme(DEFAULT_THEME, props.theme), [props.theme]);\n const corner: Corner = props.position ?? \"bottom-right\";\n const offset = props.offset ?? { x: 16, y: 80 };\n\n const [visible, setVisible] = useState(false);\n const [tab, setTab] = useState<TabName>(\"overview\");\n const [query, setQuery] = useState(\"\");\n const [routeFilterActive, setRouteFilterActive] = useState<boolean>(props.routeFilter !== false);\n\n // Register imperative open/close API.\n useEffect(() => registerOverlay(setVisible), []);\n\n // Pull a single snapshot of every piece of engine state we care about.\n const snapshot = useEngineSnapshot(\n engine,\n useCallback((e) => {\n const ctx = e.getContext();\n const route = typeof ctx.route === \"string\" ? ctx.route : undefined;\n const all = e.getExperiments();\n const scoped = e.getExperiments(route);\n const variantsById: Record<string, string> = {};\n for (const exp of all) {\n variantsById[exp.id] = e.getVariant(exp.id);\n }\n return {\n all,\n scoped,\n variantsById,\n context: ctx,\n config: e.getConfig(),\n history: e.getHistory(),\n };\n }, []),\n snapshotEqual,\n );\n\n const visibleExperiments = useMemo(() => {\n const source = routeFilterActive ? snapshot.scoped : snapshot.all;\n return filterExperiments(source, query);\n }, [routeFilterActive, snapshot.scoped, snapshot.all, query]);\n\n const handleResetAll = useCallback(() => engine.resetAll(), [engine]);\n\n return (\n <>\n {props.hideButton === true ? null : (\n <FloatingButton\n theme={theme}\n corner={corner}\n offset={offset}\n count={snapshot.scoped.length}\n onPress={() => setVisible(true)}\n />\n )}\n <BottomSheet\n visible={visible}\n onRequestClose={() => setVisible(false)}\n theme={theme}\n insetBottom={props.safeAreaBottom ?? 0}\n >\n <Header theme={theme} onClose={() => setVisible(false)} onResetAll={handleResetAll} />\n <SearchInput theme={theme} value={query} onChange={setQuery} />\n <RouteToggle\n theme={theme}\n active={routeFilterActive}\n onToggle={() => setRouteFilterActive((v) => !v)}\n />\n <TabBar theme={theme} value={tab} onChange={setTab} />\n <View style={styles.body}>\n {tab === \"overview\" ? (\n <OverviewTab\n theme={theme}\n engine={engine}\n experiments={visibleExperiments}\n variantsById={snapshot.variantsById}\n />\n ) : null}\n {tab === \"context\" ? <ContextTab theme={theme} context={snapshot.context} /> : null}\n {tab === \"config\" ? <ConfigTab theme={theme} config={snapshot.config} /> : null}\n {tab === \"history\" ? <HistoryTab theme={theme} events={snapshot.history} /> : null}\n </View>\n </BottomSheet>\n </>\n );\n}\n\n// ---------- private helpers -----------------------------------------------\n\ninterface Snapshot {\n all: readonly Experiment[];\n scoped: readonly Experiment[];\n variantsById: Readonly<Record<string, string>>;\n context: VariantContext;\n config: ExperimentsConfig;\n history: readonly EngineEvent[];\n}\n\n/**\n * Shallow-compares two {@link Snapshot}s. The engine's getters return\n * a mix of stable references (`getConfig`, unfiltered `getExperiments`)\n * and fresh arrays/objects every call (`getContext`, `getHistory`, the\n * route-filtered `getExperiments`, and our own rebuilt `variantsById`).\n *\n * Comparing everything strictly by reference would make this function\n * return `false` on every call, which in turn would make\n * `useSyncExternalStore` loop forever because `getSnapshot` would\n * never stabilise. So we drop to element-wise identity for the arrays\n * and key-wise identity for the objects — that's O(n) in the number\n * of experiments/events, which is bounded by the config and the\n * history ring buffer.\n */\nfunction snapshotEqual(a: Snapshot, b: Snapshot): boolean {\n if (a.config !== b.config) return false;\n if (!sameRefArray(a.all, b.all)) return false;\n if (!sameRefArray(a.scoped, b.scoped)) return false;\n if (!sameRefArray(a.history, b.history)) return false;\n if (!sameRefRecord(a.variantsById, b.variantsById)) return false;\n if (!sameRefRecord(a.context as Record<string, unknown>, b.context as Record<string, unknown>)) {\n return false;\n }\n return true;\n}\n\nfunction sameRefArray<T>(a: readonly T[], b: readonly T[]): boolean {\n if (a === b) return true;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n\nfunction sameRefRecord(\n a: Readonly<Record<string, unknown>>,\n b: Readonly<Record<string, unknown>>,\n): boolean {\n if (a === b) return true;\n const aKeys = Object.keys(a);\n const bKeys = Object.keys(b);\n if (aKeys.length !== bKeys.length) return false;\n for (const k of aKeys) {\n if (a[k] !== b[k]) return false;\n }\n return true;\n}\n\nfunction Header({\n theme,\n onClose,\n onResetAll,\n}: {\n theme: OverlayTheme;\n onClose: () => void;\n onResetAll: () => void;\n}): ReactElement {\n return (\n <View style={styles.header}>\n <Text style={[styles.title, { color: theme.text }]}>variantlab</Text>\n <View style={styles.headerActions}>\n <Pressable\n onPress={onResetAll}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Reset all overrides\"\n style={[styles.headerButton, { borderColor: theme.border }]}\n testID=\"variantlab-reset-all\"\n >\n <Text style={[styles.headerButtonText, { color: theme.textMuted }]}>Reset</Text>\n </Pressable>\n <Pressable\n onPress={onClose}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Close debug overlay\"\n style={[styles.headerButton, { borderColor: theme.border }]}\n testID=\"variantlab-close\"\n >\n <Text style={[styles.headerButtonText, { color: theme.textMuted }]}>Close</Text>\n </Pressable>\n </View>\n </View>\n );\n}\n\nfunction RouteToggle({\n theme,\n active,\n onToggle,\n}: {\n theme: OverlayTheme;\n active: boolean;\n onToggle: () => void;\n}): ReactElement {\n return (\n <Pressable\n onPress={onToggle}\n accessibilityRole=\"button\"\n accessibilityLabel={active ? \"Show all experiments\" : \"Show only current route\"}\n style={styles.routeToggle}\n testID=\"variantlab-route-toggle\"\n >\n <Text style={[styles.routeToggleText, { color: theme.textMuted }]}>\n {active ? \"Showing: current route\" : \"Showing: all experiments\"}\n </Text>\n </Pressable>\n );\n}\n\nfunction TabBar({\n theme,\n value,\n onChange,\n}: {\n theme: OverlayTheme;\n value: TabName;\n onChange: (next: TabName) => void;\n}): ReactElement {\n const tabs: TabName[] = [\"overview\", \"context\", \"config\", \"history\"];\n return (\n <View style={[styles.tabBar, { borderColor: theme.border }]}>\n {tabs.map((t) => {\n const isActive = t === value;\n return (\n <Pressable\n key={t}\n onPress={() => onChange(t)}\n accessibilityRole=\"button\"\n accessibilityState={{ selected: isActive }}\n accessibilityLabel={`${t} tab`}\n style={[\n styles.tab,\n isActive ? { borderBottomColor: theme.accent } : { borderBottomColor: \"transparent\" },\n ]}\n testID={`variantlab-tab-${t}`}\n >\n <Text style={[styles.tabText, { color: isActive ? theme.text : theme.textMuted }]}>\n {t}\n </Text>\n </Pressable>\n );\n })}\n </View>\n );\n}\n\nconst styles = StyleSheet.create({\n header: {\n flexDirection: \"row\" as unknown as undefined,\n alignItems: \"center\",\n justifyContent: \"space-between\",\n paddingBottom: 12,\n },\n title: {\n fontSize: 16,\n fontWeight: \"700\",\n },\n headerActions: {\n flexDirection: \"row\" as unknown as undefined,\n gap: 8,\n },\n headerButton: {\n paddingHorizontal: 10,\n paddingVertical: 4,\n borderRadius: 6,\n borderWidth: 1,\n },\n headerButtonText: {\n fontSize: 11,\n fontWeight: \"600\",\n },\n routeToggle: {\n paddingVertical: 8,\n },\n routeToggleText: {\n fontSize: 11,\n },\n tabBar: {\n flexDirection: \"row\" as unknown as undefined,\n borderTopWidth: 1,\n marginTop: 4,\n },\n tab: {\n flex: 1,\n alignItems: \"center\",\n paddingVertical: 10,\n borderBottomWidth: 2,\n },\n tabText: {\n fontSize: 12,\n fontWeight: \"600\",\n textTransform: \"capitalize\" as unknown as undefined,\n },\n body: {\n flexShrink: 1,\n },\n});\n"]}
@@ -0,0 +1,206 @@
1
+ import { Experiment, VariantContext, EngineEvent } from '@variantlab/core';
2
+ import { ReactElement } from 'react';
3
+
4
+ /**
5
+ * Default colour palette for the debug overlay.
6
+ *
7
+ * The overlay deliberately ships a built-in dark theme rather than
8
+ * trying to follow the host app's theme — debug surfaces look better
9
+ * when they're visually distinct from production UI, and a fixed
10
+ * palette lets us hand-tune contrast for WCAG AA without depending
11
+ * on a styling library.
12
+ *
13
+ * Users override the palette via the `theme` prop on
14
+ * `<VariantDebugOverlay />`, e.g.:
15
+ *
16
+ * <VariantDebugOverlay theme={{ accent: "#a78bfa" }} />
17
+ */
18
+ interface OverlayTheme {
19
+ background: string;
20
+ surface: string;
21
+ border: string;
22
+ text: string;
23
+ textMuted: string;
24
+ accent: string;
25
+ accentText: string;
26
+ danger: string;
27
+ }
28
+ declare const DEFAULT_THEME: OverlayTheme;
29
+ declare function mergeTheme(base: OverlayTheme, patch?: Partial<OverlayTheme>): OverlayTheme;
30
+
31
+ /**
32
+ * One experiment card in the bottom sheet.
33
+ *
34
+ * Each card collapses by default to keep the list scannable, expands
35
+ * on tap to reveal a radio picker over the variants, and shows the
36
+ * currently active variant with a badge. The card is fully controlled
37
+ * by the parent — expansion state lives in the overlay so toggling
38
+ * one card never re-renders the others.
39
+ *
40
+ * Variant rows expose a row-press handler instead of a real radio
41
+ * widget; React Native's stock <Switch> looks out of place at the
42
+ * sizes we want. The chosen variant gets an inline highlight ring
43
+ * via the theme accent so it's obvious which variant is active.
44
+ */
45
+
46
+ /**
47
+ * Pure helper used by both the card and the overlay's "manual override"
48
+ * indicator: returns whether the active variant was forced or assigned
49
+ * naturally. Exported so unit tests can exercise the logic without
50
+ * spinning up a renderer.
51
+ */
52
+ declare function describeAssignmentSource(hasOverride: boolean, matchedTargeting: boolean): "manual override" | "by targeting" | "by default";
53
+
54
+ /**
55
+ * Pure helpers for the overlay's experiment filtering pipeline.
56
+ *
57
+ * The overlay applies three independent filters to the engine's
58
+ * experiment list:
59
+ *
60
+ * 1. Route filter — only show experiments whose `routes` glob
61
+ * matches the current path. Provided by core via
62
+ * `engine.getExperiments(route)`, but the overlay also needs to
63
+ * strip archived experiments which core does not.
64
+ * 2. "Hidden when archived" — `status: "archived"` experiments
65
+ * should never appear in the picker. They live in core for
66
+ * historical inspection.
67
+ * 3. Search query — case-insensitive substring match across the
68
+ * id, name, and any variant id/label.
69
+ *
70
+ * Splitting these out into a pure module makes them trivial to unit
71
+ * test without spinning up a renderer.
72
+ */
73
+
74
+ declare function isVisibleExperiment(experiment: Experiment): boolean;
75
+ declare function matchesSearch(experiment: Experiment, query: string): boolean;
76
+ declare function filterExperiments(list: readonly Experiment[], query: string): readonly Experiment[];
77
+
78
+ /**
79
+ * The floating debug button.
80
+ *
81
+ * A 48×48 circular Pressable absolute-positioned in one of the four
82
+ * screen corners. Renders a badge with the count of route-scoped
83
+ * experiments so users see at a glance how many things they can
84
+ * tweak on the current screen.
85
+ *
86
+ * Implementation notes:
87
+ *
88
+ * - We avoid `react-native-svg` and inline icons by drawing a
89
+ * simple beaker glyph with a couple of `<View>` rectangles. The
90
+ * icon is recognisable enough at 24px, costs nothing in bundle
91
+ * size, and removes a peer dependency.
92
+ * - The badge collapses when there are zero experiments — showing
93
+ * `0` is more confusing than no badge at all.
94
+ * - The component does not subscribe to engine events itself; the
95
+ * parent passes `count` so a single subscription drives the whole
96
+ * overlay.
97
+ */
98
+
99
+ type Corner = "top-left" | "top-right" | "bottom-left" | "bottom-right";
100
+
101
+ /**
102
+ * Imperative open / close API for the debug overlay.
103
+ *
104
+ * Some debug surfaces want to open the overlay from outside React —
105
+ * a long-press on a Detox e2e harness, a custom shake handler, a
106
+ * react-navigation menu item. We expose a tiny pub/sub so any number
107
+ * of mounted overlays can be opened in lock-step:
108
+ *
109
+ * import { openDebugOverlay } from "@variantlab/react-native/debug";
110
+ *
111
+ * shakeListener.on("shake", openDebugOverlay);
112
+ *
113
+ * The overlay registers/unregisters its `setVisible` callback in
114
+ * `useEffect`, and the imperative helpers fan calls out to every
115
+ * registered callback. In practice almost every app mounts a single
116
+ * overlay so the set has size 1; the registry simply guarantees we
117
+ * don't crash if a developer mounts two.
118
+ */
119
+ declare function registerOverlay(setVisible: (visible: boolean) => void): () => void;
120
+ declare function openDebugOverlay(): void;
121
+ declare function closeDebugOverlay(): void;
122
+
123
+ /**
124
+ * `<VariantDebugOverlay />` — the entire debug overlay surface.
125
+ *
126
+ * This component lives in a **separate package entrypoint**
127
+ * (`@variantlab/react-native/debug`) so production bundles never
128
+ * import it unless the app explicitly opts in. The kickoff for
129
+ * Phase 1 (`docs/phases/phase-1-kickoff-prompts.md` §6) calls out
130
+ * tree-shakeability as a hard requirement and the size budget for
131
+ * `@variantlab/react-native` excludes this entrypoint entirely.
132
+ *
133
+ * Architectural choices:
134
+ *
135
+ * - The component reads the engine from `useVariantLabEngine` (the
136
+ * hook is re-exported through `@variantlab/react`), so callers
137
+ * mount it inside the same `<VariantLabProvider>` they already
138
+ * have. No extra wiring.
139
+ * - All state lives in `useState` / `useRef` — no Context, no
140
+ * reducer. The overlay is a leaf and never re-renders the host
141
+ * app.
142
+ * - Engine subscription is consolidated into a single
143
+ * `useSyncExternalStore` call via `useEngineSnapshot`, then
144
+ * fanned out to the tab components as plain props. This means
145
+ * toggling between tabs has zero engine-side cost.
146
+ * - The overlay self-disables in production by default. The
147
+ * `forceEnable` prop is the documented escape hatch for QA
148
+ * builds; the warning fires only in `process.env.NODE_ENV === "production"`
149
+ * so jest/vitest test runs stay quiet.
150
+ */
151
+
152
+ interface VariantDebugOverlayProps {
153
+ /** Force the overlay on even outside of `__DEV__`. Default: `false`. */
154
+ readonly forceEnable?: boolean;
155
+ /** Hide the floating button entirely (open via `openDebugOverlay()`). */
156
+ readonly hideButton?: boolean;
157
+ /** Floating-button corner. */
158
+ readonly position?: Corner;
159
+ /** Pixel offset from the chosen corner. */
160
+ readonly offset?: {
161
+ x: number;
162
+ y: number;
163
+ };
164
+ /** Override the colour palette. */
165
+ readonly theme?: Partial<OverlayTheme>;
166
+ /** Show only experiments active on the current route. Default: `true`. */
167
+ readonly routeFilter?: boolean;
168
+ /** Optional safe-area inset injected by the host. */
169
+ readonly safeAreaBottom?: number;
170
+ }
171
+ declare function VariantDebugOverlay(props: VariantDebugOverlayProps): ReactElement | null;
172
+ /**
173
+ * Determines whether the overlay should render. Exported for tests.
174
+ *
175
+ * Order of precedence:
176
+ * 1. Explicit `forceEnable` prop wins.
177
+ * 2. The RN-injected `__DEV__` global is `true`.
178
+ * 3. `process.env.NODE_ENV === "development"` (web fallback).
179
+ */
180
+ declare function shouldRender(forceEnable: boolean | undefined): boolean;
181
+
182
+ /**
183
+ * "Context" tab — JSON-tree view of the active `VariantContext`.
184
+ *
185
+ * The implementation is intentionally dumb: stringify the context with
186
+ * 2-space indentation, render it inside a monospace `<Text>` block,
187
+ * and let the user scroll. Recursive React-tree expansion would look
188
+ * nicer but isn't worth the bundle size for a debug surface.
189
+ */
190
+
191
+ /** Mask the userId so screenshots don't leak PII. */
192
+ declare function stringifyContext(context: VariantContext): string;
193
+
194
+ /**
195
+ * "History" tab — scrollable list of recent engine events.
196
+ *
197
+ * Reads from `engine.getHistory()` which is backed by a fixed-size
198
+ * ring buffer. Newest events render at the top so the user always
199
+ * sees the last action they took. Each event renders as a single
200
+ * row with type, experiment id, and a tiny detail blurb.
201
+ */
202
+
203
+ /** Render a single event into a one-line human-readable string. */
204
+ declare function summarize(event: EngineEvent): string;
205
+
206
+ export { type Corner, DEFAULT_THEME, type OverlayTheme, VariantDebugOverlay, type VariantDebugOverlayProps, closeDebugOverlay, describeAssignmentSource, filterExperiments, isVisibleExperiment, matchesSearch, mergeTheme, openDebugOverlay, registerOverlay, shouldRender, stringifyContext, summarize as summarizeEvent };
@@ -0,0 +1,206 @@
1
+ import { Experiment, VariantContext, EngineEvent } from '@variantlab/core';
2
+ import { ReactElement } from 'react';
3
+
4
+ /**
5
+ * Default colour palette for the debug overlay.
6
+ *
7
+ * The overlay deliberately ships a built-in dark theme rather than
8
+ * trying to follow the host app's theme — debug surfaces look better
9
+ * when they're visually distinct from production UI, and a fixed
10
+ * palette lets us hand-tune contrast for WCAG AA without depending
11
+ * on a styling library.
12
+ *
13
+ * Users override the palette via the `theme` prop on
14
+ * `<VariantDebugOverlay />`, e.g.:
15
+ *
16
+ * <VariantDebugOverlay theme={{ accent: "#a78bfa" }} />
17
+ */
18
+ interface OverlayTheme {
19
+ background: string;
20
+ surface: string;
21
+ border: string;
22
+ text: string;
23
+ textMuted: string;
24
+ accent: string;
25
+ accentText: string;
26
+ danger: string;
27
+ }
28
+ declare const DEFAULT_THEME: OverlayTheme;
29
+ declare function mergeTheme(base: OverlayTheme, patch?: Partial<OverlayTheme>): OverlayTheme;
30
+
31
+ /**
32
+ * One experiment card in the bottom sheet.
33
+ *
34
+ * Each card collapses by default to keep the list scannable, expands
35
+ * on tap to reveal a radio picker over the variants, and shows the
36
+ * currently active variant with a badge. The card is fully controlled
37
+ * by the parent — expansion state lives in the overlay so toggling
38
+ * one card never re-renders the others.
39
+ *
40
+ * Variant rows expose a row-press handler instead of a real radio
41
+ * widget; React Native's stock <Switch> looks out of place at the
42
+ * sizes we want. The chosen variant gets an inline highlight ring
43
+ * via the theme accent so it's obvious which variant is active.
44
+ */
45
+
46
+ /**
47
+ * Pure helper used by both the card and the overlay's "manual override"
48
+ * indicator: returns whether the active variant was forced or assigned
49
+ * naturally. Exported so unit tests can exercise the logic without
50
+ * spinning up a renderer.
51
+ */
52
+ declare function describeAssignmentSource(hasOverride: boolean, matchedTargeting: boolean): "manual override" | "by targeting" | "by default";
53
+
54
+ /**
55
+ * Pure helpers for the overlay's experiment filtering pipeline.
56
+ *
57
+ * The overlay applies three independent filters to the engine's
58
+ * experiment list:
59
+ *
60
+ * 1. Route filter — only show experiments whose `routes` glob
61
+ * matches the current path. Provided by core via
62
+ * `engine.getExperiments(route)`, but the overlay also needs to
63
+ * strip archived experiments which core does not.
64
+ * 2. "Hidden when archived" — `status: "archived"` experiments
65
+ * should never appear in the picker. They live in core for
66
+ * historical inspection.
67
+ * 3. Search query — case-insensitive substring match across the
68
+ * id, name, and any variant id/label.
69
+ *
70
+ * Splitting these out into a pure module makes them trivial to unit
71
+ * test without spinning up a renderer.
72
+ */
73
+
74
+ declare function isVisibleExperiment(experiment: Experiment): boolean;
75
+ declare function matchesSearch(experiment: Experiment, query: string): boolean;
76
+ declare function filterExperiments(list: readonly Experiment[], query: string): readonly Experiment[];
77
+
78
+ /**
79
+ * The floating debug button.
80
+ *
81
+ * A 48×48 circular Pressable absolute-positioned in one of the four
82
+ * screen corners. Renders a badge with the count of route-scoped
83
+ * experiments so users see at a glance how many things they can
84
+ * tweak on the current screen.
85
+ *
86
+ * Implementation notes:
87
+ *
88
+ * - We avoid `react-native-svg` and inline icons by drawing a
89
+ * simple beaker glyph with a couple of `<View>` rectangles. The
90
+ * icon is recognisable enough at 24px, costs nothing in bundle
91
+ * size, and removes a peer dependency.
92
+ * - The badge collapses when there are zero experiments — showing
93
+ * `0` is more confusing than no badge at all.
94
+ * - The component does not subscribe to engine events itself; the
95
+ * parent passes `count` so a single subscription drives the whole
96
+ * overlay.
97
+ */
98
+
99
+ type Corner = "top-left" | "top-right" | "bottom-left" | "bottom-right";
100
+
101
+ /**
102
+ * Imperative open / close API for the debug overlay.
103
+ *
104
+ * Some debug surfaces want to open the overlay from outside React —
105
+ * a long-press on a Detox e2e harness, a custom shake handler, a
106
+ * react-navigation menu item. We expose a tiny pub/sub so any number
107
+ * of mounted overlays can be opened in lock-step:
108
+ *
109
+ * import { openDebugOverlay } from "@variantlab/react-native/debug";
110
+ *
111
+ * shakeListener.on("shake", openDebugOverlay);
112
+ *
113
+ * The overlay registers/unregisters its `setVisible` callback in
114
+ * `useEffect`, and the imperative helpers fan calls out to every
115
+ * registered callback. In practice almost every app mounts a single
116
+ * overlay so the set has size 1; the registry simply guarantees we
117
+ * don't crash if a developer mounts two.
118
+ */
119
+ declare function registerOverlay(setVisible: (visible: boolean) => void): () => void;
120
+ declare function openDebugOverlay(): void;
121
+ declare function closeDebugOverlay(): void;
122
+
123
+ /**
124
+ * `<VariantDebugOverlay />` — the entire debug overlay surface.
125
+ *
126
+ * This component lives in a **separate package entrypoint**
127
+ * (`@variantlab/react-native/debug`) so production bundles never
128
+ * import it unless the app explicitly opts in. The kickoff for
129
+ * Phase 1 (`docs/phases/phase-1-kickoff-prompts.md` §6) calls out
130
+ * tree-shakeability as a hard requirement and the size budget for
131
+ * `@variantlab/react-native` excludes this entrypoint entirely.
132
+ *
133
+ * Architectural choices:
134
+ *
135
+ * - The component reads the engine from `useVariantLabEngine` (the
136
+ * hook is re-exported through `@variantlab/react`), so callers
137
+ * mount it inside the same `<VariantLabProvider>` they already
138
+ * have. No extra wiring.
139
+ * - All state lives in `useState` / `useRef` — no Context, no
140
+ * reducer. The overlay is a leaf and never re-renders the host
141
+ * app.
142
+ * - Engine subscription is consolidated into a single
143
+ * `useSyncExternalStore` call via `useEngineSnapshot`, then
144
+ * fanned out to the tab components as plain props. This means
145
+ * toggling between tabs has zero engine-side cost.
146
+ * - The overlay self-disables in production by default. The
147
+ * `forceEnable` prop is the documented escape hatch for QA
148
+ * builds; the warning fires only in `process.env.NODE_ENV === "production"`
149
+ * so jest/vitest test runs stay quiet.
150
+ */
151
+
152
+ interface VariantDebugOverlayProps {
153
+ /** Force the overlay on even outside of `__DEV__`. Default: `false`. */
154
+ readonly forceEnable?: boolean;
155
+ /** Hide the floating button entirely (open via `openDebugOverlay()`). */
156
+ readonly hideButton?: boolean;
157
+ /** Floating-button corner. */
158
+ readonly position?: Corner;
159
+ /** Pixel offset from the chosen corner. */
160
+ readonly offset?: {
161
+ x: number;
162
+ y: number;
163
+ };
164
+ /** Override the colour palette. */
165
+ readonly theme?: Partial<OverlayTheme>;
166
+ /** Show only experiments active on the current route. Default: `true`. */
167
+ readonly routeFilter?: boolean;
168
+ /** Optional safe-area inset injected by the host. */
169
+ readonly safeAreaBottom?: number;
170
+ }
171
+ declare function VariantDebugOverlay(props: VariantDebugOverlayProps): ReactElement | null;
172
+ /**
173
+ * Determines whether the overlay should render. Exported for tests.
174
+ *
175
+ * Order of precedence:
176
+ * 1. Explicit `forceEnable` prop wins.
177
+ * 2. The RN-injected `__DEV__` global is `true`.
178
+ * 3. `process.env.NODE_ENV === "development"` (web fallback).
179
+ */
180
+ declare function shouldRender(forceEnable: boolean | undefined): boolean;
181
+
182
+ /**
183
+ * "Context" tab — JSON-tree view of the active `VariantContext`.
184
+ *
185
+ * The implementation is intentionally dumb: stringify the context with
186
+ * 2-space indentation, render it inside a monospace `<Text>` block,
187
+ * and let the user scroll. Recursive React-tree expansion would look
188
+ * nicer but isn't worth the bundle size for a debug surface.
189
+ */
190
+
191
+ /** Mask the userId so screenshots don't leak PII. */
192
+ declare function stringifyContext(context: VariantContext): string;
193
+
194
+ /**
195
+ * "History" tab — scrollable list of recent engine events.
196
+ *
197
+ * Reads from `engine.getHistory()` which is backed by a fixed-size
198
+ * ring buffer. Newest events render at the top so the user always
199
+ * sees the last action they took. Each event renders as a single
200
+ * row with type, experiment id, and a tiny detail blurb.
201
+ */
202
+
203
+ /** Render a single event into a one-line human-readable string. */
204
+ declare function summarize(event: EngineEvent): string;
205
+
206
+ export { type Corner, DEFAULT_THEME, type OverlayTheme, VariantDebugOverlay, type VariantDebugOverlayProps, closeDebugOverlay, describeAssignmentSource, filterExperiments, isVisibleExperiment, matchesSearch, mergeTheme, openDebugOverlay, registerOverlay, shouldRender, stringifyContext, summarize as summarizeEvent };