react-native-inapp-inspector 1.1.24 → 1.1.26

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 (114) hide show
  1. package/android/build.gradle +36 -0
  2. package/android/src/main/AndroidManifest.xml +3 -0
  3. package/android/src/main/java/com/inappinspector/NetworkInspectorModule.java +114 -0
  4. package/android/src/main/java/com/inappinspector/NetworkInspectorPackage.java +28 -0
  5. package/dist/commonjs/components/AnalyticsEventCard.js +94 -88
  6. package/dist/commonjs/components/ConsoleLogCard.js +49 -25
  7. package/dist/commonjs/components/ErrorBoundary.d.ts +1 -1
  8. package/dist/commonjs/components/ErrorBoundary.js +9 -145
  9. package/dist/commonjs/components/Inspector/AnalyticsFilterModal.d.ts +7 -0
  10. package/dist/commonjs/components/Inspector/AnalyticsFilterModal.js +1002 -0
  11. package/dist/commonjs/components/Inspector/AnalyticsTab.js +637 -10
  12. package/dist/commonjs/components/Inspector/BundleTab.d.ts +1 -1
  13. package/dist/commonjs/components/Inspector/BundleTab.js +826 -406
  14. package/dist/commonjs/components/Inspector/CrashDetail.d.ts +3 -0
  15. package/dist/commonjs/components/Inspector/CrashDetail.js +796 -0
  16. package/dist/commonjs/components/Inspector/CrashFilterModal.d.ts +20 -0
  17. package/dist/commonjs/components/Inspector/CrashFilterModal.js +597 -0
  18. package/dist/commonjs/components/Inspector/CrashTab.d.ts +3 -0
  19. package/dist/commonjs/components/Inspector/CrashTab.js +727 -0
  20. package/dist/commonjs/components/Inspector/InspectorContext.js +6 -1
  21. package/dist/commonjs/components/Inspector/InspectorHeader.js +65 -28
  22. package/dist/commonjs/components/Inspector/LogDetail.js +227 -114
  23. package/dist/commonjs/components/Inspector/MainScreen.js +13 -4
  24. package/dist/commonjs/components/Inspector/NetworkDetail.js +131 -81
  25. package/dist/commonjs/components/Inspector/PerformanceTab.js +20 -10
  26. package/dist/commonjs/components/Inspector/SettingsPanel.js +90 -1
  27. package/dist/commonjs/components/Inspector/TabBar.js +15 -3
  28. package/dist/commonjs/components/NetworkIcons.d.ts +25 -0
  29. package/dist/commonjs/components/NetworkIcons.js +188 -1
  30. package/dist/commonjs/components/SegmentedTabs.js +33 -23
  31. package/dist/commonjs/components/TreeNode.js +13 -12
  32. package/dist/commonjs/constants/version.d.ts +1 -1
  33. package/dist/commonjs/constants/version.js +1 -1
  34. package/dist/commonjs/customHooks/analyticsLogger.js +70 -44
  35. package/dist/commonjs/customHooks/bundleAnalyzer.d.ts +5 -1
  36. package/dist/commonjs/customHooks/bundleAnalyzer.js +497 -126
  37. package/dist/commonjs/customHooks/consoleLogger.d.ts +1 -0
  38. package/dist/commonjs/customHooks/consoleLogger.js +153 -51
  39. package/dist/commonjs/customHooks/crashHandler.d.ts +64 -0
  40. package/dist/commonjs/customHooks/crashHandler.js +681 -0
  41. package/dist/commonjs/customHooks/networkLogger.js +74 -49
  42. package/dist/commonjs/customHooks/performanceTracker.d.ts +8 -0
  43. package/dist/commonjs/customHooks/performanceTracker.js +44 -3
  44. package/dist/commonjs/decorators/index.d.ts +49 -0
  45. package/dist/commonjs/decorators/index.js +142 -0
  46. package/dist/commonjs/helpers/gaAnalyticsRegistry.d.ts +18 -0
  47. package/dist/commonjs/helpers/gaAnalyticsRegistry.js +174 -0
  48. package/dist/commonjs/helpers/index.d.ts +7 -4
  49. package/dist/commonjs/helpers/index.js +102 -64
  50. package/dist/commonjs/i18n/locales/en.json +182 -4
  51. package/dist/commonjs/index.d.ts +6 -0
  52. package/dist/commonjs/index.js +310 -8
  53. package/dist/commonjs/styles/index.d.ts +4 -1
  54. package/dist/commonjs/styles/index.js +22 -19
  55. package/dist/commonjs/types/enums.d.ts +156 -9
  56. package/dist/commonjs/types/enums.js +139 -2
  57. package/dist/commonjs/types/interfaces.d.ts +70 -1
  58. package/dist/esm/components/AnalyticsEventCard.js +95 -89
  59. package/dist/esm/components/ConsoleLogCard.js +49 -25
  60. package/dist/esm/components/ErrorBoundary.d.ts +1 -1
  61. package/dist/esm/components/ErrorBoundary.js +10 -113
  62. package/dist/esm/components/Inspector/AnalyticsFilterModal.d.ts +7 -0
  63. package/dist/esm/components/Inspector/AnalyticsFilterModal.js +964 -0
  64. package/dist/esm/components/Inspector/AnalyticsTab.js +639 -12
  65. package/dist/esm/components/Inspector/BundleTab.d.ts +1 -1
  66. package/dist/esm/components/Inspector/BundleTab.js +827 -407
  67. package/dist/esm/components/Inspector/CrashDetail.d.ts +3 -0
  68. package/dist/esm/components/Inspector/CrashDetail.js +758 -0
  69. package/dist/esm/components/Inspector/CrashFilterModal.d.ts +20 -0
  70. package/dist/esm/components/Inspector/CrashFilterModal.js +557 -0
  71. package/dist/esm/components/Inspector/CrashTab.d.ts +3 -0
  72. package/dist/esm/components/Inspector/CrashTab.js +689 -0
  73. package/dist/esm/components/Inspector/InspectorContext.js +1 -0
  74. package/dist/esm/components/Inspector/InspectorHeader.js +66 -29
  75. package/dist/esm/components/Inspector/LogDetail.js +227 -114
  76. package/dist/esm/components/Inspector/MainScreen.js +14 -5
  77. package/dist/esm/components/Inspector/NetworkDetail.js +132 -49
  78. package/dist/esm/components/Inspector/PerformanceTab.js +20 -10
  79. package/dist/esm/components/Inspector/SettingsPanel.js +91 -2
  80. package/dist/esm/components/Inspector/TabBar.js +16 -4
  81. package/dist/esm/components/NetworkIcons.d.ts +25 -0
  82. package/dist/esm/components/NetworkIcons.js +161 -0
  83. package/dist/esm/components/SegmentedTabs.js +33 -23
  84. package/dist/esm/components/TreeNode.js +13 -12
  85. package/dist/esm/constants/version.d.ts +1 -1
  86. package/dist/esm/constants/version.js +1 -1
  87. package/dist/esm/customHooks/analyticsLogger.js +70 -44
  88. package/dist/esm/customHooks/bundleAnalyzer.d.ts +5 -1
  89. package/dist/esm/customHooks/bundleAnalyzer.js +493 -125
  90. package/dist/esm/customHooks/consoleLogger.d.ts +1 -0
  91. package/dist/esm/customHooks/consoleLogger.js +151 -50
  92. package/dist/esm/customHooks/crashHandler.d.ts +64 -0
  93. package/dist/esm/customHooks/crashHandler.js +659 -0
  94. package/dist/esm/customHooks/networkLogger.js +74 -49
  95. package/dist/esm/customHooks/performanceTracker.d.ts +8 -0
  96. package/dist/esm/customHooks/performanceTracker.js +38 -1
  97. package/dist/esm/decorators/index.d.ts +49 -0
  98. package/dist/esm/decorators/index.js +137 -0
  99. package/dist/esm/helpers/gaAnalyticsRegistry.d.ts +18 -0
  100. package/dist/esm/helpers/gaAnalyticsRegistry.js +169 -0
  101. package/dist/esm/helpers/index.d.ts +7 -4
  102. package/dist/esm/helpers/index.js +100 -63
  103. package/dist/esm/i18n/locales/en.json +182 -4
  104. package/dist/esm/index.d.ts +6 -0
  105. package/dist/esm/index.js +262 -8
  106. package/dist/esm/styles/index.d.ts +4 -1
  107. package/dist/esm/styles/index.js +22 -19
  108. package/dist/esm/types/enums.d.ts +156 -9
  109. package/dist/esm/types/enums.js +138 -3
  110. package/dist/esm/types/interfaces.d.ts +70 -1
  111. package/ios/NetworkInspectorModule.h +6 -0
  112. package/ios/NetworkInspectorModule.m +125 -0
  113. package/package.json +5 -2
  114. package/react-native-inapp-inspector.podspec +18 -0
@@ -1,5 +1,5 @@
1
1
  import React, { useState, useEffect } from 'react';
2
- import { ActivityIndicator, Modal, Pressable, StatusBar, StyleSheet, Text, View, } from 'react-native';
2
+ import { ActivityIndicator, Modal, Platform, Pressable, StatusBar, StyleSheet, Text, View, } from 'react-native';
3
3
  import { useInspector } from './InspectorContext';
4
4
  import ErrorBoundary from '../ErrorBoundary';
5
5
  import FabLauncher from './FabLauncher';
@@ -15,12 +15,14 @@ import ReduxTab from './ReduxTab';
15
15
  import ReduxDetail from './ReduxDetail';
16
16
  import BundleTab from './BundleTab';
17
17
  import PerformanceTab from './PerformanceTab';
18
+ import CrashTab from './CrashTab';
19
+ import CrashDetail from './CrashDetail';
18
20
  import SettingsPanel from './SettingsPanel';
19
21
  import styles from '../../styles';
20
22
  import { AppColors } from '../../styles/AppColors';
21
23
  import NavigationTracker from './NavigationTracker';
22
24
  const MainScreen = () => {
23
- const { visible, modalAnimationType, closeModal, modalHeightPercent, selected, selectedEvent, selectedLog, selectedReduxSlice, selectedReduxAction, settingsPage, activeTab, isReady, isEnabled, hasNavigationContext, setNavState, } = useInspector();
25
+ const { visible, modalAnimationType, closeModal, modalHeightPercent, selected, selectedEvent, selectedLog, selectedReduxSlice, selectedReduxAction, selectedCrash, settingsPage, activeTab, isReady, isEnabled, hasNavigationContext, setNavState, } = useInspector();
24
26
  // Lazy-mount each tab on first visit and cache it in memory for 0ms instantaneous switching
25
27
  const [mountedTabs, setMountedTabs] = useState({
26
28
  [activeTab]: true,
@@ -35,9 +37,10 @@ const MainScreen = () => {
35
37
  const isDetailActive = (activeTab === 'apis' && selected != null) ||
36
38
  (activeTab === 'analytics' && selectedEvent != null) ||
37
39
  (activeTab === 'logs' && selectedLog != null) ||
38
- (activeTab === 'redux' && (selectedReduxSlice != null || selectedReduxAction != null));
40
+ (activeTab === 'redux' && (selectedReduxSlice != null || selectedReduxAction != null)) ||
41
+ (activeTab === 'crash' && selectedCrash != null);
39
42
  return (<>
40
- {isEnabled && <FabLauncher />}
43
+ {(Platform.OS === 'ios' || Platform.OS === 'android') && isEnabled && <FabLauncher />}
41
44
  <Modal visible={visible} animationType={modalAnimationType} transparent>
42
45
  {visible && (<ErrorBoundary onClose={closeModal}>
43
46
  <View style={styles.modalBackdrop}>
@@ -53,7 +56,7 @@ const MainScreen = () => {
53
56
  {/* ─── Horizontal Scrollable Tab Bar inside Content ─── */}
54
57
  {!isDetailActive && settingsPage === null ? (<TabBar />) : null}
55
58
 
56
- {isReady ? (isDetailActive ? (activeTab === 'apis' && selected != null ? (<NetworkDetail />) : activeTab === 'analytics' && selectedEvent != null ? (<AnalyticsDetail event={selectedEvent}/>) : activeTab === 'logs' && selectedLog != null ? (<LogDetail />) : activeTab === 'redux' ? (<ReduxDetail />) : null) : (<>
59
+ {isReady ? (isDetailActive ? (activeTab === 'apis' && selected != null ? (<NetworkDetail />) : activeTab === 'analytics' && selectedEvent != null ? (<AnalyticsDetail event={selectedEvent}/>) : activeTab === 'logs' && selectedLog != null ? (<LogDetail />) : activeTab === 'redux' ? (<ReduxDetail />) : activeTab === 'crash' && selectedCrash != null ? (<CrashDetail />) : null) : (<>
57
60
  <View style={{
58
61
  flex: 1,
59
62
  display: activeTab === 'apis' ? 'flex' : 'none',
@@ -90,6 +93,12 @@ const MainScreen = () => {
90
93
  }}>
91
94
  {mountedTabs.performance && <PerformanceTab />}
92
95
  </View>
96
+ <View style={{
97
+ flex: 1,
98
+ display: activeTab === 'crash' ? 'flex' : 'none',
99
+ }}>
100
+ {mountedTabs.crash && <CrashTab />}
101
+ </View>
93
102
  </>)) : (<View style={styles.empty}>
94
103
  <ActivityIndicator size="large" color={AppColors.purple}/>
95
104
  <Text style={[styles.emptySub, { marginTop: 12 }]}>
@@ -1,7 +1,6 @@
1
1
  import React from 'react';
2
2
  import { useTranslation } from '../../i18n';
3
3
  import { Image, Linking, Pressable, ScrollView, Text, TextInput, View, } from 'react-native';
4
- import Svg, { Circle } from 'react-native-svg';
5
4
  import { animateNextLayout, useInspector } from './InspectorContext';
6
5
  import TouchableScale from '../TouchableScale';
7
6
  import SegmentedTabs from '../SegmentedTabs';
@@ -17,7 +16,7 @@ import { AppColors } from '../../styles/AppColors';
17
16
  import { AppFonts } from '../../styles/AppFonts';
18
17
  import { METHOD_COLORS } from '../../constants';
19
18
  import { getStatusColor, getSize, formatDateTime, getFetchCommand, getCurlCommand, } from '../../helpers';
20
- import { FailIcon, GlobeIcon, StatusIcon, HeadersIcon, RequestIcon, ResponseIcon, DownloadIcon, ClearIcon, SizeIcon, } from '../NetworkIcons';
19
+ import { FailIcon, StatusIcon, HeadersIcon, RequestIcon, ResponseIcon, DownloadIcon, ClearIcon, SizeIcon, ExternalLinkIcon, ClockIcon, } from '../NetworkIcons';
21
20
  const NetworkDetail = React.memo(() => {
22
21
  const { t } = useTranslation();
23
22
  const { selected, detailDisplayUrl, apiDetailActiveTab, setApiDetailActiveTab, detailSearch, setDetailSearch, reqExpanded, setReqExpanded, resExpanded, setResExpanded, showReqDiff, setShowReqDiff, showResDiff, setShowResDiff, prevRequestData, prevResponseData, logRouteMapRef, } = useInspector();
@@ -73,70 +72,122 @@ const NetworkDetail = React.memo(() => {
73
72
  <View style={{
74
73
  flexDirection: 'row',
75
74
  alignItems: 'center',
76
- gap: 8,
75
+ gap: 6,
76
+ flexWrap: 'wrap',
77
+ flex: 1,
77
78
  }}>
79
+ {/* Method Chip */}
78
80
  <View style={[
79
81
  styles.methodBadge,
80
82
  {
81
83
  backgroundColor: METHOD_COLORS[selected.method] ?? METHOD_COLORS.ALL,
84
+ paddingHorizontal: 8,
85
+ paddingVertical: 4,
86
+ borderRadius: 7,
82
87
  },
83
88
  ]}>
84
89
  <Text style={[
85
90
  styles.methodBadgeText,
86
91
  {
87
92
  color: AppColors.white,
93
+ fontSize: 10.5,
88
94
  },
89
95
  ]}>
90
96
  {selected.method}
91
97
  </Text>
92
98
  </View>
93
99
 
100
+ {/* Status Pill */}
94
101
  {selected.status != null && (<View style={[
95
102
  styles.chip,
96
103
  {
97
104
  backgroundColor: selected.status === 0
98
- ? `${AppColors.errorColor}15`
99
- : `${getStatusColor(selected.status)}15`,
105
+ ? `${AppColors.errorColor}18`
106
+ : `${getStatusColor(selected.status)}18`,
100
107
  borderColor: selected.status === 0
101
108
  ? `${AppColors.errorColor}40`
102
109
  : `${getStatusColor(selected.status)}40`,
110
+ paddingHorizontal: 7,
111
+ paddingVertical: 3.5,
112
+ borderRadius: 7,
103
113
  },
104
114
  ]}>
105
- {selected.status === 0 ? (<FailIcon size={8} color={AppColors.errorColor}/>) : (<Svg width={6} height={6} viewBox="0 0 10 10" fill="none">
106
- <Circle cx="5" cy="5" r="5" fill={getStatusColor(selected.status)}/>
107
- </Svg>)}
115
+ {selected.status === 0 ? (<FailIcon size={8} color={AppColors.errorColor}/>) : (<View style={{
116
+ width: 6,
117
+ height: 6,
118
+ borderRadius: 3,
119
+ backgroundColor: getStatusColor(selected.status),
120
+ }}/>)}
108
121
  <Text style={[
109
122
  styles.chipText,
110
123
  {
111
124
  color: selected.status === 0
112
125
  ? AppColors.errorColor
113
126
  : getStatusColor(selected.status),
127
+ fontFamily: AppFonts.interBold,
128
+ fontSize: 10.5,
114
129
  },
115
130
  ]}>
116
131
  {selected.status === 0
117
132
  ? 'Failed'
118
- : String(selected.status)}
133
+ : `${selected.status} ${selected.status === 200 ? 'OK' : ''}`}
119
134
  </Text>
120
135
  </View>)}
121
136
 
137
+ {/* Duration Pill */}
122
138
  {selected.duration != null && (<View style={[
123
139
  styles.chip,
124
140
  {
125
- backgroundColor: `${AppColors.purple}14`,
126
- borderColor: `${AppColors.purple}2E`,
141
+ backgroundColor: `${AppColors.brandPurple}14`,
142
+ borderColor: `${AppColors.brandPurple}30`,
143
+ paddingHorizontal: 7,
144
+ paddingVertical: 3.5,
145
+ borderRadius: 7,
146
+ gap: 4,
127
147
  },
128
148
  ]}>
149
+ <ClockIcon color={AppColors.brandPurple} size={10}/>
129
150
  <Text style={[
130
151
  styles.chipText,
131
- { color: AppColors.purple },
152
+ {
153
+ color: AppColors.brandPurple,
154
+ fontFamily: AppFonts.interBold,
155
+ fontSize: 10.5,
156
+ },
132
157
  ]}>
133
158
  {selected.duration}ms
134
159
  </Text>
135
160
  </View>)}
161
+
162
+ {/* Size Pill */}
163
+ {selected.response != null && (<View style={[
164
+ styles.chip,
165
+ {
166
+ backgroundColor: `${AppColors.sky600}14`,
167
+ borderColor: `${AppColors.sky600}30`,
168
+ paddingHorizontal: 7,
169
+ paddingVertical: 3.5,
170
+ borderRadius: 7,
171
+ gap: 4,
172
+ },
173
+ ]}>
174
+ <SizeIcon color={AppColors.sky600} size={10}/>
175
+ <Text style={[
176
+ styles.chipText,
177
+ {
178
+ color: AppColors.sky600,
179
+ fontFamily: AppFonts.interBold,
180
+ fontSize: 10.5,
181
+ },
182
+ ]}>
183
+ {getSize(selected.response)}
184
+ </Text>
185
+ </View>)}
136
186
  </View>
187
+
137
188
  <View style={styles.detailInfoRight}>
138
- <TouchableScale style={styles.iconSquareBtn} onPress={() => Linking.openURL(detailDisplayUrl)} hitSlop={12}>
139
- <GlobeIcon color={AppColors.grayTextWeak} size={14}/>
189
+ <TouchableScale style={[styles.iconSquareBtn, { backgroundColor: `${AppColors.sky600}15` }]} onPress={() => Linking.openURL(detailDisplayUrl)} hitSlop={12}>
190
+ <ExternalLinkIcon color={AppColors.sky600} size={14}/>
140
191
  </TouchableScale>
141
192
  <CopyButton value={getFetchCommand(selected)} label="fetch()" iconType="fetch"/>
142
193
  <CopyButton value={getCurlCommand(selected)} label="cURL" iconType="terminal"/>
@@ -144,67 +195,99 @@ const NetworkDetail = React.memo(() => {
144
195
  </View>
145
196
  </View>
146
197
 
198
+ {/* Full Live URL Analysis Card */}
147
199
  <Pressable style={{
148
200
  backgroundColor: AppColors.grayBackground,
149
201
  borderRadius: 10,
150
202
  borderWidth: 1,
151
203
  borderColor: AppColors.dividerColor,
152
204
  padding: 10,
153
- marginTop: 6,
205
+ marginTop: 8,
206
+ gap: 6,
154
207
  }} onPress={() => Linking.openURL(detailDisplayUrl)}>
155
208
  <View style={{
156
209
  flexDirection: 'row',
157
210
  alignItems: 'center',
158
211
  justifyContent: 'space-between',
159
- marginBottom: 2,
160
212
  }}>
161
- <Text style={{
162
- fontFamily: AppFonts.interMedium,
163
- fontSize: 10,
164
- color: AppColors.grayTextWeak,
165
- flex: 1,
166
- }} numberOfLines={1}>
167
- {schemeStr ? (<>
213
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
214
+ {schemeStr ? (<View style={{
215
+ backgroundColor: schemeStr.startsWith('https')
216
+ ? `${AppColors.green600}18`
217
+ : `${AppColors.amber500}18`,
218
+ paddingHorizontal: 6,
219
+ paddingVertical: 2,
220
+ borderRadius: 4,
221
+ }}>
168
222
  <Text style={{
169
- color: AppColors.slate400,
170
- fontFamily: AppFonts.interRegular,
223
+ fontFamily: AppFonts.interBold,
224
+ fontSize: 9,
225
+ color: schemeStr.startsWith('https')
226
+ ? AppColors.green600
227
+ : AppColors.amber500,
171
228
  }}>
172
- {schemeStr}
229
+ {schemeStr.replace('://', '').toUpperCase()}
173
230
  </Text>
174
- {hostStr}
175
- </>) : (hostStr || 'API Endpoint')}
176
- </Text>
177
- {queryStr ? (<View style={{
231
+ </View>) : null}
232
+
233
+ <Text style={{
234
+ fontFamily: AppFonts.interBold,
235
+ fontSize: 10,
236
+ color: AppColors.grayTextWeak,
237
+ letterSpacing: 0.4,
238
+ textTransform: 'uppercase',
239
+ }}>
240
+ ENDPOINT URL ↗
241
+ </Text>
242
+ </View>
243
+
244
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 4 }}>
245
+ {queryStr ? (<View style={{
178
246
  backgroundColor: `${AppColors.purple}14`,
179
- paddingHorizontal: 5,
180
- paddingVertical: 1,
247
+ paddingHorizontal: 6,
248
+ paddingVertical: 2,
181
249
  borderRadius: 4,
182
250
  }}>
183
- <Text style={{
251
+ <Text style={{
184
252
  fontFamily: AppFonts.interBold,
185
253
  fontSize: 8.5,
186
254
  color: AppColors.purple,
187
255
  }}>
188
- {t('network.queryParams')}
256
+ {t('network.queryParams')}
257
+ </Text>
258
+ </View>) : null}
259
+
260
+ <View style={{
261
+ flexDirection: 'row',
262
+ alignItems: 'center',
263
+ gap: 3,
264
+ backgroundColor: `${AppColors.sky600}14`,
265
+ paddingHorizontal: 6,
266
+ paddingVertical: 2,
267
+ borderRadius: 4,
268
+ }}>
269
+ <ExternalLinkIcon color={AppColors.sky600} size={10}/>
270
+ <Text style={{
271
+ fontFamily: AppFonts.interBold,
272
+ fontSize: 9,
273
+ color: AppColors.sky600,
274
+ }}>
275
+ Open ↗
189
276
  </Text>
190
- </View>) : null}
277
+ </View>
278
+ </View>
191
279
  </View>
280
+
281
+ {/* Full Clickable Hyperlink */}
192
282
  <Text selectable={true} style={{
193
- fontFamily: AppFonts.interBold,
194
- fontSize: 12,
195
- color: AppColors.primaryBlack,
196
- marginTop: 2,
197
- }} numberOfLines={2}>
198
- {pathStr}
283
+ fontFamily: AppFonts.interMedium,
284
+ fontSize: 12.5,
285
+ color: AppColors.skyBlue,
286
+ textDecorationLine: 'underline',
287
+ lineHeight: 18,
288
+ }}>
289
+ {detailDisplayUrl}
199
290
  </Text>
200
- {queryStr ? (<Text selectable={true} style={{
201
- fontFamily: AppFonts.interRegular,
202
- fontSize: 10,
203
- color: AppColors.grayTextWeak,
204
- marginTop: 4,
205
- }} numberOfLines={1}>
206
- {queryStr}
207
- </Text>) : null}
208
291
  </Pressable>
209
292
  </>);
210
293
  })()}
@@ -9,6 +9,7 @@ import { AppFonts } from '../../styles/AppFonts';
9
9
  import { useTranslation } from '../../i18n';
10
10
  import { getRuntimeDiagnostics } from '../../helpers';
11
11
  import { SearchIcon, CircleXIcon, PerformanceIcon, BoltIcon, LightbulbIcon, WarningTriangleIcon, MapIcon, AtomIcon, BrainIcon, GlobeIcon, } from '../NetworkIcons';
12
+ import { INITIAL_EVENTS, subscribePerformanceEvents, logPerformanceEvent, clearPerformanceEvents, } from '../../customHooks/performanceTracker';
12
13
  const PerformanceTab = React.memo(() => {
13
14
  const { t } = useTranslation();
14
15
  // ─── Real-Time FPS Tracking ────────────────────────────────────────────────
@@ -34,10 +35,16 @@ const PerformanceTab = React.memo(() => {
34
35
  const [fpsHistory, setFpsHistory] = useState([
35
36
  60, 59, 60, 60, 58, 60, 59, 60, 60, 60, 57, 60, 59, 60, 60, 58, 60, 60, 59, 60,
36
37
  ]);
37
- // Performance Log Events
38
- const [events, setEvents] = useState([]);
38
+ // Performance Log Events (Initialized with benchmark & diagnostics events)
39
+ const [events, setEvents] = useState(INITIAL_EVENTS);
39
40
  const [filterCategory, setFilterCategory] = useState('ALL');
40
41
  const [search, setSearch] = useState('');
42
+ useEffect(() => {
43
+ const unsub = subscribePerformanceEvents(liveEvents => {
44
+ setEvents(liveEvents);
45
+ });
46
+ return unsub;
47
+ }, []);
41
48
  // ─── Live Frame Measurement Loop (1-Second Batch Interval) ────────────────
42
49
  const lastFrameTimeRef = useRef(Date.now());
43
50
  const rafIdRef = useRef(null);
@@ -81,9 +88,7 @@ const PerformanceTab = React.memo(() => {
81
88
  // Trigger log if noticeable frame drop
82
89
  if (measuredFps < 50) {
83
90
  const durationStr = (1000 / measuredFps).toFixed(1);
84
- const newEvent = {
85
- id: `drop-${Date.now()}`,
86
- timestamp: Date.now(),
91
+ logPerformanceEvent({
87
92
  type: 'fps_drop',
88
93
  category: 'render',
89
94
  fps: measuredFps,
@@ -97,8 +102,7 @@ const PerformanceTab = React.memo(() => {
97
102
  },
98
103
  advice: t('performance.liveFpsDipAdvice'),
99
104
  severity: measuredFps < 30 ? 'critical' : 'warning',
100
- };
101
- setEvents(prev => [newEvent, ...prev.slice(0, 49)]);
105
+ });
102
106
  }
103
107
  frameCount = 0;
104
108
  maxLagInSecond = 0;
@@ -208,7 +212,7 @@ const PerformanceTab = React.memo(() => {
208
212
  };
209
213
  }, [avgFps, jsLagMs]);
210
214
  const clearEvents = () => {
211
- setEvents([]);
215
+ clearPerformanceEvents();
212
216
  };
213
217
  return (<View style={perfStyles.container}>
214
218
  {/* Search & Live Control Header */}
@@ -253,9 +257,9 @@ const PerformanceTab = React.memo(() => {
253
257
  </View>
254
258
  </View>
255
259
 
256
- {/* Filter Tabs */}
260
+ {/* ─── Frozen Interactive Filter Tabs Under Search Component ─── */}
257
261
  <View style={perfStyles.filterRow}>
258
- <ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={{ gap: 6, paddingHorizontal: 12, paddingVertical: 4 }}>
262
+ <ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={perfStyles.filterScrollContent}>
259
263
  {[
260
264
  { key: 'ALL', label: t('performance.allLogs'), Icon: BoltIcon, count: events.length },
261
265
  {
@@ -652,6 +656,12 @@ const perfStyles = StyleSheet.create({
652
656
  backgroundColor: AppColors.white,
653
657
  borderBottomWidth: 1,
654
658
  borderBottomColor: AppColors.dividerColor,
659
+ paddingVertical: 5,
660
+ },
661
+ filterScrollContent: {
662
+ gap: 6,
663
+ paddingHorizontal: 12,
664
+ paddingVertical: 2,
655
665
  },
656
666
  filterPill: {
657
667
  flexDirection: 'row',
@@ -12,9 +12,10 @@ import { isPersistentStorageAvailable } from '../../helpers/settingsStore';
12
12
  import { clearNetworkLogs } from '../../customHooks/networkLogger';
13
13
  import { clearConsoleLogs } from '../../customHooks/consoleLogger';
14
14
  import { clearAnalyticsEvents } from '../../customHooks/analyticsLogger';
15
- import { SignalIcon, TerminalIcon, AnalyticsIcon, SettingsIcon, SunIcon, MoonIcon, ScreenIcon, MotionIcon, LayersIcon, EyeIcon, CheckIcon, TrashIcon, WhiteBackNavigation, PackageIcon, ReduxIcon, PerformanceIcon, } from '../NetworkIcons';
15
+ import { clearCrashRecords, } from '../../customHooks/crashHandler';
16
+ import { SignalIcon, TerminalIcon, AnalyticsIcon, SettingsIcon, SunIcon, MoonIcon, ScreenIcon, MotionIcon, LayersIcon, EyeIcon, CheckIcon, TrashIcon, WhiteBackNavigation, PackageIcon, ReduxIcon, PerformanceIcon, CrashIcon, ShieldAlertIcon, } from '../NetworkIcons';
16
17
  const SettingsPanel = () => {
17
- const { settingsPage, setSettingsPage, settingsActiveSubTab, setSettingsActiveSubTab, tabVisibility, toggleTabVisibility, defaultTab, setDefaultTab, isDark, setIsDark, modalHeightPercent, setModalHeightPercent, modalAnimationType, setModalAnimationType, showDuplicateLogs, setShowDuplicateLogs, showConsoleLevels, setShowConsoleLevels, resetToDefaults, storage, logs, consoleLogs, analyticsEvents, reduxState, maxNetworkLogs, setMaxNetworkLogs, maxConsoleLogs, setMaxConsoleLogs, reduxAutoRefresh, setReduxAutoRefreshState, reduxExpandDepth, setReduxExpandDepth, switchActiveTab, setSelected, setSelectedEvent, setReduxState, } = useInspector();
18
+ const { settingsPage, setSettingsPage, settingsActiveSubTab, setSettingsActiveSubTab, tabVisibility, toggleTabVisibility, defaultTab, setDefaultTab, isDark, setIsDark, modalHeightPercent, setModalHeightPercent, modalAnimationType, setModalAnimationType, showDuplicateLogs, setShowDuplicateLogs, showConsoleLevels, setShowConsoleLevels, resetToDefaults, storage, logs, consoleLogs, analyticsEvents, reduxState, maxNetworkLogs, setMaxNetworkLogs, maxConsoleLogs, setMaxConsoleLogs, reduxAutoRefresh, setReduxAutoRefreshState, reduxExpandDepth, setReduxExpandDepth, switchActiveTab, setSelected, setSelectedEvent, setReduxState, crashRecords, maxCrashLogs, setMaxCrashLogs, } = useInspector();
18
19
  const isPersistent = isPersistentStorageAvailable();
19
20
  const headerTopPadding = Platform.OS === 'ios' && modalHeightPercent >= 95 ? 44 : 0;
20
21
  if (settingsPage === 'main') {
@@ -25,6 +26,7 @@ const SettingsPanel = () => {
25
26
  { key: 'redux', label: 'Redux', icon: 'redux' },
26
27
  { key: 'bundle', label: 'Bundle', icon: 'bundle' },
27
28
  { key: 'performance', label: 'Performance', icon: 'performance' },
29
+ { key: 'crash', label: 'Crash', icon: 'crash' },
28
30
  ];
29
31
  return (<View style={{ flex: 1, backgroundColor: AppColors.grayBackground }}>
30
32
  {/* Settings Header with back button */}
@@ -235,6 +237,9 @@ const SettingsPanel = () => {
235
237
  {tab.icon === 'performance' && (<PerformanceIcon color={isLocked
236
238
  ? AppColors.grayTextWeak
237
239
  : AppColors.purple} size={11}/>)}
240
+ {tab.icon === 'crash' && (<CrashIcon color={isLocked
241
+ ? AppColors.grayTextWeak
242
+ : AppColors.purple} size={11}/>)}
238
243
  </View>
239
244
  {/* Label + Required badge */}
240
245
  <Text style={{
@@ -674,6 +679,7 @@ const SettingsPanel = () => {
674
679
  {tab.icon === 'redux' && (<ReduxIcon color={isActive ? AppColors.white : AppColors.purple} size={11}/>)}
675
680
  {tab.icon === 'bundle' && (<PackageIcon color={isActive ? AppColors.white : AppColors.purple} size={11}/>)}
676
681
  {tab.icon === 'performance' && (<PerformanceIcon color={isActive ? AppColors.white : AppColors.purple} size={11}/>)}
682
+ {tab.icon === 'crash' && (<CrashIcon color={isActive ? AppColors.white : AppColors.purple} size={11}/>)}
677
683
  </View>
678
684
  <Text style={{
679
685
  fontFamily: AppFonts.interBold,
@@ -1392,6 +1398,89 @@ const SettingsPanel = () => {
1392
1398
  </View>
1393
1399
  </ScrollView>);
1394
1400
  }
1401
+ else if (settingsPage === 'crash') {
1402
+ title = 'Crash Settings';
1403
+ icon = <CrashIcon color={AppColors.white} size={16}/>;
1404
+ rightInfo = `Total: ${crashRecords?.length || 0}`;
1405
+ content = (<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16 }}>
1406
+ <View style={{
1407
+ backgroundColor: AppColors.primaryLight,
1408
+ padding: 16,
1409
+ borderRadius: 12,
1410
+ borderWidth: 1,
1411
+ borderColor: AppColors.grayBorderSecondary,
1412
+ gap: 4,
1413
+ }}>
1414
+ {renderSettingRow({
1415
+ icon: <LayersIcon color={AppColors.purple} size={16}/>,
1416
+ label: 'Max Crash Logs',
1417
+ description: 'How many crash records to preserve in history',
1418
+ picker: {
1419
+ options: [25, 50, 100, 200],
1420
+ selectedValue: maxCrashLogs || 100,
1421
+ onSelect: val => setMaxCrashLogs(val),
1422
+ },
1423
+ })}
1424
+ <View style={{ height: 1, backgroundColor: AppColors.dividerColor }}/>
1425
+ {renderSettingRow({
1426
+ icon: <ShieldAlertIcon color={AppColors.greenColor} size={16}/>,
1427
+ label: 'Global Crash Guard',
1428
+ description: 'Intercepts native, JS, and render errors directly',
1429
+ isLast: true,
1430
+ right: (<View style={{
1431
+ paddingHorizontal: 8,
1432
+ paddingVertical: 4,
1433
+ borderRadius: 6,
1434
+ backgroundColor: `${AppColors.greenColor}1F`,
1435
+ }}>
1436
+ <Text style={{
1437
+ fontFamily: AppFonts.interBold,
1438
+ fontSize: 10,
1439
+ color: AppColors.greenColor,
1440
+ }}>
1441
+ PROTECTED
1442
+ </Text>
1443
+ </View>),
1444
+ })}
1445
+ </View>
1446
+
1447
+ <View style={{
1448
+ backgroundColor: AppColors.primaryLight,
1449
+ borderRadius: 12,
1450
+ borderWidth: 1,
1451
+ borderColor: AppColors.grayBorderSecondary,
1452
+ padding: 16,
1453
+ marginTop: 12,
1454
+ }}>
1455
+ {renderSettingRow({
1456
+ icon: <TrashIcon color={AppColors.errorColor} size={16}/>,
1457
+ label: 'Clear Crash History',
1458
+ description: `${crashRecords?.length || 0} crash logs stored`,
1459
+ isLast: true,
1460
+ onPress: () => {
1461
+ clearCrashRecords();
1462
+ Alert.alert('Success', 'Crash logs cleared.');
1463
+ },
1464
+ right: (<View style={{
1465
+ paddingHorizontal: 10,
1466
+ paddingVertical: 5,
1467
+ borderRadius: 7,
1468
+ backgroundColor: `${AppColors.errorColor}14`,
1469
+ borderWidth: 1,
1470
+ borderColor: `${AppColors.errorColor}33`,
1471
+ }}>
1472
+ <Text style={{
1473
+ fontFamily: AppFonts.interBold,
1474
+ fontSize: 11,
1475
+ color: AppColors.errorColor,
1476
+ }}>
1477
+ Clear
1478
+ </Text>
1479
+ </View>),
1480
+ })}
1481
+ </View>
1482
+ </ScrollView>);
1483
+ }
1395
1484
  return (<View style={{ flex: 1, backgroundColor: AppColors.grayBackground }}>
1396
1485
  <LinearGradient colors={[AppColors.purple, AppColors.brandPurple]} start={{ x: 0, y: 0 }} end={{ x: 1, y: 0 }} style={styles.headerGradient}>
1397
1486
  <View style={{ paddingTop: headerTopPadding, width: '100%' }}>
@@ -4,9 +4,9 @@ import { useInspector } from './InspectorContext';
4
4
  import TouchableScale from '../TouchableScale';
5
5
  import styles from '../../styles';
6
6
  import { AppColors } from '../../styles/AppColors';
7
- import { SignalIcon, TerminalIcon, AnalyticsIcon, PackageIcon, ReduxIcon, PerformanceIcon, } from '../NetworkIcons';
7
+ import { SignalIcon, TerminalIcon, AnalyticsIcon, PackageIcon, ReduxIcon, PerformanceIcon, CrashIcon, } from '../NetworkIcons';
8
8
  const TabBar = React.memo(() => {
9
- const { activeTab, switchActiveTab, tabVisibility, logs, consoleLogs, analyticsEvents, lastReadApisCount, lastReadLogsCount, unreadPulseAnim, } = useInspector();
9
+ const { activeTab, switchActiveTab, tabVisibility, logs, consoleLogs, analyticsEvents, crashRecords, lastReadApisCount, lastReadLogsCount, lastReadCrashesCount, unreadPulseAnim, } = useInspector();
10
10
  return (<View style={styles.tabBarContainer}>
11
11
  <ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={{ paddingRight: 16 }}>
12
12
  {[
@@ -46,18 +46,28 @@ const TabBar = React.memo(() => {
46
46
  count: 0,
47
47
  icon: 'performance',
48
48
  },
49
+ {
50
+ key: 'crash',
51
+ label: 'Crash',
52
+ count: crashRecords?.length || 0,
53
+ icon: 'crash',
54
+ },
49
55
  ]
50
56
  .filter(tab => tabVisibility?.[tab.key])
51
57
  .map(tab => {
52
58
  const isActive = activeTab === tab.key;
53
59
  const iconColor = isActive
54
60
  ? AppColors.white
55
- : AppColors.grayText;
61
+ : tab.key === 'crash' && tab.count > 0
62
+ ? AppColors.errorColor
63
+ : AppColors.grayText;
56
64
  const countLabel = tab.count > 9 ? '9+' : String(tab.count);
57
65
  const hasUnreadApis = activeTab !== 'apis' &&
58
66
  logs.length > lastReadApisCount;
59
67
  const hasUnreadLogs = activeTab !== 'logs' &&
60
68
  consoleLogs.length > lastReadLogsCount;
69
+ const hasUnreadCrashes = activeTab !== 'crash' &&
70
+ (crashRecords?.length || 0) > (lastReadCrashesCount || 0);
61
71
  return (<TouchableScale key={tab.key} onPress={() => switchActiveTab(tab.key)} style={[
62
72
  styles.contentTabButton,
63
73
  isActive && styles.contentTabButtonActive,
@@ -73,6 +83,7 @@ const TabBar = React.memo(() => {
73
83
  {tab.icon === 'redux' && (<ReduxIcon color={iconColor} size={14}/>)}
74
84
  {tab.icon === 'bundle' && (<PackageIcon color={iconColor} size={14}/>)}
75
85
  {tab.icon === 'performance' && (<PerformanceIcon color={iconColor} size={14}/>)}
86
+ {tab.icon === 'crash' && (<CrashIcon color={iconColor} size={14}/>)}
76
87
  <Text numberOfLines={1} ellipsizeMode="tail" style={[
77
88
  styles.contentTabButtonText,
78
89
  isActive &&
@@ -82,7 +93,8 @@ const TabBar = React.memo(() => {
82
93
  {tab.count > 0 ? `(${countLabel})` : ''}
83
94
  </Text>
84
95
  {((tab.key === 'apis' && hasUnreadApis) ||
85
- (tab.key === 'logs' && hasUnreadLogs)) && (<Animated.View style={{
96
+ (tab.key === 'logs' && hasUnreadLogs) ||
97
+ (tab.key === 'crash' && hasUnreadCrashes)) && (<Animated.View style={{
86
98
  width: 6,
87
99
  height: 6,
88
100
  borderRadius: 3,
@@ -62,6 +62,7 @@ export declare const ForwardChevronIcon: ({ color, size, }: any) => React.JSX.El
62
62
  export declare const BundleIcon: ({ color, size, }: any) => React.JSX.Element;
63
63
  export declare const LiveStateIcon: ({ color, size, }: any) => React.JSX.Element;
64
64
  export declare const TimelineIcon: ({ color, size, }: any) => React.JSX.Element;
65
+ export declare const RefreshCcwIcon: ({ color, size, }: any) => React.JSX.Element;
65
66
  export declare const StorageIcon: ({ color, size, }: any) => React.JSX.Element;
66
67
  export declare const MetadataIcon: ({ color, size, }: any) => React.JSX.Element;
67
68
  export declare const ReduxIcon: ({ color, size, }: any) => React.JSX.Element;
@@ -78,3 +79,27 @@ export declare const BrainIcon: ({ color, size }: any) => React.JSX.Element;
78
79
  export declare const TextAaIcon: ({ color, size }: any) => React.JSX.Element;
79
80
  export declare const CartIcon: ({ color, size }: any) => React.JSX.Element;
80
81
  export declare const MoneyIcon: ({ color, size }: any) => React.JSX.Element;
82
+ export declare const ExternalLinkIcon: ({ color, size, }: any) => React.JSX.Element;
83
+ export declare const PackageBoxIcon: ({ color, size, }: any) => React.JSX.Element;
84
+ export declare const GitHubIcon: ({ color, size, }: any) => React.JSX.Element;
85
+ export declare const LinkChainIcon: ({ color, size, }: any) => React.JSX.Element;
86
+ export declare const AppleIcon: ({ color, size, }: any) => React.JSX.Element;
87
+ export declare const AndroidIcon: ({ color, size, }: any) => React.JSX.Element;
88
+ export declare const CodeBracketsIcon: ({ color, size, }: any) => React.JSX.Element;
89
+ export declare const FlameIcon: ({ color, size, }: any) => React.JSX.Element;
90
+ export declare const HourglassIcon: ({ color, size, }: any) => React.JSX.Element;
91
+ export declare const PinIcon: ({ color, size, }: any) => React.JSX.Element;
92
+ export declare const RepeatIcon: ({ color, size, }: any) => React.JSX.Element;
93
+ export declare const TargetGoalIcon: ({ color, size, }: any) => React.JSX.Element;
94
+ export declare const UserCheckIcon: ({ color, size, }: any) => React.JSX.Element;
95
+ export declare const CrashIcon: ({ color, size, }: any) => React.JSX.Element;
96
+ export declare const ShieldAlertIcon: ({ color, size, }: any) => React.JSX.Element;
97
+ export declare const SkullIcon: ({ color, size, }: any) => React.JSX.Element;
98
+ export declare const ChipIcon: ({ color, size, }: any) => React.JSX.Element;
99
+ export declare const LayoutIcon: ({ color, size, }: any) => React.JSX.Element;
100
+ export declare const StackTraceIcon: ({ color, size, }: any) => React.JSX.Element;
101
+ export declare const DiagnosticsIcon: ({ color, size, }: any) => React.JSX.Element;
102
+ export declare const TrailIcon: ({ color, size, }: any) => React.JSX.Element;
103
+ export declare const RawJsonIcon: ({ color, size, }: any) => React.JSX.Element;
104
+ export declare const AppFramesIcon: ({ color, size, }: any) => React.JSX.Element;
105
+ export declare const AllFramesIcon: ({ color, size, }: any) => React.JSX.Element;