react-native-inapp-inspector 2.2.2 → 2.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/dist/commonjs/constants/version.d.ts +1 -1
  2. package/dist/commonjs/constants/version.js +1 -1
  3. package/dist/esm/constants/version.d.ts +1 -1
  4. package/dist/esm/constants/version.js +1 -1
  5. package/package.json +2 -1
  6. package/src/analytics.ts +35 -0
  7. package/src/bundle.ts +25 -0
  8. package/src/components/AnalyticsDetail.tsx +865 -0
  9. package/src/components/AnalyticsEventCard.tsx +441 -0
  10. package/src/components/AnalyticsGraph.tsx +583 -0
  11. package/src/components/AnimatedEntrance.tsx +64 -0
  12. package/src/components/AppHeaderLogo.tsx +109 -0
  13. package/src/components/BrandCircleIcon.tsx +144 -0
  14. package/src/components/BrandSquareIcon.tsx +144 -0
  15. package/src/components/CodeSnippet.tsx +725 -0
  16. package/src/components/ConsoleLogCard.tsx +628 -0
  17. package/src/components/CopyButton.tsx +87 -0
  18. package/src/components/DiffViewer.tsx +82 -0
  19. package/src/components/DomainHeader.tsx +237 -0
  20. package/src/components/EmptyState.tsx +73 -0
  21. package/src/components/EndOfListFooter.tsx +100 -0
  22. package/src/components/ErrorBoundary.tsx +688 -0
  23. package/src/components/HeadersSection.tsx +192 -0
  24. package/src/components/HighlightText.tsx +100 -0
  25. package/src/components/Inspector/AnalyticsFilterModal.tsx +1250 -0
  26. package/src/components/Inspector/AnalyticsTab.tsx +1336 -0
  27. package/src/components/Inspector/BundleTab.tsx +4731 -0
  28. package/src/components/Inspector/ConsoleTab.tsx +702 -0
  29. package/src/components/Inspector/CrashDetail.tsx +941 -0
  30. package/src/components/Inspector/CrashFilterModal.tsx +721 -0
  31. package/src/components/Inspector/CrashTab.tsx +871 -0
  32. package/src/components/Inspector/FabLauncher.tsx +80 -0
  33. package/src/components/Inspector/InspectorContext.tsx +28 -0
  34. package/src/components/Inspector/InspectorHeader.tsx +973 -0
  35. package/src/components/Inspector/LogDetail.tsx +1427 -0
  36. package/src/components/Inspector/MainScreen.tsx +258 -0
  37. package/src/components/Inspector/NavigationTracker.tsx +13 -0
  38. package/src/components/Inspector/NetworkDetail.tsx +794 -0
  39. package/src/components/Inspector/NetworkTab.tsx +1095 -0
  40. package/src/components/Inspector/NpmUpdateToast.tsx +392 -0
  41. package/src/components/Inspector/PerformanceTab.tsx +1894 -0
  42. package/src/components/Inspector/ReduxDetail.tsx +1651 -0
  43. package/src/components/Inspector/ReduxTab.tsx +954 -0
  44. package/src/components/Inspector/SettingsPanel.tsx +3181 -0
  45. package/src/components/Inspector/TabBar.tsx +187 -0
  46. package/src/components/Inspector/TelemetryConsentModal.tsx +392 -0
  47. package/src/components/Inspector/UpdateAvailableModal.tsx +545 -0
  48. package/src/components/JsonViewer.tsx +486 -0
  49. package/src/components/LogCard.tsx +491 -0
  50. package/src/components/LogSyntaxHighlighter.tsx +178 -0
  51. package/src/components/MetaAccordion.tsx +340 -0
  52. package/src/components/MiniBarChart.tsx +42 -0
  53. package/src/components/MiniLineChart.tsx +33 -0
  54. package/src/components/NetworkIcons.tsx +2118 -0
  55. package/src/components/SectionHeader.tsx +113 -0
  56. package/src/components/SegmentedTabs.tsx +83 -0
  57. package/src/components/Slider.tsx +300 -0
  58. package/src/components/SourcePageCard.tsx +148 -0
  59. package/src/components/Toast.tsx +131 -0
  60. package/src/components/TouchableScale.tsx +91 -0
  61. package/src/components/TreeNode.tsx +186 -0
  62. package/src/console.ts +24 -0
  63. package/src/constants/index.ts +38 -0
  64. package/src/constants/version.ts +3 -0
  65. package/src/crash.ts +32 -0
  66. package/src/customHooks/analyticsLogger.ts +336 -0
  67. package/src/customHooks/bundleAnalyzer.ts +1231 -0
  68. package/src/customHooks/consoleLogger.ts +497 -0
  69. package/src/customHooks/crashHandler.ts +944 -0
  70. package/src/customHooks/logFilters.ts +32 -0
  71. package/src/customHooks/networkLogger.ts +419 -0
  72. package/src/customHooks/performanceTracker.ts +1014 -0
  73. package/src/customHooks/reduxLogger.ts +406 -0
  74. package/src/customHooks/useAccordion.tsx +60 -0
  75. package/src/decorators/index.ts +184 -0
  76. package/src/helpers/gaAnalyticsRegistry.ts +204 -0
  77. package/src/helpers/index.ts +860 -0
  78. package/src/helpers/memoryManager.ts +138 -0
  79. package/src/helpers/searchQueryParser.ts +283 -0
  80. package/src/helpers/settingsStore.ts +171 -0
  81. package/src/helpers/telemetry.ts +505 -0
  82. package/src/helpers/toast.ts +17 -0
  83. package/src/i18n/index.ts +69 -0
  84. package/src/i18n/locales/en.json +1052 -0
  85. package/src/index.tsx +2336 -0
  86. package/src/native/NativeInspector.ts +397 -0
  87. package/src/native/NativeNetworkInspector.ts +24 -0
  88. package/src/network.ts +22 -0
  89. package/src/performance.ts +38 -0
  90. package/src/redux.ts +23 -0
  91. package/src/styles/AppColors.ts +408 -0
  92. package/src/styles/AppFonts.ts +8 -0
  93. package/src/styles/common.ts +209 -0
  94. package/src/styles/index.ts +1570 -0
  95. package/src/types/enums.ts +194 -0
  96. package/src/types/index.ts +34 -0
  97. package/src/types/interfaces.ts +515 -0
@@ -0,0 +1,113 @@
1
+ import React, {useRef, useEffect} from 'react';
2
+ import {Animated, View, Pressable, Text} from 'react-native';
3
+
4
+ // Stylesheet
5
+ import {AppColors} from '../styles/AppColors';
6
+
7
+ // Components
8
+ import CopyButton from './CopyButton';
9
+ import TouchableScale from './TouchableScale';
10
+
11
+ // Assets
12
+ import {
13
+ RequestIcon,
14
+ ResponseIcon,
15
+ HeadersIcon,
16
+ DiffIcon,
17
+ ChevronIcon,
18
+ SearchIcon,
19
+ } from './NetworkIcons';
20
+
21
+ // Stylesheet
22
+ import styles from '../styles';
23
+
24
+ // Type Definition
25
+ import {SectionHeaderProps} from '../types';
26
+
27
+ const SectionHeader = ({
28
+ title,
29
+ value,
30
+ expanded,
31
+ onToggleExpand,
32
+ showDiff,
33
+ isDiffing,
34
+ onToggleDiff,
35
+ showSearch,
36
+ isSearching,
37
+ onToggleSearch,
38
+ }: SectionHeaderProps) => {
39
+ const isOpen = expanded === undefined ? false : !!expanded;
40
+ const chevronAnim = useRef(new Animated.Value(isOpen ? 1 : 0)).current;
41
+ useEffect(() => {
42
+ Animated.timing(chevronAnim, {
43
+ toValue: isOpen ? 1 : 0,
44
+ duration: 220,
45
+ useNativeDriver: true,
46
+ }).start();
47
+ }, [isOpen]);
48
+ const chevronRotate = chevronAnim.interpolate({
49
+ inputRange: [0, 1],
50
+ outputRange: ['0deg', '180deg'],
51
+ });
52
+
53
+ return (
54
+ <View style={styles.sectionHeaderGradient}>
55
+ <View style={styles.sectionHeaderRow}>
56
+ <Pressable
57
+ style={styles.sectionTitleRow}
58
+ onPress={onToggleExpand}
59
+ hitSlop={12}>
60
+ {title === 'Request' || title === 'API' ? (
61
+ <RequestIcon color={AppColors.offerPurple} />
62
+ ) : title === 'Response' ? (
63
+ <ResponseIcon color={AppColors.greenColor} />
64
+ ) : (
65
+ <HeadersIcon color={AppColors.skyBlue} />
66
+ )}
67
+ <Text style={styles.sectionTitle}>{title}</Text>
68
+ </Pressable>
69
+ <View style={styles.sectionHeaderActions}>
70
+ {showSearch && (
71
+ <TouchableScale
72
+ onPress={onToggleSearch}
73
+ hitSlop={12}
74
+ style={[
75
+ styles.iconSquareBtn,
76
+ isSearching ? styles.iconSquareBtnActive : null,
77
+ ]}>
78
+ <SearchIcon
79
+ color={isSearching ? AppColors.skyBlue : AppColors.grayTextWeak}
80
+ size={14}
81
+ />
82
+ </TouchableScale>
83
+ )}
84
+ {showDiff && (
85
+ <TouchableScale
86
+ onPress={onToggleDiff}
87
+ hitSlop={12}
88
+ style={[
89
+ styles.iconSquareBtn,
90
+ isDiffing ? styles.iconSquareBtnActive : null,
91
+ ]}>
92
+ <DiffIcon
93
+ color={isDiffing ? AppColors.skyBlue : AppColors.grayTextWeak}
94
+ size={14}
95
+ />
96
+ </TouchableScale>
97
+ )}
98
+ <CopyButton value={value} label={title} />
99
+ <TouchableScale
100
+ onPress={onToggleExpand}
101
+ hitSlop={12}
102
+ style={styles.iconSquareBtn}>
103
+ <Animated.View style={{transform: [{rotate: chevronRotate}]}}>
104
+ <ChevronIcon color={AppColors.grayTextStrong} size={14} />
105
+ </Animated.View>
106
+ </TouchableScale>
107
+ </View>
108
+ </View>
109
+ </View>
110
+ );
111
+ };
112
+
113
+ export default SectionHeader;
@@ -0,0 +1,83 @@
1
+ import React from 'react';
2
+ import {Pressable, Text, View, ViewStyle} from 'react-native';
3
+ import {AppColors} from '../styles/AppColors';
4
+ import {AppFonts} from '../styles/AppFonts';
5
+
6
+ export interface SegmentedTab {
7
+ key: string;
8
+ label: string;
9
+ icon?: (isActive: boolean) => React.ReactNode;
10
+ }
11
+
12
+ interface SegmentedTabsProps {
13
+ tabs: SegmentedTab[];
14
+ activeKey: string;
15
+ onChange: (key: string) => void;
16
+ style?: ViewStyle | ViewStyle[];
17
+ }
18
+
19
+ const SegmentedTabs = React.memo(({tabs, activeKey, onChange, style}: SegmentedTabsProps) => (
20
+ <View
21
+ style={[
22
+ {
23
+ flexDirection: 'row',
24
+ borderRadius: 10,
25
+ backgroundColor: `${AppColors.slate200}80`,
26
+ padding: 3,
27
+ borderWidth: 1,
28
+ borderColor: AppColors.dividerColor,
29
+ gap: 3,
30
+ },
31
+ style,
32
+ ]}>
33
+ {tabs.map((tab) => {
34
+ const isActive = activeKey === tab.key;
35
+ return (
36
+ <Pressable
37
+ key={tab.key}
38
+ onPress={() => onChange(tab.key)}
39
+ style={({pressed}) => [
40
+ {
41
+ flex: 1,
42
+ paddingVertical: 7,
43
+ paddingHorizontal: 6,
44
+ borderRadius: 7,
45
+ backgroundColor: isActive
46
+ ? AppColors.brandPurple
47
+ : 'transparent',
48
+ alignItems: 'center',
49
+ justifyContent: 'center',
50
+ gap: 4.5,
51
+ flexDirection: 'row',
52
+ opacity: pressed ? 0.8 : 1,
53
+ },
54
+ isActive && {
55
+ shadowColor: AppColors.brandPurple,
56
+ shadowOffset: {width: 0, height: 1},
57
+ shadowOpacity: 0.25,
58
+ shadowRadius: 3,
59
+ elevation: 2,
60
+ },
61
+ ]}>
62
+ {tab.icon ? tab.icon(isActive) : null}
63
+ <Text
64
+ numberOfLines={1}
65
+ ellipsizeMode="tail"
66
+ style={{
67
+ flexShrink: 1,
68
+ fontFamily: AppFonts.interBold,
69
+ fontSize: 10.5,
70
+ color: isActive
71
+ ? AppColors.white
72
+ : AppColors.grayText,
73
+ letterSpacing: 0.2,
74
+ }}>
75
+ {tab.label}
76
+ </Text>
77
+ </Pressable>
78
+ );
79
+ })}
80
+ </View>
81
+ ));
82
+
83
+ export default SegmentedTabs;
@@ -0,0 +1,300 @@
1
+ import React, {useRef, useState, useCallback, useMemo} from 'react';
2
+ import {
3
+ View,
4
+ Text,
5
+ StyleSheet,
6
+ PanResponder,
7
+ LayoutChangeEvent,
8
+ GestureResponderEvent,
9
+ PanResponderGestureState,
10
+ } from 'react-native';
11
+ import {AppColors} from '../styles/AppColors';
12
+ import {AppFonts} from '../styles/AppFonts';
13
+ import TouchableScale from './TouchableScale';
14
+
15
+ export interface SliderProps {
16
+ value: number;
17
+ onValueChange: (value: number) => void;
18
+ min?: number;
19
+ max?: number;
20
+ step?: number;
21
+ formatLabel?: (value: number) => string;
22
+ quickPresets?: number[];
23
+ disabled?: boolean;
24
+ }
25
+
26
+ export const Slider: React.FC<SliderProps> = React.memo(
27
+ ({
28
+ value,
29
+ onValueChange,
30
+ min = 50,
31
+ max = 90,
32
+ step = 5,
33
+ formatLabel = val => `${Math.round(val)}%`,
34
+ quickPresets = [50, 60, 70, 80, 90],
35
+ disabled = false,
36
+ }) => {
37
+ const trackWidthRef = useRef<number>(0);
38
+ const [trackWidth, setTrackWidth] = useState<number>(0);
39
+ const [isDragging, setIsDragging] = useState<boolean>(false);
40
+
41
+ const clampedValue = Math.max(min, Math.min(max, value));
42
+ const percentage = (clampedValue - min) / (max - min || 1);
43
+
44
+ const updateValueFromPosition = useCallback(
45
+ (pageX: number, trackPageX: number) => {
46
+ if (trackWidthRef.current <= 0) return;
47
+ const relativeX = Math.max(
48
+ 0,
49
+ Math.min(trackWidthRef.current, pageX - trackPageX),
50
+ );
51
+ const rawRatio = relativeX / trackWidthRef.current;
52
+ let newValue = min + rawRatio * (max - min);
53
+
54
+ if (step > 0) {
55
+ newValue = Math.round(newValue / step) * step;
56
+ }
57
+ newValue = Math.max(min, Math.min(max, newValue));
58
+ onValueChange(newValue);
59
+ },
60
+ [min, max, step, onValueChange],
61
+ );
62
+
63
+ const trackContainerRef = useRef<View>(null);
64
+ const trackPageXRef = useRef<number>(0);
65
+
66
+ const measureTrack = useCallback(() => {
67
+ if (trackContainerRef.current) {
68
+ trackContainerRef.current.measure((_x, _y, _width, _height, pageX) => {
69
+ trackPageXRef.current = pageX;
70
+ });
71
+ }
72
+ }, []);
73
+
74
+ const panResponder = useMemo(
75
+ () =>
76
+ PanResponder.create({
77
+ onStartShouldSetPanResponder: () => !disabled,
78
+ onMoveShouldSetPanResponder: () => !disabled,
79
+ onPanResponderGrant: (evt: GestureResponderEvent) => {
80
+ setIsDragging(true);
81
+ measureTrack();
82
+ updateValueFromPosition(evt.nativeEvent.pageX, trackPageXRef.current);
83
+ },
84
+ onPanResponderMove: (
85
+ evt: GestureResponderEvent,
86
+ _gestureState: PanResponderGestureState,
87
+ ) => {
88
+ updateValueFromPosition(evt.nativeEvent.pageX, trackPageXRef.current);
89
+ },
90
+ onPanResponderRelease: () => {
91
+ setIsDragging(false);
92
+ },
93
+ onPanResponderTerminate: () => {
94
+ setIsDragging(false);
95
+ },
96
+ }),
97
+ [disabled, measureTrack, updateValueFromPosition],
98
+ );
99
+
100
+ const handleLayout = (e: LayoutChangeEvent) => {
101
+ const w = e.nativeEvent.layout.width;
102
+ trackWidthRef.current = w;
103
+ setTrackWidth(w);
104
+ measureTrack();
105
+ };
106
+
107
+ const thumbPosition = percentage * (trackWidth || 0);
108
+
109
+ return (
110
+ <View style={styles.container}>
111
+ {/* Value Display Row */}
112
+ <View style={styles.valueRow}>
113
+ <Text style={styles.rangeText}>{min}%</Text>
114
+ <View
115
+ style={[
116
+ styles.currentValueBadge,
117
+ isDragging && styles.currentValueBadgeActive,
118
+ ]}>
119
+ <Text style={styles.currentValueText}>{formatLabel(clampedValue)}</Text>
120
+ </View>
121
+ <Text style={styles.rangeText}>{max}%</Text>
122
+ </View>
123
+
124
+ {/* Interactive Track Area */}
125
+ <View
126
+ ref={trackContainerRef}
127
+ onLayout={handleLayout}
128
+ {...panResponder.panHandlers}
129
+ style={styles.touchArea}>
130
+ {/* Background Rail */}
131
+ <View style={styles.rail}>
132
+ {/* Active Filled Bar */}
133
+ <View
134
+ style={[
135
+ styles.fillBar,
136
+ {width: `${Math.round(percentage * 100)}%`},
137
+ ]}
138
+ />
139
+ </View>
140
+
141
+ {/* Draggable Thumb */}
142
+ <View
143
+ style={[
144
+ styles.thumb,
145
+ {
146
+ left: Math.max(
147
+ 0,
148
+ Math.min(
149
+ (trackWidth || 0) - 24,
150
+ thumbPosition - 12,
151
+ ),
152
+ ),
153
+ },
154
+ isDragging && styles.thumbActive,
155
+ ]}>
156
+ <View style={styles.thumbCenterDot} />
157
+ </View>
158
+ </View>
159
+
160
+ {/* Quick Presets */}
161
+ {quickPresets && quickPresets.length > 0 && (
162
+ <View style={styles.presetRow}>
163
+ {quickPresets.map(preset => {
164
+ const isSelected = Math.round(clampedValue) === preset;
165
+ return (
166
+ <TouchableScale
167
+ key={preset}
168
+ onPress={() => onValueChange(preset)}
169
+ style={[
170
+ styles.presetButton,
171
+ isSelected && styles.presetButtonActive,
172
+ ]}>
173
+ <Text
174
+ style={[
175
+ styles.presetText,
176
+ isSelected && styles.presetTextActive,
177
+ ]}>
178
+ {preset}%
179
+ </Text>
180
+ </TouchableScale>
181
+ );
182
+ })}
183
+ </View>
184
+ )}
185
+ </View>
186
+ );
187
+ },
188
+ );
189
+
190
+ const styles = StyleSheet.create({
191
+ container: {
192
+ paddingVertical: 4,
193
+ gap: 8,
194
+ },
195
+ valueRow: {
196
+ flexDirection: 'row',
197
+ alignItems: 'center',
198
+ justifyContent: 'space-between',
199
+ paddingHorizontal: 2,
200
+ },
201
+ rangeText: {
202
+ fontFamily: AppFonts.interMedium,
203
+ fontSize: 11,
204
+ color: AppColors.grayText,
205
+ },
206
+ currentValueBadge: {
207
+ backgroundColor: `${AppColors.purple}1A`,
208
+ borderColor: `${AppColors.purple}33`,
209
+ borderWidth: 1,
210
+ borderRadius: 8,
211
+ paddingHorizontal: 10,
212
+ paddingVertical: 2.5,
213
+ },
214
+ currentValueBadgeActive: {
215
+ backgroundColor: AppColors.purple,
216
+ borderColor: AppColors.purple,
217
+ transform: [{scale: 1.05}],
218
+ },
219
+ currentValueText: {
220
+ fontFamily: AppFonts.interBold,
221
+ fontSize: 12,
222
+ color: AppColors.purple,
223
+ },
224
+ touchArea: {
225
+ height: 36,
226
+ justifyContent: 'center',
227
+ position: 'relative',
228
+ },
229
+ rail: {
230
+ height: 8,
231
+ backgroundColor: `${AppColors.grayTextWeak}2B`,
232
+ borderRadius: 4,
233
+ overflow: 'hidden',
234
+ position: 'relative',
235
+ },
236
+ fillBar: {
237
+ height: '100%',
238
+ backgroundColor: AppColors.purple,
239
+ borderRadius: 4,
240
+ },
241
+ thumb: {
242
+ position: 'absolute',
243
+ width: 24,
244
+ height: 24,
245
+ borderRadius: 12,
246
+ backgroundColor: AppColors.white,
247
+ borderWidth: 2.5,
248
+ borderColor: AppColors.purple,
249
+ alignItems: 'center',
250
+ justifyContent: 'center',
251
+ shadowColor: AppColors.black,
252
+ shadowOffset: {width: 0, height: 2},
253
+ shadowOpacity: 0.18,
254
+ shadowRadius: 3.5,
255
+ elevation: 4,
256
+ },
257
+ thumbActive: {
258
+ transform: [{scale: 1.15}],
259
+ shadowOpacity: 0.28,
260
+ shadowRadius: 5,
261
+ elevation: 6,
262
+ borderColor: AppColors.purple,
263
+ },
264
+ thumbCenterDot: {
265
+ width: 6,
266
+ height: 6,
267
+ borderRadius: 3,
268
+ backgroundColor: AppColors.purple,
269
+ },
270
+ presetRow: {
271
+ flexDirection: 'row',
272
+ alignItems: 'center',
273
+ justifyContent: 'space-between',
274
+ gap: 8,
275
+ marginTop: 2,
276
+ },
277
+ presetButton: {
278
+ flex: 1,
279
+ paddingVertical: 5,
280
+ borderRadius: 7,
281
+ alignItems: 'center',
282
+ backgroundColor: AppColors.grayBackground,
283
+ borderWidth: 1,
284
+ borderColor: AppColors.dividerColor,
285
+ },
286
+ presetButtonActive: {
287
+ backgroundColor: `${AppColors.purple}20`,
288
+ borderColor: AppColors.purple,
289
+ },
290
+ presetText: {
291
+ fontFamily: AppFonts.interBold,
292
+ fontSize: 10.5,
293
+ color: AppColors.grayText,
294
+ },
295
+ presetTextActive: {
296
+ color: AppColors.purple,
297
+ },
298
+ });
299
+
300
+ export default Slider;
@@ -0,0 +1,148 @@
1
+ import React from 'react';
2
+ import {View, Pressable, Text, Animated} from 'react-native';
3
+ import Svg, {Rect, Path} from 'react-native-svg';
4
+
5
+ // Components
6
+ import CopyButton from './CopyButton';
7
+
8
+ // Custom Hooks
9
+ import useAccordion from '../customHooks/useAccordion';
10
+
11
+ // Assets
12
+ import {ChevronIcon} from './NetworkIcons';
13
+
14
+ // Type Definition
15
+ import {SourcePageCardProps} from '../types';
16
+
17
+ // Stylesheet
18
+ import {AppColors} from '../styles/AppColors';
19
+ import styles from '../styles';
20
+
21
+ const SourcePageCard = ({routeInfo}: SourcePageCardProps) => {
22
+ const main = useAccordion(true, 600, 280);
23
+ const params = useAccordion(false, 800, 260);
24
+
25
+ const hasParams =
26
+ routeInfo.params && Object.keys(routeInfo.params).length > 0;
27
+
28
+ return (
29
+ <View style={styles.sourcePageCard}>
30
+ <Pressable onPress={main.toggleOpen} hitSlop={12}>
31
+ <View style={styles.sourcePageAccordionHeader}>
32
+ <View style={styles.sourcePageTop}>
33
+ <View style={styles.sourcePageIcon}>
34
+ <Svg width={14} height={14} viewBox="0 0 24 24" fill="none">
35
+ <Rect
36
+ x="3"
37
+ y="3"
38
+ width="7"
39
+ height="7"
40
+ rx="1.5"
41
+ stroke={AppColors.purple}
42
+ strokeWidth="2"
43
+ />
44
+ <Rect
45
+ x="14"
46
+ y="3"
47
+ width="7"
48
+ height="7"
49
+ rx="1.5"
50
+ stroke={AppColors.purple}
51
+ strokeWidth="2"
52
+ />
53
+ <Rect
54
+ x="3"
55
+ y="14"
56
+ width="7"
57
+ height="7"
58
+ rx="1.5"
59
+ stroke={AppColors.purple}
60
+ strokeWidth="2"
61
+ />
62
+ <Path
63
+ d="M14 17.5h7M17.5 14v7"
64
+ stroke={AppColors.purple}
65
+ strokeWidth="2"
66
+ strokeLinecap="round"
67
+ />
68
+ </Svg>
69
+ </View>
70
+ <View style={{flex: 1}}>
71
+ <Text style={styles.sourcePageLabel}>Source Page</Text>
72
+ <Text style={styles.sourcePageValue} numberOfLines={1}>
73
+ {routeInfo.path}
74
+ </Text>
75
+ </View>
76
+ </View>
77
+ <View style={styles.sourcePageHeaderRight}>
78
+ <CopyButton value={routeInfo.path} label="Source Path" />
79
+ <View style={styles.iconSquareBtn}>
80
+ <Animated.View style={main.chevronStyle}>
81
+ <ChevronIcon color={AppColors.grayTextStrong} size={14} />
82
+ </Animated.View>
83
+ </View>
84
+ </View>
85
+ </View>
86
+ </Pressable>
87
+
88
+ <Animated.View style={main.bodyStyle}>
89
+ {hasParams && (
90
+ <View style={styles.sourceParamsBox}>
91
+ <Pressable onPress={params.toggleOpen} hitSlop={10}>
92
+ <View style={styles.paramsAccordionHeader}>
93
+ <View style={styles.paramsAccordionLeft}>
94
+ <Text style={styles.sourceParamsLabel}>Parameters</Text>
95
+ <View style={styles.headerCountBadge}>
96
+ <Text style={styles.headerCountText}>
97
+ {Object.keys(routeInfo.params).length}
98
+ </Text>
99
+ </View>
100
+ </View>
101
+ <View style={styles.paramsAccordionRight}>
102
+ <CopyButton value={routeInfo.params} label="Parameters" />
103
+ <View style={styles.iconSquareBtn}>
104
+ <Animated.View style={params.chevronStyle}>
105
+ <ChevronIcon color={AppColors.grayTextStrong} size={14} />
106
+ </Animated.View>
107
+ </View>
108
+ </View>
109
+ </View>
110
+ </Pressable>
111
+
112
+ <Animated.View style={params.bodyStyle}>
113
+ <View style={styles.paramsBody}>
114
+ {Object.entries(routeInfo.params).map(([key, val], i, arr) => (
115
+ <View
116
+ key={key}
117
+ style={[
118
+ styles.paramRow,
119
+ i < arr.length - 1 ? styles.paramRowBorder : null,
120
+ ]}>
121
+ <Text style={styles.paramKey}>{key}</Text>
122
+ <View style={styles.paramValueRow}>
123
+ <Text selectable style={styles.paramValue}>
124
+ {typeof val === 'object'
125
+ ? JSON.stringify(val)
126
+ : String(val)}
127
+ </Text>
128
+ <CopyButton
129
+ value={
130
+ typeof val === 'object'
131
+ ? JSON.stringify(val)
132
+ : String(val)
133
+ }
134
+ label={key}
135
+ />
136
+ </View>
137
+ </View>
138
+ ))}
139
+ </View>
140
+ </Animated.View>
141
+ </View>
142
+ )}
143
+ </Animated.View>
144
+ </View>
145
+ );
146
+ };
147
+
148
+ export default SourcePageCard;