react-native-inapp-inspector 2.2.2 → 2.2.4

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 (159) hide show
  1. package/dist/commonjs/components/AnalyticsDetail.js +5 -1
  2. package/dist/commonjs/components/AnalyticsEventCard.js +2 -1
  3. package/dist/commonjs/components/DiffViewer.js +1 -1
  4. package/dist/commonjs/components/Inspector/BundleTab.js +22 -0
  5. package/dist/commonjs/components/Inspector/DeviceInfoTab.d.ts +3 -0
  6. package/dist/commonjs/components/Inspector/DeviceInfoTab.js +835 -0
  7. package/dist/commonjs/components/Inspector/InspectorHeader.js +12 -14
  8. package/dist/commonjs/components/Inspector/LogDetail.js +6 -3
  9. package/dist/commonjs/components/Inspector/MainScreen.js +7 -3
  10. package/dist/commonjs/components/Inspector/NetworkTab.js +92 -15
  11. package/dist/commonjs/components/Inspector/PerformanceTab.js +38 -12
  12. package/dist/commonjs/components/Inspector/SettingsPanel.js +440 -229
  13. package/dist/commonjs/components/Inspector/StorageTab.d.ts +3 -0
  14. package/dist/commonjs/components/Inspector/StorageTab.js +892 -0
  15. package/dist/commonjs/components/Inspector/TabBar.js +14 -0
  16. package/dist/commonjs/components/Inspector/TelemetryConsentModal.js +11 -2
  17. package/dist/commonjs/components/Inspector/UpdateAvailableModal.js +4 -1
  18. package/dist/commonjs/components/NetworkIcons.d.ts +6 -0
  19. package/dist/commonjs/components/NetworkIcons.js +30 -1
  20. package/dist/commonjs/constants/version.d.ts +1 -1
  21. package/dist/commonjs/constants/version.js +1 -1
  22. package/dist/commonjs/customHooks/bundleAnalyzer.js +44 -22
  23. package/dist/commonjs/customHooks/storageInspector.d.ts +67 -0
  24. package/dist/commonjs/customHooks/storageInspector.js +486 -0
  25. package/dist/commonjs/i18n/locales/en.json +8 -2
  26. package/dist/commonjs/index.d.ts +1 -0
  27. package/dist/commonjs/index.js +16 -1
  28. package/dist/commonjs/storage.d.ts +5 -0
  29. package/dist/commonjs/storage.js +18 -0
  30. package/dist/commonjs/types/enums.d.ts +5 -0
  31. package/dist/commonjs/types/enums.js +5 -0
  32. package/dist/esm/components/AnalyticsDetail.js +6 -2
  33. package/dist/esm/components/AnalyticsEventCard.js +2 -1
  34. package/dist/esm/components/DiffViewer.js +1 -1
  35. package/dist/esm/components/Inspector/BundleTab.js +22 -0
  36. package/dist/esm/components/Inspector/DeviceInfoTab.d.ts +3 -0
  37. package/dist/esm/components/Inspector/DeviceInfoTab.js +796 -0
  38. package/dist/esm/components/Inspector/InspectorHeader.js +13 -15
  39. package/dist/esm/components/Inspector/LogDetail.js +7 -4
  40. package/dist/esm/components/Inspector/MainScreen.js +7 -3
  41. package/dist/esm/components/Inspector/NetworkTab.js +93 -16
  42. package/dist/esm/components/Inspector/PerformanceTab.js +39 -13
  43. package/dist/esm/components/Inspector/SettingsPanel.js +441 -230
  44. package/dist/esm/components/Inspector/StorageTab.d.ts +3 -0
  45. package/dist/esm/components/Inspector/StorageTab.js +853 -0
  46. package/dist/esm/components/Inspector/TabBar.js +15 -1
  47. package/dist/esm/components/Inspector/TelemetryConsentModal.js +11 -2
  48. package/dist/esm/components/Inspector/UpdateAvailableModal.js +4 -1
  49. package/dist/esm/components/NetworkIcons.d.ts +6 -0
  50. package/dist/esm/components/NetworkIcons.js +23 -0
  51. package/dist/esm/constants/version.d.ts +1 -1
  52. package/dist/esm/constants/version.js +1 -1
  53. package/dist/esm/customHooks/bundleAnalyzer.js +44 -22
  54. package/dist/esm/customHooks/storageInspector.d.ts +67 -0
  55. package/dist/esm/customHooks/storageInspector.js +469 -0
  56. package/dist/esm/i18n/locales/en.json +8 -2
  57. package/dist/esm/index.d.ts +1 -0
  58. package/dist/esm/index.js +5 -0
  59. package/dist/esm/storage.d.ts +5 -0
  60. package/dist/esm/storage.js +5 -0
  61. package/dist/esm/types/enums.d.ts +5 -0
  62. package/dist/esm/types/enums.js +5 -0
  63. package/package.json +9 -1
  64. package/src/analytics.ts +35 -0
  65. package/src/bundle.ts +25 -0
  66. package/src/components/AnalyticsDetail.tsx +870 -0
  67. package/src/components/AnalyticsEventCard.tsx +442 -0
  68. package/src/components/AnalyticsGraph.tsx +583 -0
  69. package/src/components/AnimatedEntrance.tsx +64 -0
  70. package/src/components/AppHeaderLogo.tsx +109 -0
  71. package/src/components/BrandCircleIcon.tsx +144 -0
  72. package/src/components/BrandSquareIcon.tsx +144 -0
  73. package/src/components/CodeSnippet.tsx +725 -0
  74. package/src/components/ConsoleLogCard.tsx +628 -0
  75. package/src/components/CopyButton.tsx +87 -0
  76. package/src/components/DiffViewer.tsx +82 -0
  77. package/src/components/DomainHeader.tsx +237 -0
  78. package/src/components/EmptyState.tsx +73 -0
  79. package/src/components/EndOfListFooter.tsx +100 -0
  80. package/src/components/ErrorBoundary.tsx +688 -0
  81. package/src/components/HeadersSection.tsx +192 -0
  82. package/src/components/HighlightText.tsx +100 -0
  83. package/src/components/Inspector/AnalyticsFilterModal.tsx +1250 -0
  84. package/src/components/Inspector/AnalyticsTab.tsx +1336 -0
  85. package/src/components/Inspector/BundleTab.tsx +4760 -0
  86. package/src/components/Inspector/ConsoleTab.tsx +702 -0
  87. package/src/components/Inspector/CrashDetail.tsx +941 -0
  88. package/src/components/Inspector/CrashFilterModal.tsx +721 -0
  89. package/src/components/Inspector/CrashTab.tsx +871 -0
  90. package/src/components/Inspector/DeviceInfoTab.tsx +1204 -0
  91. package/src/components/Inspector/FabLauncher.tsx +80 -0
  92. package/src/components/Inspector/InspectorContext.tsx +28 -0
  93. package/src/components/Inspector/InspectorHeader.tsx +972 -0
  94. package/src/components/Inspector/LogDetail.tsx +1431 -0
  95. package/src/components/Inspector/MainScreen.tsx +262 -0
  96. package/src/components/Inspector/NavigationTracker.tsx +13 -0
  97. package/src/components/Inspector/NetworkDetail.tsx +794 -0
  98. package/src/components/Inspector/NetworkTab.tsx +1183 -0
  99. package/src/components/Inspector/NpmUpdateToast.tsx +392 -0
  100. package/src/components/Inspector/PerformanceTab.tsx +1928 -0
  101. package/src/components/Inspector/ReduxDetail.tsx +1651 -0
  102. package/src/components/Inspector/ReduxTab.tsx +954 -0
  103. package/src/components/Inspector/SettingsPanel.tsx +3447 -0
  104. package/src/components/Inspector/StorageTab.tsx +1048 -0
  105. package/src/components/Inspector/TabBar.tsx +207 -0
  106. package/src/components/Inspector/TelemetryConsentModal.tsx +416 -0
  107. package/src/components/Inspector/UpdateAvailableModal.tsx +557 -0
  108. package/src/components/JsonViewer.tsx +486 -0
  109. package/src/components/LogCard.tsx +491 -0
  110. package/src/components/LogSyntaxHighlighter.tsx +178 -0
  111. package/src/components/MetaAccordion.tsx +340 -0
  112. package/src/components/MiniBarChart.tsx +42 -0
  113. package/src/components/MiniLineChart.tsx +33 -0
  114. package/src/components/NetworkIcons.tsx +2221 -0
  115. package/src/components/SectionHeader.tsx +113 -0
  116. package/src/components/SegmentedTabs.tsx +83 -0
  117. package/src/components/Slider.tsx +300 -0
  118. package/src/components/SourcePageCard.tsx +148 -0
  119. package/src/components/Toast.tsx +131 -0
  120. package/src/components/TouchableScale.tsx +91 -0
  121. package/src/components/TreeNode.tsx +186 -0
  122. package/src/console.ts +24 -0
  123. package/src/constants/index.ts +38 -0
  124. package/src/constants/version.ts +3 -0
  125. package/src/crash.ts +32 -0
  126. package/src/customHooks/analyticsLogger.ts +336 -0
  127. package/src/customHooks/bundleAnalyzer.ts +1256 -0
  128. package/src/customHooks/consoleLogger.ts +497 -0
  129. package/src/customHooks/crashHandler.ts +944 -0
  130. package/src/customHooks/logFilters.ts +32 -0
  131. package/src/customHooks/networkLogger.ts +419 -0
  132. package/src/customHooks/performanceTracker.ts +1014 -0
  133. package/src/customHooks/reduxLogger.ts +406 -0
  134. package/src/customHooks/storageInspector.ts +509 -0
  135. package/src/customHooks/useAccordion.tsx +60 -0
  136. package/src/decorators/index.ts +184 -0
  137. package/src/helpers/gaAnalyticsRegistry.ts +204 -0
  138. package/src/helpers/index.ts +860 -0
  139. package/src/helpers/memoryManager.ts +138 -0
  140. package/src/helpers/searchQueryParser.ts +283 -0
  141. package/src/helpers/settingsStore.ts +171 -0
  142. package/src/helpers/telemetry.ts +505 -0
  143. package/src/helpers/toast.ts +17 -0
  144. package/src/i18n/index.ts +69 -0
  145. package/src/i18n/locales/en.json +1058 -0
  146. package/src/index.tsx +2356 -0
  147. package/src/native/NativeInspector.ts +397 -0
  148. package/src/native/NativeNetworkInspector.ts +24 -0
  149. package/src/network.ts +22 -0
  150. package/src/performance.ts +38 -0
  151. package/src/redux.ts +23 -0
  152. package/src/storage.ts +18 -0
  153. package/src/styles/AppColors.ts +408 -0
  154. package/src/styles/AppFonts.ts +8 -0
  155. package/src/styles/common.ts +209 -0
  156. package/src/styles/index.ts +1570 -0
  157. package/src/types/enums.ts +199 -0
  158. package/src/types/index.ts +34 -0
  159. package/src/types/interfaces.ts +515 -0
@@ -0,0 +1,972 @@
1
+ import React, {useMemo} from 'react';
2
+ import {
3
+ Alert,
4
+ Animated,
5
+ Linking,
6
+ Platform,
7
+ Pressable,
8
+ Text,
9
+ View,
10
+ } from 'react-native';
11
+ import LinearGradient from 'react-native-linear-gradient';
12
+ import {useInspector} from './InspectorContext';
13
+ import TouchableScale from '../TouchableScale';
14
+ import AppHeaderLogo from '../AppHeaderLogo';
15
+ import styles from '../../styles';
16
+ import {AppColors} from '../../styles/AppColors';
17
+ import {AppFonts} from '../../styles/AppFonts';
18
+ import {METHOD_COLORS} from '../../constants';
19
+ import {LIB_VERSION} from '../../constants';
20
+ import {Method} from '../../types';
21
+ import {getStatusColor, getAppName, formatTime, getSize} from '../../helpers';
22
+ import {getTelemetryConsentStatus} from '../../helpers/telemetry';
23
+ import {UpdateAvailableModal} from './UpdateAvailableModal';
24
+ import {
25
+ WhiteBackNavigation,
26
+ TrashIcon,
27
+ SettingsIcon,
28
+ CloseWhite,
29
+ ClockIcon,
30
+ SizeIcon,
31
+ AppleIcon,
32
+ AndroidIcon,
33
+ NpmIcon,
34
+ ResetIcon,
35
+ BoltIcon,
36
+ } from '../NetworkIcons';
37
+
38
+ const InspectorHeader = React.memo(() => {
39
+ const {
40
+ modalHeightPercent,
41
+ appIcon,
42
+ selected,
43
+ setSelected,
44
+ selectedEvent,
45
+ setSelectedEvent,
46
+ selectedLog,
47
+ setSelectedLog,
48
+ selectedReduxSlice,
49
+ setSelectedReduxSlice,
50
+ selectedReduxAction,
51
+ setSelectedReduxAction,
52
+ reduxState,
53
+ reduxLastActionMap,
54
+ showHeaderInfo,
55
+ setShowHeaderInfo,
56
+ updateAvailable,
57
+ latestNpmVersion,
58
+ clearAnim,
59
+ activePulseAnim,
60
+ unreadPulseAnim,
61
+ runClearAllWithAnimation,
62
+ settingsPage,
63
+ setSettingsPage,
64
+ resetToDefaults,
65
+ closeModal,
66
+ detailTitle,
67
+ activeTab,
68
+ environment,
69
+ visible,
70
+ selectedCrash,
71
+ setSelectedCrash,
72
+ } = useInspector();
73
+
74
+ const [showUpdateModal, setShowUpdateModal] = React.useState<boolean>(false);
75
+ const [isTelemetryActive, setIsTelemetryActive] =
76
+ React.useState<boolean>(false);
77
+ const telemetryPulseAnim = React.useRef(new Animated.Value(1)).current;
78
+
79
+ React.useEffect(() => {
80
+ getTelemetryConsentStatus().then(status => {
81
+ setIsTelemetryActive(status === 'granted');
82
+ });
83
+ }, [visible]);
84
+
85
+ React.useEffect(() => {
86
+ if (!isTelemetryActive) return;
87
+ const pulse = Animated.loop(
88
+ Animated.sequence([
89
+ Animated.timing(telemetryPulseAnim, {
90
+ toValue: 2.2,
91
+ duration: 1500,
92
+ useNativeDriver: true,
93
+ }),
94
+ Animated.timing(telemetryPulseAnim, {
95
+ toValue: 1,
96
+ duration: 0,
97
+ useNativeDriver: true,
98
+ }),
99
+ ]),
100
+ );
101
+ pulse.start();
102
+ return () => pulse.stop();
103
+ }, [isTelemetryActive, telemetryPulseAnim]);
104
+
105
+ const envConfig = useMemo(() => {
106
+ const rawEnv = (environment || (__DEV__ ? 'DEV' : 'PROD')).trim();
107
+ const clean = rawEnv.toUpperCase();
108
+
109
+ if (clean === 'DEV' || clean.includes('DEV') || clean === 'LOCAL') {
110
+ return {
111
+ label: rawEnv,
112
+ bg: `${AppColors.emerald500}40`, // emerald
113
+ border: `${AppColors.emerald400}8C`,
114
+ text: AppColors.mintGreenBorder,
115
+ };
116
+ }
117
+ if (clean === 'UAT' || clean === 'QA' || clean === 'TEST') {
118
+ return {
119
+ label: rawEnv,
120
+ bg: `${AppColors.amber500}47`, // amber
121
+ border: `${AppColors.amber400}99`,
122
+ text: AppColors.amberWarmBorder,
123
+ };
124
+ }
125
+ if (clean === 'PREPROD' || clean === 'STAGE' || clean === 'STAGING') {
126
+ return {
127
+ label: rawEnv,
128
+ bg: `${AppColors.purple500}47`, // purple
129
+ border: `${AppColors.purple400}99`,
130
+ text: AppColors.violetSoftBorder,
131
+ };
132
+ }
133
+ // PROD / Live
134
+ return {
135
+ label: rawEnv,
136
+ bg: `${AppColors.rose500}40`, // rose
137
+ border: `${AppColors.roseBorder}8C`,
138
+ text: AppColors.errorBorder,
139
+ };
140
+ }, [environment]);
141
+
142
+ const isDetailView =
143
+ (activeTab === 'apis' && selected != null) ||
144
+ (activeTab === 'analytics' && selectedEvent != null) ||
145
+ (activeTab === 'logs' && selectedLog != null) ||
146
+ (activeTab === 'redux' &&
147
+ (selectedReduxSlice != null || selectedReduxAction != null)) ||
148
+ (activeTab === 'crash' && selectedCrash != null);
149
+
150
+ const isSettingsView = settingsPage !== null;
151
+ const isAnySelected = isDetailView || isSettingsView;
152
+
153
+ const settingsModuleTitle = useMemo(() => {
154
+ switch (settingsPage) {
155
+ case 'apis':
156
+ return 'APIs (Network)';
157
+ case 'logs':
158
+ return 'Console Logs';
159
+ case 'performance':
160
+ return 'Performance Tracker';
161
+ case 'bundle':
162
+ return 'Bundle Analyzer';
163
+ case 'crash':
164
+ return 'Crash Protection';
165
+ case 'analytics':
166
+ return 'Analytics Logger';
167
+ case 'redux':
168
+ return 'Redux Inspector';
169
+ default:
170
+ return 'Settings & Modules';
171
+ }
172
+ }, [settingsPage]);
173
+
174
+ const headerTopPadding =
175
+ Platform.OS === 'ios' && modalHeightPercent >= 95 ? 44 : 0;
176
+
177
+ return (
178
+ <>
179
+ <LinearGradient
180
+ colors={['#4F46E5', '#7C3AED']}
181
+ start={{x: 0, y: 0}}
182
+ end={{x: 1, y: 1}}
183
+ style={styles.headerGradient}>
184
+ <View style={{paddingTop: headerTopPadding, width: '100%'}}>
185
+ <View style={styles.header}>
186
+ <View
187
+ style={[
188
+ styles.headerLeft,
189
+ {
190
+ flexDirection: 'row',
191
+ alignItems: 'center',
192
+ gap: 8,
193
+ flex: !isDetailView ? 1 : 0,
194
+ minWidth: 0,
195
+ },
196
+ ]}>
197
+ <TouchableScale
198
+ onPress={() => {
199
+ if (isSettingsView) {
200
+ if (settingsPage === 'main') {
201
+ setSettingsPage(null);
202
+ } else {
203
+ setSettingsPage('main');
204
+ }
205
+ return;
206
+ }
207
+ requestAnimationFrame(() => {
208
+ setSelected(null);
209
+ setSelectedEvent(null);
210
+ setSelectedLog(null);
211
+ setSelectedReduxSlice(null);
212
+ setSelectedReduxAction(null);
213
+ setSelectedCrash(null);
214
+ });
215
+ }}
216
+ hitSlop={15}
217
+ style={[
218
+ {
219
+ width: 38,
220
+ height: 38,
221
+ borderRadius: 19,
222
+ alignItems: 'center',
223
+ justifyContent: 'center',
224
+ backgroundColor: `${AppColors.white}2E`,
225
+ borderWidth: 1,
226
+ borderColor: `${AppColors.white}4D`,
227
+ },
228
+ !isAnySelected && {display: 'none'},
229
+ ]}>
230
+ {/* Soft outer glow to fake a blurred circle */}
231
+ <View
232
+ style={{
233
+ position: 'absolute',
234
+ width: 48,
235
+ height: 48,
236
+ borderRadius: 24,
237
+ backgroundColor: `${AppColors.white}1A`,
238
+ }}
239
+ />
240
+ <WhiteBackNavigation />
241
+ </TouchableScale>
242
+
243
+ {isSettingsView ? (
244
+ <View style={{gap: 2, flex: 1, minWidth: 0}}>
245
+ <View
246
+ style={{flexDirection: 'row', alignItems: 'center', gap: 6}}>
247
+ <Text
248
+ style={{
249
+ fontFamily: AppFonts.interBold,
250
+ fontSize: 16,
251
+ color: AppColors.white,
252
+ letterSpacing: -0.2,
253
+ }}
254
+ numberOfLines={1}>
255
+ {settingsModuleTitle}
256
+ </Text>
257
+ {settingsPage === 'main' && (
258
+ <View
259
+ style={{
260
+ backgroundColor: `${AppColors.white}26`,
261
+ paddingHorizontal: 6,
262
+ paddingVertical: 1.5,
263
+ borderRadius: 10,
264
+ borderWidth: 1,
265
+ borderColor: `${AppColors.white}20`,
266
+ }}>
267
+ <Text
268
+ style={{
269
+ fontFamily: AppFonts.interBold,
270
+ fontSize: 9,
271
+ color: AppColors.white,
272
+ }}>
273
+ v{LIB_VERSION}
274
+ </Text>
275
+ </View>
276
+ )}
277
+ </View>
278
+ <Text
279
+ style={{
280
+ fontFamily: AppFonts.interRegular,
281
+ fontSize: 10.5,
282
+ color: `${AppColors.white}CC`,
283
+ }}
284
+ numberOfLines={1}>
285
+ {settingsPage === 'main'
286
+ ? 'Manage modules and preferences'
287
+ : 'Configure module parameters'}
288
+ </Text>
289
+ </View>
290
+ ) : !isAnySelected ? (
291
+ <View
292
+ style={{
293
+ flexDirection: 'row',
294
+ alignItems: 'center',
295
+ gap: 8,
296
+ flex: 1,
297
+ minWidth: 0,
298
+ marginRight: 6,
299
+ }}>
300
+ <AppHeaderLogo size={46} customIcon={appIcon} />
301
+ <View style={{gap: 2.5, flex: 1, minWidth: 0}}>
302
+ <View
303
+ style={{
304
+ flexDirection: 'row',
305
+ alignItems: 'center',
306
+ gap: 6,
307
+ minWidth: 0,
308
+ }}>
309
+ <Text
310
+ style={[styles.headerTitle, {flexShrink: 1}]}
311
+ numberOfLines={1}
312
+ ellipsizeMode="tail">
313
+ {getAppName()}
314
+ </Text>
315
+ <View
316
+ style={[
317
+ styles.envBadge,
318
+ {
319
+ backgroundColor: envConfig.bg,
320
+ borderColor: envConfig.border,
321
+ flexShrink: 0,
322
+ paddingVertical: 1.5,
323
+ marginBottom: 0,
324
+ },
325
+ ]}>
326
+ <Text
327
+ style={[styles.envBadgeText, {color: envConfig.text}]}>
328
+ {envConfig.label}
329
+ </Text>
330
+ </View>
331
+ {updateAvailable && (
332
+ <Pressable
333
+ hitSlop={10}
334
+ onPress={() => setShowUpdateModal(true)}
335
+ style={{
336
+ flexDirection: 'row',
337
+ alignItems: 'center',
338
+ backgroundColor: '#F59E0B',
339
+ borderRadius: 5,
340
+ paddingHorizontal: 5.5,
341
+ paddingVertical: 1.5,
342
+ gap: 3.5,
343
+ shadowColor: '#F59E0B',
344
+ shadowOffset: {width: 0, height: 1.5},
345
+ shadowOpacity: 0.35,
346
+ shadowRadius: 3,
347
+ elevation: 3,
348
+ flexShrink: 0,
349
+ }}>
350
+ <Animated.View
351
+ style={{
352
+ width: 5,
353
+ height: 5,
354
+ borderRadius: 2.5,
355
+ backgroundColor: '#FFFFFF',
356
+ opacity: activePulseAnim,
357
+ transform: [{scale: unreadPulseAnim}],
358
+ }}
359
+ />
360
+ <Text
361
+ style={{
362
+ fontFamily: AppFonts.interBold,
363
+ fontSize: 9,
364
+ color: '#FFFFFF',
365
+ letterSpacing: 0.3,
366
+ }}>
367
+ UPDATE
368
+ </Text>
369
+ <BoltIcon size={9} color="#FFFFFF" />
370
+ </Pressable>
371
+ )}
372
+ </View>
373
+
374
+ {/* OS & NPM Version Representation */}
375
+ <View
376
+ style={{
377
+ flexDirection: 'row',
378
+ alignItems: 'center',
379
+ gap: 5,
380
+ minWidth: 0,
381
+ }}>
382
+ <View
383
+ style={{
384
+ flexDirection: 'row',
385
+ alignItems: 'center',
386
+ backgroundColor: `${AppColors.white}1F`,
387
+ borderRadius: 5,
388
+ paddingHorizontal: 6,
389
+ paddingVertical: 2,
390
+ gap: 4,
391
+ borderWidth: 1,
392
+ borderColor: `${AppColors.white}2E`,
393
+ flexShrink: 0,
394
+ }}>
395
+ {Platform.OS === 'ios' ? (
396
+ <AppleIcon color={`${AppColors.white}E6`} size={10} />
397
+ ) : (
398
+ <AndroidIcon color={`${AppColors.white}E6`} size={10} />
399
+ )}
400
+ <Text
401
+ style={{
402
+ fontFamily: AppFonts.interMedium,
403
+ fontSize: 9.5,
404
+ color: `${AppColors.white}EB`,
405
+ letterSpacing: 0.1,
406
+ }}
407
+ numberOfLines={1}>
408
+ {Platform.OS === 'ios'
409
+ ? `iOS ${Platform.Version}`
410
+ : `Android ${Platform.Version}`}
411
+ </Text>
412
+ </View>
413
+
414
+ <Pressable
415
+ onPress={() => {
416
+ if (updateAvailable) {
417
+ setShowUpdateModal(true);
418
+ } else {
419
+ Linking.openURL(
420
+ 'https://www.npmjs.com/package/react-native-inapp-inspector',
421
+ ).catch(() => {});
422
+ }
423
+ }}
424
+ style={{
425
+ flexDirection: 'row',
426
+ alignItems: 'center',
427
+ backgroundColor: `${AppColors.white}1F`,
428
+ borderRadius: 5,
429
+ paddingHorizontal: 6,
430
+ paddingVertical: 2,
431
+ gap: 4,
432
+ borderWidth: 1,
433
+ borderColor: `${AppColors.white}2E`,
434
+ flexShrink: 0,
435
+ }}>
436
+ <NpmIcon size={10} color="#FF6B6B" />
437
+ <Text
438
+ style={{
439
+ fontFamily: AppFonts.interMedium,
440
+ fontSize: 9.5,
441
+ color: `${AppColors.white}EB`,
442
+ letterSpacing: 0.1,
443
+ }}
444
+ numberOfLines={1}>
445
+ v{LIB_VERSION}
446
+ </Text>
447
+ {updateAvailable && (
448
+ <Text
449
+ style={{
450
+ fontFamily: AppFonts.interBold,
451
+ fontSize: 9,
452
+ color: '#F59E0B',
453
+ }}>
454
+
455
+ </Text>
456
+ )}
457
+ </Pressable>
458
+
459
+ {isTelemetryActive && (
460
+ <Pressable
461
+ onPress={() =>
462
+ Alert.alert(
463
+ 'Anonymous Telemetry Active',
464
+ 'Anonymous diagnostics (such as React Native version, JavaScript engine, and device model) are currently enabled to help improve library tooling.\n\nNo user data or network payloads are captured. You can toggle this anytime in Settings.',
465
+ [{text: 'Got it'}],
466
+ )
467
+ }
468
+ style={{
469
+ flexDirection: 'row',
470
+ alignItems: 'center',
471
+ backgroundColor: 'rgba(16, 185, 129, 0.22)',
472
+ borderRadius: 5,
473
+ paddingHorizontal: 5.5,
474
+ paddingVertical: 2,
475
+ gap: 4.5,
476
+ borderWidth: 1,
477
+ borderColor: 'rgba(52, 211, 153, 0.45)',
478
+ flexShrink: 0,
479
+ }}>
480
+ <View
481
+ style={{
482
+ width: 6,
483
+ height: 6,
484
+ alignItems: 'center',
485
+ justifyContent: 'center',
486
+ }}>
487
+ <Animated.View
488
+ style={{
489
+ position: 'absolute',
490
+ width: 6,
491
+ height: 6,
492
+ borderRadius: 3,
493
+ backgroundColor: '#34D399',
494
+ opacity: telemetryPulseAnim.interpolate({
495
+ inputRange: [1, 2.2],
496
+ outputRange: [0.8, 0],
497
+ }),
498
+ transform: [{scale: telemetryPulseAnim}],
499
+ }}
500
+ />
501
+ <View
502
+ style={{
503
+ width: 4,
504
+ height: 4,
505
+ borderRadius: 2,
506
+ backgroundColor: '#10B981',
507
+ }}
508
+ />
509
+ </View>
510
+ <Text
511
+ style={{
512
+ fontFamily: AppFonts.interBold,
513
+ fontSize: 9,
514
+ color: '#6EE7B7',
515
+ letterSpacing: 0.2,
516
+ }}>
517
+ LIVE
518
+ </Text>
519
+ </Pressable>
520
+ )}
521
+ </View>
522
+ </View>
523
+ </View>
524
+ ) : null}
525
+ </View>
526
+
527
+ {isDetailView && (
528
+ <View style={styles.headerCenter}>
529
+ {activeTab === 'apis' && selected != null ? (
530
+ <View style={styles.headerDetailCenter}>
531
+ <View style={styles.headerDetailRow}>
532
+ <View
533
+ style={[
534
+ styles.headerMethodBadge,
535
+ {
536
+ backgroundColor:
537
+ METHOD_COLORS[selected.method as Method] ??
538
+ AppColors.grayText,
539
+ },
540
+ ]}>
541
+ <Text style={styles.headerMethodText}>
542
+ {selected.method}
543
+ </Text>
544
+ </View>
545
+ <Text
546
+ style={styles.headerDetailTitle}
547
+ numberOfLines={1}
548
+ ellipsizeMode="middle">
549
+ {detailTitle}
550
+ </Text>
551
+ </View>
552
+ <View style={styles.headerDetailSubRow}>
553
+ <View
554
+ style={{
555
+ flexDirection: 'row',
556
+ alignItems: 'center',
557
+ gap: 5,
558
+ paddingHorizontal: 8,
559
+ paddingVertical: 3,
560
+ borderRadius: 20,
561
+ backgroundColor: `${getStatusColor(selected.status)}26`,
562
+ borderWidth: 1,
563
+ borderColor: `${getStatusColor(selected.status)}55`,
564
+ }}>
565
+ <View
566
+ style={[
567
+ styles.headerStatusDot,
568
+ {
569
+ backgroundColor: getStatusColor(selected.status),
570
+ },
571
+ ]}
572
+ />
573
+ <Text
574
+ style={[
575
+ styles.headerSubTitle,
576
+ {fontFamily: AppFonts.interBold},
577
+ ]}>
578
+ {selected.status === 0
579
+ ? 'Failed'
580
+ : selected.status ?? 'Pending'}
581
+ </Text>
582
+ </View>
583
+ <View
584
+ style={{
585
+ flexDirection: 'row',
586
+ alignItems: 'center',
587
+ gap: 4,
588
+ paddingHorizontal: 8,
589
+ paddingVertical: 3,
590
+ borderRadius: 20,
591
+ backgroundColor: `${AppColors.white}29`,
592
+ }}>
593
+ <ClockIcon color={AppColors.white} size={11} />
594
+ <Text style={styles.headerSubTitle}>
595
+ {selected.duration != null
596
+ ? `${selected.duration}ms`
597
+ : '—'}
598
+ </Text>
599
+ </View>
600
+ {selected.response != null && (
601
+ <View
602
+ style={{
603
+ flexDirection: 'row',
604
+ alignItems: 'center',
605
+ gap: 4,
606
+ paddingHorizontal: 8,
607
+ paddingVertical: 3,
608
+ borderRadius: 20,
609
+ backgroundColor: `${AppColors.white}29`,
610
+ }}>
611
+ <SizeIcon color={AppColors.white} size={11} />
612
+ <Text style={styles.headerSubTitle}>
613
+ {getSize(selected.response)}
614
+ </Text>
615
+ </View>
616
+ )}
617
+ </View>
618
+ </View>
619
+ ) : activeTab === 'analytics' && selectedEvent != null ? (
620
+ <View style={styles.headerDetailCenter}>
621
+ <View style={styles.headerDetailRow}>
622
+ <View
623
+ style={[
624
+ styles.headerMethodBadge,
625
+ {
626
+ backgroundColor:
627
+ selectedEvent.source === 'firebase'
628
+ ? `${AppColors.firebaseOrange}4D`
629
+ : `${AppColors.purple}4D`,
630
+ },
631
+ ]}>
632
+ <Text style={styles.headerMethodText}>
633
+ {selectedEvent.source === 'firebase' ? 'FB' : 'MAN'}
634
+ </Text>
635
+ </View>
636
+ <Text
637
+ style={styles.headerDetailTitle}
638
+ numberOfLines={1}
639
+ ellipsizeMode="middle">
640
+ {selectedEvent.name}
641
+ </Text>
642
+ </View>
643
+ <View style={styles.headerDetailSubRow}>
644
+ <View
645
+ style={[
646
+ styles.headerStatusDot,
647
+ {
648
+ backgroundColor:
649
+ selectedEvent.source === 'firebase'
650
+ ? AppColors.firebaseOrange
651
+ : AppColors.purple,
652
+ },
653
+ ]}
654
+ />
655
+ <Text style={styles.headerSubTitle}>
656
+ {Object.keys(selectedEvent.params).length} param
657
+ {Object.keys(selectedEvent.params).length !== 1
658
+ ? 's'
659
+ : ''}
660
+ {' · '}
661
+ {selectedEvent.source}
662
+ </Text>
663
+ </View>
664
+ </View>
665
+ ) : activeTab === 'logs' && selectedLog != null ? (
666
+ <View style={styles.headerDetailCenter}>
667
+ <View style={styles.headerDetailRow}>
668
+ <View
669
+ style={[
670
+ styles.headerMethodBadge,
671
+ {
672
+ backgroundColor:
673
+ selectedLog.type === 'error'
674
+ ? `${AppColors.errorColor}4D`
675
+ : selectedLog.type === 'warn'
676
+ ? `${AppColors.lightOrange}4D`
677
+ : `${AppColors.purple}4D`,
678
+ },
679
+ ]}>
680
+ <Text style={styles.headerMethodText}>
681
+ {selectedLog.type.toUpperCase()}
682
+ </Text>
683
+ </View>
684
+ <Text
685
+ style={styles.headerDetailTitle}
686
+ numberOfLines={1}
687
+ ellipsizeMode="middle">
688
+ console.
689
+ {selectedLog.sourceMethod || selectedLog.type || 'log'}
690
+ </Text>
691
+ </View>
692
+ <View style={styles.headerDetailSubRow}>
693
+ <View
694
+ style={{
695
+ flexDirection: 'row',
696
+ alignItems: 'center',
697
+ gap: 4,
698
+ paddingHorizontal: 8,
699
+ paddingVertical: 3,
700
+ borderRadius: 20,
701
+ backgroundColor: `${AppColors.white}29`,
702
+ }}>
703
+ <ClockIcon color={AppColors.white} size={11} />
704
+ <Text style={styles.headerSubTitle}>
705
+ {formatTime(selectedLog.timestamp)}
706
+ </Text>
707
+ </View>
708
+ </View>
709
+ </View>
710
+ ) : activeTab === 'redux' && selectedReduxSlice != null ? (
711
+ (() => {
712
+ const sliceData = reduxState?.[selectedReduxSlice];
713
+ const keyCount =
714
+ sliceData && typeof sliceData === 'object'
715
+ ? Object.keys(sliceData).length
716
+ : typeof sliceData !== 'undefined'
717
+ ? 1
718
+ : 0;
719
+ const sliceSize = getSize(sliceData);
720
+ const lastAction = reduxLastActionMap[selectedReduxSlice];
721
+
722
+ return (
723
+ <View style={styles.headerDetailCenter}>
724
+ <View style={styles.headerDetailRow}>
725
+ <View
726
+ style={[
727
+ styles.headerMethodBadge,
728
+ {
729
+ backgroundColor: `${AppColors.purple}4D`,
730
+ },
731
+ ]}>
732
+ <Text style={styles.headerMethodText}>SLICE</Text>
733
+ </View>
734
+ <Text
735
+ style={styles.headerDetailTitle}
736
+ numberOfLines={1}
737
+ ellipsizeMode="middle">
738
+ {selectedReduxSlice}
739
+ </Text>
740
+ </View>
741
+ <View style={styles.headerDetailSubRow}>
742
+ <View
743
+ style={[
744
+ styles.headerStatusDot,
745
+ {backgroundColor: AppColors.liveGreen},
746
+ ]}
747
+ />
748
+ <Text style={styles.headerSubTitle}>Live</Text>
749
+ <Text style={[styles.headerSubTitle, {opacity: 0.6}]}>
750
+
751
+ </Text>
752
+ <Text style={styles.headerSubTitle}>
753
+ {keyCount} keys
754
+ </Text>
755
+ <Text style={[styles.headerSubTitle, {opacity: 0.6}]}>
756
+
757
+ </Text>
758
+ <Text style={styles.headerSubTitle}>{sliceSize}</Text>
759
+ {lastAction?.timestamp && (
760
+ <>
761
+ <Text
762
+ style={[styles.headerSubTitle, {opacity: 0.6}]}>
763
+
764
+ </Text>
765
+ <View
766
+ style={{
767
+ flexDirection: 'row',
768
+ alignItems: 'center',
769
+ gap: 3,
770
+ }}>
771
+ <ClockIcon color={AppColors.white} size={10} />
772
+ <Text style={styles.headerSubTitle}>
773
+ {lastAction.timestamp}
774
+ </Text>
775
+ </View>
776
+ </>
777
+ )}
778
+ </View>
779
+ </View>
780
+ );
781
+ })()
782
+ ) : activeTab === 'redux' && selectedReduxAction != null ? (
783
+ <View style={styles.headerDetailCenter}>
784
+ <View style={styles.headerDetailRow}>
785
+ <View
786
+ style={[
787
+ styles.headerMethodBadge,
788
+ {
789
+ backgroundColor: `${AppColors.brandPurple}4D`,
790
+ },
791
+ ]}>
792
+ <Text style={styles.headerMethodText}>ACTION</Text>
793
+ </View>
794
+ <Text
795
+ style={styles.headerDetailTitle}
796
+ numberOfLines={1}
797
+ ellipsizeMode="middle">
798
+ {selectedReduxAction.type}
799
+ </Text>
800
+ </View>
801
+ <View style={styles.headerDetailSubRow}>
802
+ <View
803
+ style={[
804
+ styles.headerStatusDot,
805
+ {backgroundColor: AppColors.purple},
806
+ ]}
807
+ />
808
+ <Text style={styles.headerSubTitle}>
809
+ {selectedReduxAction.timestamp || 'Dispatched'}
810
+ </Text>
811
+ </View>
812
+ </View>
813
+ ) : activeTab === 'crash' && selectedCrash != null ? (
814
+ <View style={styles.headerDetailCenter}>
815
+ <View style={styles.headerDetailRow}>
816
+ <View
817
+ style={[
818
+ styles.headerMethodBadge,
819
+ {
820
+ backgroundColor: selectedCrash.isFatal
821
+ ? AppColors.red600
822
+ : AppColors.amber600,
823
+ },
824
+ ]}>
825
+ <Text style={styles.headerMethodText}>
826
+ {selectedCrash.isFatal
827
+ ? 'FATAL'
828
+ : selectedCrash.type.toUpperCase()}
829
+ </Text>
830
+ </View>
831
+ <Text
832
+ style={styles.headerDetailTitle}
833
+ numberOfLines={1}
834
+ ellipsizeMode="middle">
835
+ {selectedCrash.name || selectedCrash.message}
836
+ </Text>
837
+ </View>
838
+ <View style={styles.headerDetailSubRow}>
839
+ <View
840
+ style={[
841
+ styles.headerStatusDot,
842
+ {
843
+ backgroundColor: selectedCrash.isFatal
844
+ ? AppColors.red600
845
+ : AppColors.amber500,
846
+ },
847
+ ]}
848
+ />
849
+ <Text style={styles.headerSubTitle}>
850
+ {selectedCrash.timeStr ||
851
+ new Date(selectedCrash.timestamp).toLocaleTimeString()}
852
+ </Text>
853
+ {selectedCrash.deviceInfo?.platform && (
854
+ <>
855
+ <Text style={[styles.headerSubTitle, {opacity: 0.6}]}>
856
+
857
+ </Text>
858
+ <Text style={styles.headerSubTitle}>
859
+ {selectedCrash.deviceInfo.platform.toUpperCase()}
860
+ </Text>
861
+ </>
862
+ )}
863
+ </View>
864
+ </View>
865
+ ) : null}
866
+ </View>
867
+ )}
868
+
869
+ <View
870
+ style={[
871
+ styles.headerRight,
872
+ {
873
+ flexDirection: 'row',
874
+ alignItems: 'center',
875
+ justifyContent: 'flex-end',
876
+ flexShrink: 0,
877
+ gap: 8,
878
+ },
879
+ ]}>
880
+ {isSettingsView && (
881
+ <TouchableScale
882
+ onPress={() => {
883
+ Alert.alert(
884
+ 'Reset All Settings',
885
+ 'This restores all module visibility and UI preferences to defaults. Continue?',
886
+ [
887
+ {text: 'Cancel', style: 'cancel'},
888
+ {
889
+ text: 'Reset',
890
+ style: 'destructive',
891
+ onPress: resetToDefaults,
892
+ },
893
+ ],
894
+ );
895
+ }}
896
+ hitSlop={15}
897
+ style={styles.closeButtonSquare}>
898
+ <ResetIcon color={AppColors.white} size={14} />
899
+ </TouchableScale>
900
+ )}
901
+
902
+ {!isAnySelected && (
903
+ <TouchableScale
904
+ onPress={() => {
905
+ Alert.alert(
906
+ 'Clear Everything',
907
+ 'This clears all tabs — APIs, Logs, Analytics, Redux timeline and Crash history. Continue?',
908
+ [
909
+ {text: 'Cancel', style: 'cancel'},
910
+ {
911
+ text: 'Clear All',
912
+ onPress: runClearAllWithAnimation,
913
+ style: 'destructive',
914
+ },
915
+ ],
916
+ );
917
+ }}
918
+ hitSlop={15}
919
+ style={styles.closeButtonSquare}>
920
+ <Animated.View
921
+ style={{
922
+ transform: [
923
+ {
924
+ rotate: clearAnim.interpolate({
925
+ inputRange: [0, 1],
926
+ outputRange: ['0deg', '-25deg'],
927
+ }),
928
+ },
929
+ {
930
+ scale: clearAnim.interpolate({
931
+ inputRange: [0, 0.5, 1],
932
+ outputRange: [1, 1.25, 1],
933
+ }),
934
+ },
935
+ ],
936
+ }}>
937
+ <TrashIcon color={AppColors.white} size={14} />
938
+ </Animated.View>
939
+ </TouchableScale>
940
+ )}
941
+
942
+ {!isAnySelected && (
943
+ <TouchableScale
944
+ onPress={() => setSettingsPage('main')}
945
+ hitSlop={15}
946
+ style={styles.closeButtonSquare}>
947
+ <SettingsIcon color={AppColors.white} size={14} />
948
+ </TouchableScale>
949
+ )}
950
+
951
+ <TouchableScale
952
+ onPress={closeModal}
953
+ hitSlop={15}
954
+ style={styles.closeButtonSquare}>
955
+ <CloseWhite size={14} />
956
+ </TouchableScale>
957
+ </View>
958
+ </View>
959
+ </View>
960
+ </LinearGradient>
961
+
962
+ {/* Dedicated Update Available Details Modal */}
963
+ <UpdateAvailableModal
964
+ visible={showUpdateModal}
965
+ latestVersion={latestNpmVersion}
966
+ onClose={() => setShowUpdateModal(false)}
967
+ />
968
+ </>
969
+ );
970
+ });
971
+
972
+ export default InspectorHeader;