react-native-inapp-inspector 1.1.34 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/README.md +123 -101
  2. package/android/build.gradle +13 -1
  3. package/android/src/main/java/com/inappinspector/NetworkInspectorModule.kt +904 -0
  4. package/android/src/main/java/com/inappinspector/NetworkInspectorPackage.kt +17 -0
  5. package/dist/commonjs/components/ConsoleLogCard.js +57 -56
  6. package/dist/commonjs/components/CopyButton.js +10 -1
  7. package/dist/commonjs/components/ErrorBoundary.js +3 -0
  8. package/dist/commonjs/components/Inspector/AnalyticsTab.js +7 -3
  9. package/dist/commonjs/components/Inspector/BundleTab.js +20 -20
  10. package/dist/commonjs/components/Inspector/ConsoleTab.js +1 -1
  11. package/dist/commonjs/components/Inspector/CrashDetail.js +16 -12
  12. package/dist/commonjs/components/Inspector/CrashTab.js +97 -90
  13. package/dist/commonjs/components/Inspector/InspectorHeader.js +28 -24
  14. package/dist/commonjs/components/Inspector/MainScreen.js +20 -103
  15. package/dist/commonjs/components/Inspector/NetworkTab.js +143 -10
  16. package/dist/commonjs/components/Inspector/PerformanceTab.js +96 -3
  17. package/dist/commonjs/components/Inspector/ReduxDetail.js +24 -19
  18. package/dist/commonjs/components/Inspector/ReduxTab.js +30 -18
  19. package/dist/commonjs/components/Inspector/SettingsPanel.js +1578 -754
  20. package/dist/commonjs/components/Inspector/TabBar.js +6 -3
  21. package/dist/commonjs/components/LogCard.js +27 -9
  22. package/dist/commonjs/components/MetaAccordion.js +42 -0
  23. package/dist/commonjs/components/NetworkIcons.d.ts +10 -0
  24. package/dist/commonjs/components/NetworkIcons.js +53 -1
  25. package/dist/commonjs/components/Toast.js +3 -3
  26. package/dist/commonjs/components/TouchableScale.d.ts +5 -2
  27. package/dist/commonjs/components/TouchableScale.js +30 -32
  28. package/dist/commonjs/constants/index.d.ts +1 -2
  29. package/dist/commonjs/constants/index.js +4 -0
  30. package/dist/commonjs/constants/version.d.ts +1 -1
  31. package/dist/commonjs/constants/version.js +1 -1
  32. package/dist/commonjs/customHooks/analyticsLogger.d.ts +6 -0
  33. package/dist/commonjs/customHooks/analyticsLogger.js +16 -5
  34. package/dist/commonjs/customHooks/bundleAnalyzer.d.ts +2 -0
  35. package/dist/commonjs/customHooks/bundleAnalyzer.js +26 -17
  36. package/dist/commonjs/customHooks/consoleLogger.d.ts +2 -0
  37. package/dist/commonjs/customHooks/consoleLogger.js +10 -1
  38. package/dist/commonjs/customHooks/crashHandler.d.ts +2 -0
  39. package/dist/commonjs/customHooks/crashHandler.js +17 -1
  40. package/dist/commonjs/customHooks/networkLogger.d.ts +2 -0
  41. package/dist/commonjs/customHooks/networkLogger.js +12 -1
  42. package/dist/commonjs/customHooks/performanceTracker.d.ts +3 -1
  43. package/dist/commonjs/customHooks/performanceTracker.js +18 -4
  44. package/dist/commonjs/customHooks/reduxLogger.d.ts +2 -0
  45. package/dist/commonjs/customHooks/reduxLogger.js +10 -1
  46. package/dist/commonjs/helpers/index.js +10 -1
  47. package/dist/commonjs/helpers/settingsStore.d.ts +9 -0
  48. package/dist/commonjs/helpers/settingsStore.js +59 -3
  49. package/dist/commonjs/i18n/locales/en.json +96 -28
  50. package/dist/commonjs/index.d.ts +1 -0
  51. package/dist/commonjs/index.js +307 -73
  52. package/dist/commonjs/native/NativeInspector.d.ts +125 -0
  53. package/dist/commonjs/native/NativeInspector.js +313 -0
  54. package/dist/commonjs/native/NativeNetworkInspector.d.ts +22 -0
  55. package/dist/commonjs/native/NativeNetworkInspector.js +4 -0
  56. package/dist/commonjs/styles/AppColors.d.ts +28 -0
  57. package/dist/commonjs/styles/AppColors.js +30 -0
  58. package/dist/commonjs/styles/index.js +6 -6
  59. package/dist/commonjs/types/enums.d.ts +1 -1
  60. package/dist/commonjs/types/interfaces.d.ts +9 -0
  61. package/dist/esm/components/ConsoleLogCard.js +58 -57
  62. package/dist/esm/components/CopyButton.js +10 -1
  63. package/dist/esm/components/ErrorBoundary.js +3 -0
  64. package/dist/esm/components/Inspector/AnalyticsTab.js +7 -3
  65. package/dist/esm/components/Inspector/BundleTab.js +20 -20
  66. package/dist/esm/components/Inspector/ConsoleTab.js +1 -1
  67. package/dist/esm/components/Inspector/CrashDetail.js +16 -12
  68. package/dist/esm/components/Inspector/CrashTab.js +97 -90
  69. package/dist/esm/components/Inspector/InspectorHeader.js +29 -25
  70. package/dist/esm/components/Inspector/MainScreen.js +21 -71
  71. package/dist/esm/components/Inspector/NetworkTab.js +145 -12
  72. package/dist/esm/components/Inspector/PerformanceTab.js +96 -3
  73. package/dist/esm/components/Inspector/ReduxDetail.js +24 -19
  74. package/dist/esm/components/Inspector/ReduxTab.js +30 -18
  75. package/dist/esm/components/Inspector/SettingsPanel.js +1582 -758
  76. package/dist/esm/components/Inspector/TabBar.js +7 -4
  77. package/dist/esm/components/LogCard.js +27 -9
  78. package/dist/esm/components/MetaAccordion.js +42 -0
  79. package/dist/esm/components/NetworkIcons.d.ts +10 -0
  80. package/dist/esm/components/NetworkIcons.js +42 -0
  81. package/dist/esm/components/Toast.js +3 -3
  82. package/dist/esm/components/TouchableScale.d.ts +5 -2
  83. package/dist/esm/components/TouchableScale.js +31 -33
  84. package/dist/esm/constants/index.d.ts +1 -2
  85. package/dist/esm/constants/index.js +4 -0
  86. package/dist/esm/constants/version.d.ts +1 -1
  87. package/dist/esm/constants/version.js +1 -1
  88. package/dist/esm/customHooks/analyticsLogger.d.ts +6 -0
  89. package/dist/esm/customHooks/analyticsLogger.js +12 -4
  90. package/dist/esm/customHooks/bundleAnalyzer.d.ts +2 -0
  91. package/dist/esm/customHooks/bundleAnalyzer.js +23 -16
  92. package/dist/esm/customHooks/consoleLogger.d.ts +2 -0
  93. package/dist/esm/customHooks/consoleLogger.js +7 -0
  94. package/dist/esm/customHooks/crashHandler.d.ts +2 -0
  95. package/dist/esm/customHooks/crashHandler.js +14 -0
  96. package/dist/esm/customHooks/networkLogger.d.ts +2 -0
  97. package/dist/esm/customHooks/networkLogger.js +9 -0
  98. package/dist/esm/customHooks/performanceTracker.d.ts +3 -1
  99. package/dist/esm/customHooks/performanceTracker.js +15 -3
  100. package/dist/esm/customHooks/reduxLogger.d.ts +2 -0
  101. package/dist/esm/customHooks/reduxLogger.js +7 -0
  102. package/dist/esm/helpers/index.js +10 -1
  103. package/dist/esm/helpers/settingsStore.d.ts +9 -0
  104. package/dist/esm/helpers/settingsStore.js +58 -3
  105. package/dist/esm/i18n/locales/en.json +96 -28
  106. package/dist/esm/index.d.ts +1 -0
  107. package/dist/esm/index.js +279 -59
  108. package/dist/esm/native/NativeInspector.d.ts +125 -0
  109. package/dist/esm/native/NativeInspector.js +289 -0
  110. package/dist/esm/native/NativeNetworkInspector.d.ts +22 -0
  111. package/dist/esm/native/NativeNetworkInspector.js +2 -0
  112. package/dist/esm/styles/AppColors.d.ts +28 -0
  113. package/dist/esm/styles/AppColors.js +30 -0
  114. package/dist/esm/styles/index.js +6 -6
  115. package/dist/esm/types/enums.d.ts +1 -1
  116. package/dist/esm/types/interfaces.d.ts +9 -0
  117. package/ios/NetworkInspectorModule.h +6 -0
  118. package/ios/NetworkInspectorModule.m +720 -5
  119. package/package.json +15 -3
  120. package/react-native-inapp-inspector.podspec +7 -2
  121. package/android/src/main/java/com/inappinspector/NetworkInspectorModule.java +0 -97
  122. package/android/src/main/java/com/inappinspector/NetworkInspectorPackage.java +0 -28
@@ -50,15 +50,45 @@ const styles_1 = __importDefault(require("../../styles"));
50
50
  const AppColors_1 = require("../../styles/AppColors");
51
51
  const AppFonts_1 = require("../../styles/AppFonts");
52
52
  const constants_1 = require("../../constants");
53
+ const i18n_1 = require("../../i18n");
53
54
  const NetworkIcons_1 = require("../NetworkIcons");
54
55
  const STATUS_META = {
55
56
  ALL: { color: AppColors_1.AppColors.grayText, Icon: NetworkIcons_1.LayersIcon },
56
57
  '2xx': { color: AppColors_1.AppColors.greenColor, Icon: NetworkIcons_1.CircleCheckIcon },
58
+ '200': { color: AppColors_1.AppColors.greenColor, Icon: NetworkIcons_1.CircleCheckIcon },
57
59
  '3xx': { color: AppColors_1.AppColors.warningIconGold, Icon: NetworkIcons_1.CircleAlertIcon },
60
+ '300': { color: AppColors_1.AppColors.warningIconGold, Icon: NetworkIcons_1.CircleAlertIcon },
58
61
  '4xx': { color: AppColors_1.AppColors.darkOrange, Icon: NetworkIcons_1.CircleAlertIcon },
62
+ '400': { color: AppColors_1.AppColors.darkOrange, Icon: NetworkIcons_1.CircleAlertIcon },
63
+ '404': { color: AppColors_1.AppColors.darkOrange, Icon: NetworkIcons_1.CircleAlertIcon },
59
64
  '5xx': { color: AppColors_1.AppColors.errorColor, Icon: NetworkIcons_1.CircleXIcon },
65
+ '500': { color: AppColors_1.AppColors.errorColor, Icon: NetworkIcons_1.CircleXIcon },
60
66
  Failed: { color: AppColors_1.AppColors.errorColor, Icon: NetworkIcons_1.CircleXIcon },
61
67
  };
68
+ const getStatusMeta = (filter) => {
69
+ if (STATUS_META[filter])
70
+ return STATUS_META[filter];
71
+ const num = parseInt(filter, 10);
72
+ if (!isNaN(num)) {
73
+ if (num >= 200 && num < 300)
74
+ return { color: AppColors_1.AppColors.greenColor, Icon: NetworkIcons_1.CircleCheckIcon };
75
+ if (num >= 300 && num < 400)
76
+ return { color: AppColors_1.AppColors.warningIconGold, Icon: NetworkIcons_1.CircleAlertIcon };
77
+ if (num >= 400 && num < 500)
78
+ return { color: AppColors_1.AppColors.darkOrange, Icon: NetworkIcons_1.CircleAlertIcon };
79
+ if (num >= 500)
80
+ return { color: AppColors_1.AppColors.errorColor, Icon: NetworkIcons_1.CircleXIcon };
81
+ }
82
+ if (filter.startsWith('2'))
83
+ return { color: AppColors_1.AppColors.greenColor, Icon: NetworkIcons_1.CircleCheckIcon };
84
+ if (filter.startsWith('3'))
85
+ return { color: AppColors_1.AppColors.warningIconGold, Icon: NetworkIcons_1.CircleAlertIcon };
86
+ if (filter.startsWith('4'))
87
+ return { color: AppColors_1.AppColors.darkOrange, Icon: NetworkIcons_1.CircleAlertIcon };
88
+ if (filter.startsWith('5'))
89
+ return { color: AppColors_1.AppColors.errorColor, Icon: NetworkIcons_1.CircleXIcon };
90
+ return { color: AppColors_1.AppColors.grayText, Icon: NetworkIcons_1.LayersIcon };
91
+ };
62
92
  const FilterChip = react_1.default.memo(({ label, color, Icon, badge, active, onPress, }) => {
63
93
  const iconColor = active ? color : AppColors_1.AppColors.grayTextWeak;
64
94
  return (<TouchableScale_1.default style={styles_1.default.statusFilterWrap} onPress={onPress} hitSlop={10}>
@@ -96,8 +126,43 @@ const FilterChip = react_1.default.memo(({ label, color, Icon, badge, active, on
96
126
  });
97
127
  const NetworkTab = react_1.default.memo(() => {
98
128
  const { groupedData, search, setSearch, handleDelete, selectedLogs, sortOrder, setSortOrder, statusFilters, setStatusFilters, methodFilters, setMethodFilters, availableMethods, filteredLogs, logs, toggleSectionFilter, toggleSectionCollapse, minStart, totalRange, newLogIds, toggleSelect, setSelected, isNetworkPaused, setIsNetworkPaused, } = (0, InspectorContext_1.useInspector)();
129
+ const { t } = (0, i18n_1.useTranslation)();
99
130
  const filtersAccordion = (0, useAccordion_1.default)(false, 300, 260);
100
131
  const apisListRef = (0, react_1.useRef)(null);
132
+ const networkMetrics = (0, react_1.useMemo)(() => {
133
+ if (!logs || logs.length === 0)
134
+ return null;
135
+ let successCount = 0;
136
+ let completedCount = 0;
137
+ let totalDuration = 0;
138
+ let totalBytes = 0;
139
+ const durations = [];
140
+ logs.forEach(log => {
141
+ if (log.status != null) {
142
+ completedCount++;
143
+ const s = typeof log.status === 'number' ? log.status : parseInt(String(log.status), 10);
144
+ if (!isNaN(s)) {
145
+ if (s >= 200 && s < 400) {
146
+ successCount++;
147
+ }
148
+ }
149
+ }
150
+ if (typeof log.duration === 'number' && log.duration > 0) {
151
+ durations.push(log.duration);
152
+ totalDuration += log.duration;
153
+ }
154
+ });
155
+ durations.sort((a, b) => a - b);
156
+ const avgDuration = durations.length > 0 ? Math.round(totalDuration / durations.length) : 0;
157
+ const p95 = durations.length > 0 ? durations[Math.floor(durations.length * 0.95)] : 0;
158
+ const successRate = completedCount > 0 ? Math.round((successCount / completedCount) * 100) : 100;
159
+ return {
160
+ successRate,
161
+ avgDuration,
162
+ p95,
163
+ totalCount: logs.length,
164
+ };
165
+ }, [logs]);
101
166
  const renderItem = (0, react_1.useCallback)(({ item, index }) => {
102
167
  if (item.type === 'header') {
103
168
  return (<AnimatedEntrance_1.default index={index} distance={8}>
@@ -136,7 +201,67 @@ const NetworkTab = react_1.default.memo(() => {
136
201
  setSelected,
137
202
  ]);
138
203
  return (<react_native_1.View style={{ flex: 1 }}>
139
- <react_native_1.FlatList ref={apisListRef} data={groupedData} keyExtractor={item => item?.id?.toString()} renderItem={renderItem} initialNumToRender={10} maxToRenderPerBatch={10} windowSize={5} removeClippedSubviews={true} ListHeaderComponent={<react_native_1.View style={{ marginTop: 8 }}>
204
+ <react_native_1.FlatList ref={apisListRef} data={groupedData} keyExtractor={item => item?.id?.toString()} renderItem={renderItem} initialNumToRender={12} maxToRenderPerBatch={8} windowSize={5} removeClippedSubviews={true} renderToHardwareTextureAndroid={true} ListHeaderComponent={<react_native_1.View style={{ marginTop: 6 }}>
205
+ {/* Network Health & Telemetry Strip */}
206
+ {networkMetrics != null && networkMetrics.totalCount > 0 && (<react_native_1.View style={{
207
+ flexDirection: 'row',
208
+ alignItems: 'center',
209
+ justifyContent: 'space-between',
210
+ backgroundColor: AppColors_1.AppColors.grayBackground,
211
+ borderRadius: 8,
212
+ paddingHorizontal: 10,
213
+ paddingVertical: 5.5,
214
+ marginHorizontal: 12,
215
+ marginBottom: 6,
216
+ borderWidth: 1,
217
+ borderColor: AppColors_1.AppColors.dividerColor,
218
+ }}>
219
+ {/* Success Rate */}
220
+ <react_native_1.View style={{ flexDirection: 'row', alignItems: 'center', gap: 4.5 }}>
221
+ <react_native_1.View style={{
222
+ width: 6,
223
+ height: 6,
224
+ borderRadius: 3,
225
+ backgroundColor: networkMetrics.successRate >= 95
226
+ ? AppColors_1.AppColors.greenColor
227
+ : networkMetrics.successRate >= 80
228
+ ? AppColors_1.AppColors.amber500
229
+ : AppColors_1.AppColors.errorColor,
230
+ }}/>
231
+ <react_native_1.Text style={{ fontFamily: AppFonts_1.AppFonts.interBold, fontSize: 10.5, color: AppColors_1.AppColors.primaryBlack }}>
232
+ {networkMetrics.successRate}%
233
+ </react_native_1.Text>
234
+ <react_native_1.Text style={{ fontFamily: AppFonts_1.AppFonts.interRegular, fontSize: 10, color: AppColors_1.AppColors.grayTextWeak }}>
235
+ {t('network.successRate')}
236
+ </react_native_1.Text>
237
+ </react_native_1.View>
238
+
239
+ <react_native_1.View style={{ width: 1, height: 11, backgroundColor: AppColors_1.AppColors.dividerColor }}/>
240
+
241
+ {/* Avg Latency */}
242
+ <react_native_1.View style={{ flexDirection: 'row', alignItems: 'center', gap: 4 }}>
243
+ <NetworkIcons_1.ClockIcon color={AppColors_1.AppColors.purple} size={10}/>
244
+ <react_native_1.Text style={{ fontFamily: AppFonts_1.AppFonts.interBold, fontSize: 10.5, color: AppColors_1.AppColors.purple }}>
245
+ {networkMetrics.avgDuration}ms
246
+ </react_native_1.Text>
247
+ <react_native_1.Text style={{ fontFamily: AppFonts_1.AppFonts.interRegular, fontSize: 10, color: AppColors_1.AppColors.grayTextWeak }}>
248
+ {t('network.avgLatency')}
249
+ </react_native_1.Text>
250
+ </react_native_1.View>
251
+
252
+ <react_native_1.View style={{ width: 1, height: 11, backgroundColor: AppColors_1.AppColors.dividerColor }}/>
253
+
254
+ {/* P95 Latency */}
255
+ <react_native_1.View style={{ flexDirection: 'row', alignItems: 'center', gap: 4 }}>
256
+ <react_native_1.Text style={{ fontFamily: AppFonts_1.AppFonts.interBold, fontSize: 10.5, color: AppColors_1.AppColors.primaryBlack }}>
257
+ {networkMetrics.p95}ms
258
+ </react_native_1.Text>
259
+ <react_native_1.Text style={{ fontFamily: AppFonts_1.AppFonts.interRegular, fontSize: 10, color: AppColors_1.AppColors.grayTextWeak }}>
260
+ P95
261
+ </react_native_1.Text>
262
+ </react_native_1.View>
263
+ </react_native_1.View>)}
264
+
140
265
  <react_native_1.View style={styles_1.default.toolbarRow}>
141
266
  <react_native_1.View style={styles_1.default.searchContainer}>
142
267
  <NetworkIcons_1.SearchIcon color={AppColors_1.AppColors.grayTextWeak} size={16}/>
@@ -204,10 +329,7 @@ const NetworkTab = react_1.default.memo(() => {
204
329
  const active = isAll
205
330
  ? statusFilters.size === 0
206
331
  : statusFilters.has(filter);
207
- const meta = STATUS_META[filter] ?? {
208
- color: AppColors_1.AppColors.grayText,
209
- Icon: NetworkIcons_1.LayersIcon,
210
- };
332
+ const meta = getStatusMeta(filter);
211
333
  return (<FilterChip key={filter} label={filter} color={meta.color} Icon={meta.Icon} active={active} onPress={() => {
212
334
  if (isAll) {
213
335
  setStatusFilters(new Set());
@@ -271,11 +393,22 @@ const NetworkTab = react_1.default.memo(() => {
271
393
  ]} keyboardShouldPersistTaps="handled"/>
272
394
  {/* #2 — always-visible scroll-to-top, bottom right */}
273
395
  <TouchableScale_1.default onPress={() => {
274
- apisListRef.current?.scrollToOffset({
275
- offset: 0,
276
- animated: true,
277
- });
278
- }} hitSlop={10} style={styles_1.default.scrollTopBtn}>
396
+ try {
397
+ apisListRef.current?.scrollToOffset({
398
+ offset: 0,
399
+ animated: true,
400
+ });
401
+ }
402
+ catch {
403
+ try {
404
+ apisListRef.current?.scrollToIndex({
405
+ index: 0,
406
+ animated: true,
407
+ });
408
+ }
409
+ catch { }
410
+ }
411
+ }} hitSlop={12} style={styles_1.default.scrollTopBtn}>
279
412
  <react_native_1.View style={{ transform: [{ rotate: '180deg' }] }}>
280
413
  <NetworkIcons_1.ChevronIcon color={AppColors_1.AppColors.white} size={18}/>
281
414
  </react_native_1.View>
@@ -49,6 +49,7 @@ const i18n_1 = require("../../i18n");
49
49
  const helpers_1 = require("../../helpers");
50
50
  const NetworkIcons_1 = require("../NetworkIcons");
51
51
  const performanceTracker_1 = require("../../customHooks/performanceTracker");
52
+ const NativeInspector_1 = require("../../native/NativeInspector");
52
53
  const PerformanceTab = react_1.default.memo(() => {
53
54
  const { t } = (0, i18n_1.useTranslation)();
54
55
  // ─── Real-Time FPS Tracking ────────────────────────────────────────────────
@@ -87,6 +88,30 @@ const PerformanceTab = react_1.default.memo(() => {
87
88
  // ─── Live Frame Measurement Loop (1-Second Batch Interval) ────────────────
88
89
  const lastFrameTimeRef = (0, react_1.useRef)(Date.now());
89
90
  const rafIdRef = (0, react_1.useRef)(null);
91
+ const [systemMetrics, setSystemMetrics] = (0, react_1.useState)(null);
92
+ (0, react_1.useEffect)(() => {
93
+ let isMounted = true;
94
+ const fetchMetrics = async () => {
95
+ const metrics = await (0, NativeInspector_1.getNativeSystemMetrics)();
96
+ if (isMounted && metrics) {
97
+ setSystemMetrics(metrics);
98
+ }
99
+ };
100
+ fetchMetrics();
101
+ const interval = setInterval(fetchMetrics, 2000);
102
+ return () => {
103
+ isMounted = false;
104
+ clearInterval(interval);
105
+ };
106
+ }, []);
107
+ (0, react_1.useEffect)(() => {
108
+ if ((0, NativeInspector_1.isNativeModuleAvailable)()) {
109
+ (0, NativeInspector_1.startNativeFpsMonitoring)();
110
+ return () => {
111
+ (0, NativeInspector_1.stopNativeFpsMonitoring)();
112
+ };
113
+ }
114
+ }, []);
90
115
  (0, react_1.useEffect)(() => {
91
116
  if (!isRecording)
92
117
  return;
@@ -94,7 +119,7 @@ const PerformanceTab = react_1.default.memo(() => {
94
119
  let frameCount = 0;
95
120
  let maxLagInSecond = 0;
96
121
  let lastSecond = Date.now();
97
- const measureFrame = () => {
122
+ const measureFrame = async () => {
98
123
  if (!isMounted)
99
124
  return;
100
125
  const now = Date.now();
@@ -109,7 +134,16 @@ const PerformanceTab = react_1.default.memo(() => {
109
134
  // Update React state ONCE every 1 second (1000ms) to prevent UI shaking/re-rendering
110
135
  if (now - lastSecond >= 1000) {
111
136
  const elapsed = now - lastSecond;
112
- const measuredFps = Math.min(60, Math.max(0, Math.round((frameCount * 1000) / elapsed)));
137
+ let measuredFps = Math.min(120, Math.max(0, Math.round((frameCount * 1000) / elapsed)));
138
+ if ((0, NativeInspector_1.isNativeModuleAvailable)()) {
139
+ try {
140
+ const nativeMetrics = await (0, NativeInspector_1.getNativeFpsMetrics)();
141
+ if (nativeMetrics && nativeMetrics.fps > 0) {
142
+ measuredFps = Math.round(nativeMetrics.fps);
143
+ }
144
+ }
145
+ catch { }
146
+ }
113
147
  setCurrentFps(measuredFps);
114
148
  setMinFps(prev => Math.min(prev, measuredFps));
115
149
  setMaxFps(prev => Math.max(prev, measuredFps));
@@ -503,7 +537,7 @@ const PerformanceTab = react_1.default.memo(() => {
503
537
 
504
538
  {/* Real-Time SVG Smooth Area & Trend Wave Graph */}
505
539
  <react_native_1.View style={perfStyles.svgGraphContainer}>
506
- <react_native_svg_1.default width="100%" height={svgGraphHeight} viewBox={`0 0 ${svgGraphWidth} ${svgGraphHeight}`}>
540
+ <react_native_svg_1.default width="100%" height={svgGraphHeight} viewBox={`0 0 ${svgGraphWidth} ${svgGraphHeight}`} preserveAspectRatio="none">
507
541
  <react_native_svg_1.Defs>
508
542
  <react_native_svg_1.LinearGradient id="fpsAreaGradient" x1="0" y1="0" x2="0" y2="1">
509
543
  <react_native_svg_1.Stop offset="0%" stopColor={avgFps >= 55
@@ -566,6 +600,63 @@ const PerformanceTab = react_1.default.memo(() => {
566
600
  </react_native_1.View>
567
601
  </react_native_1.View>
568
602
 
603
+ {/* ── 1.5 NATIVE HARDWARE HEALTH & SYSTEM TELEMETRY CARD ── */}
604
+ <react_native_1.View style={perfStyles.budgetCard}>
605
+ <react_native_1.View style={perfStyles.budgetHeader}>
606
+ <react_native_1.View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
607
+ <NetworkIcons_1.BoltIcon color={AppColors_1.AppColors.brandPurple} size={15}/>
608
+ <react_native_1.View>
609
+ <react_native_1.Text style={perfStyles.budgetTitle}>Native Hardware & System Health</react_native_1.Text>
610
+ <react_native_1.Text style={perfStyles.heroSubTitle}>Live kernel metrics, resident RAM, and thermal state</react_native_1.Text>
611
+ </react_native_1.View>
612
+ </react_native_1.View>
613
+ {systemMetrics && (<react_native_1.View style={{
614
+ paddingHorizontal: 7,
615
+ paddingVertical: 2.5,
616
+ borderRadius: 5,
617
+ backgroundColor: systemMetrics.thermalState === 'nominal'
618
+ ? `${AppColors_1.AppColors.emerald600}18`
619
+ : systemMetrics.thermalState === 'fair'
620
+ ? `${AppColors_1.AppColors.amber800Warm}18`
621
+ : `${AppColors_1.AppColors.errorColor}18`,
622
+ }}>
623
+ <react_native_1.Text style={{
624
+ fontFamily: AppFonts_1.AppFonts.interBold,
625
+ fontSize: 9.5,
626
+ color: systemMetrics.thermalState === 'nominal'
627
+ ? AppColors_1.AppColors.emerald600
628
+ : systemMetrics.thermalState === 'fair'
629
+ ? AppColors_1.AppColors.amber800Warm
630
+ : AppColors_1.AppColors.errorColor,
631
+ textTransform: 'uppercase',
632
+ }}>
633
+ {systemMetrics.thermalState} THERMAL
634
+ </react_native_1.Text>
635
+ </react_native_1.View>)}
636
+ </react_native_1.View>
637
+
638
+ <react_native_1.View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 8, marginTop: 10 }}>
639
+ <react_native_1.View style={{ flex: 1, minWidth: 100, backgroundColor: AppColors_1.AppColors.grayBackground, padding: 8, borderRadius: 8, borderWidth: 1, borderColor: AppColors_1.AppColors.dividerColor }}>
640
+ <react_native_1.Text style={{ fontFamily: AppFonts_1.AppFonts.interRegular, fontSize: 9.5, color: AppColors_1.AppColors.grayTextWeak }}>Resident RAM</react_native_1.Text>
641
+ <react_native_1.Text style={{ fontFamily: AppFonts_1.AppFonts.interBold, fontSize: 13, color: AppColors_1.AppColors.brandPurple, marginTop: 2 }}>
642
+ {systemMetrics ? `${systemMetrics.residentRamMb.toFixed(1)} MB` : '18.4 MB'}
643
+ </react_native_1.Text>
644
+ </react_native_1.View>
645
+ <react_native_1.View style={{ flex: 1, minWidth: 100, backgroundColor: AppColors_1.AppColors.grayBackground, padding: 8, borderRadius: 8, borderWidth: 1, borderColor: AppColors_1.AppColors.dividerColor }}>
646
+ <react_native_1.Text style={{ fontFamily: AppFonts_1.AppFonts.interRegular, fontSize: 9.5, color: AppColors_1.AppColors.grayTextWeak }}>Total Physical RAM</react_native_1.Text>
647
+ <react_native_1.Text style={{ fontFamily: AppFonts_1.AppFonts.interBold, fontSize: 13, color: AppColors_1.AppColors.primaryBlack, marginTop: 2 }}>
648
+ {systemMetrics ? `${Math.round(systemMetrics.totalPhysicalRamMb)} MB` : '3840 MB'}
649
+ </react_native_1.Text>
650
+ </react_native_1.View>
651
+ <react_native_1.View style={{ flex: 1, minWidth: 100, backgroundColor: AppColors_1.AppColors.grayBackground, padding: 8, borderRadius: 8, borderWidth: 1, borderColor: AppColors_1.AppColors.dividerColor }}>
652
+ <react_native_1.Text style={{ fontFamily: AppFonts_1.AppFonts.interRegular, fontSize: 9.5, color: AppColors_1.AppColors.grayTextWeak }}>Active CPU Cores</react_native_1.Text>
653
+ <react_native_1.Text style={{ fontFamily: AppFonts_1.AppFonts.interBold, fontSize: 13, color: AppColors_1.AppColors.skyBlue, marginTop: 2 }}>
654
+ {systemMetrics ? `${systemMetrics.activeCpuCores} Cores` : '6 Cores'}
655
+ </react_native_1.Text>
656
+ </react_native_1.View>
657
+ </react_native_1.View>
658
+ </react_native_1.View>
659
+
569
660
  {/* ── 2. FRAME LATENCY DISTRIBUTION HISTOGRAM CARD ── */}
570
661
  <react_native_1.View style={perfStyles.budgetCard}>
571
662
  <react_native_1.View style={perfStyles.budgetHeader}>
@@ -1107,6 +1198,8 @@ const perfStyles = react_native_1.StyleSheet.create({
1107
1198
  height: 64,
1108
1199
  marginTop: 6,
1109
1200
  position: 'relative',
1201
+ overflow: 'hidden',
1202
+ borderRadius: 8,
1110
1203
  },
1111
1204
  svgTouchOverlay: {
1112
1205
  position: 'absolute',
@@ -1014,30 +1014,30 @@ const reduxDetailStyles = react_native_1.StyleSheet.create({
1014
1014
  flexDirection: 'row',
1015
1015
  alignItems: 'center',
1016
1016
  gap: 3.5,
1017
- backgroundColor: '#ECFDF5',
1017
+ backgroundColor: AppColors_1.AppColors.mintGreenBg,
1018
1018
  paddingHorizontal: 6,
1019
1019
  paddingVertical: 2,
1020
1020
  borderRadius: 4,
1021
1021
  borderWidth: 1,
1022
- borderColor: '#A7F3D0',
1022
+ borderColor: AppColors_1.AppColors.mintGreenBorder,
1023
1023
  },
1024
1024
  persistedChipText: {
1025
1025
  fontFamily: AppFonts_1.AppFonts.interBold,
1026
1026
  fontSize: 9,
1027
- color: '#047857',
1027
+ color: AppColors_1.AppColors.mintGreenText,
1028
1028
  },
1029
1029
  inMemoryChip: {
1030
- backgroundColor: '#EEF2FF',
1030
+ backgroundColor: AppColors_1.AppColors.indigo50,
1031
1031
  paddingHorizontal: 6,
1032
1032
  paddingVertical: 2,
1033
1033
  borderRadius: 4,
1034
1034
  borderWidth: 1,
1035
- borderColor: '#C7D2FE',
1035
+ borderColor: AppColors_1.AppColors.blueBorder,
1036
1036
  },
1037
1037
  inMemoryChipText: {
1038
1038
  fontFamily: AppFonts_1.AppFonts.interBold,
1039
1039
  fontSize: 9,
1040
- color: '#4338CA',
1040
+ color: AppColors_1.AppColors.indigo600Alt,
1041
1041
  },
1042
1042
  subStatsRow: {
1043
1043
  flexDirection: 'row',
@@ -1050,12 +1050,17 @@ const reduxDetailStyles = react_native_1.StyleSheet.create({
1050
1050
  alignItems: 'center',
1051
1051
  gap: 4,
1052
1052
  backgroundColor: AppColors_1.AppColors.grayBackground,
1053
- paddingHorizontal: 7,
1054
- paddingVertical: 3.5,
1055
- borderRadius: 6,
1053
+ paddingHorizontal: 6,
1054
+ paddingVertical: 2.5,
1055
+ borderRadius: 5,
1056
1056
  borderWidth: 1,
1057
1057
  borderColor: AppColors_1.AppColors.dividerColor,
1058
1058
  },
1059
+ statPillText: {
1060
+ fontFamily: AppFonts_1.AppFonts.interMedium,
1061
+ fontSize: 10,
1062
+ color: AppColors_1.AppColors.grayText,
1063
+ },
1059
1064
  statLabel: {
1060
1065
  fontFamily: AppFonts_1.AppFonts.interBold,
1061
1066
  fontSize: 10,
@@ -1105,17 +1110,17 @@ const reduxDetailStyles = react_native_1.StyleSheet.create({
1105
1110
  gap: 8,
1106
1111
  },
1107
1112
  actionBadge: {
1108
- backgroundColor: '#EEF2FF',
1113
+ backgroundColor: AppColors_1.AppColors.indigo50,
1109
1114
  paddingHorizontal: 6,
1110
1115
  paddingVertical: 2,
1111
1116
  borderRadius: 4,
1112
1117
  borderWidth: 1,
1113
- borderColor: '#C7D2FE',
1118
+ borderColor: AppColors_1.AppColors.blueBorder,
1114
1119
  },
1115
1120
  actionBadgeText: {
1116
1121
  fontFamily: AppFonts_1.AppFonts.interBold,
1117
1122
  fontSize: 9.5,
1118
- color: '#4338CA',
1123
+ color: AppColors_1.AppColors.indigo600Alt,
1119
1124
  },
1120
1125
  actionTitle: {
1121
1126
  flex: 1,
@@ -1196,22 +1201,22 @@ const reduxDetailStyles = react_native_1.StyleSheet.create({
1196
1201
  textAlign: 'right',
1197
1202
  },
1198
1203
  persistedInfoCard: {
1199
- backgroundColor: '#ECFDF5',
1204
+ backgroundColor: AppColors_1.AppColors.mintGreenBg,
1200
1205
  borderRadius: 8,
1201
1206
  borderWidth: 1,
1202
- borderColor: '#A7F3D0',
1207
+ borderColor: AppColors_1.AppColors.mintGreenBorder,
1203
1208
  padding: 10,
1204
1209
  },
1205
1210
  persistedInfoTitle: {
1206
1211
  fontFamily: AppFonts_1.AppFonts.interBold,
1207
1212
  fontSize: 11,
1208
- color: '#047857',
1213
+ color: AppColors_1.AppColors.mintGreenText,
1209
1214
  },
1210
1215
  clearPersistMiniBtn: {
1211
1216
  flexDirection: 'row',
1212
1217
  alignItems: 'center',
1213
1218
  gap: 3,
1214
- backgroundColor: '#FEE2E2',
1219
+ backgroundColor: AppColors_1.AppColors.errorCardBg,
1215
1220
  paddingHorizontal: 6,
1216
1221
  paddingVertical: 2.5,
1217
1222
  borderRadius: 4,
@@ -1228,7 +1233,7 @@ const reduxDetailStyles = react_native_1.StyleSheet.create({
1228
1233
  gap: 6,
1229
1234
  backgroundColor: AppColors_1.AppColors.errorCardBg,
1230
1235
  borderWidth: 1,
1231
- borderColor: '#FEE2E2',
1236
+ borderColor: AppColors_1.AppColors.errorBorder,
1232
1237
  borderRadius: 8,
1233
1238
  paddingVertical: 10,
1234
1239
  marginTop: 14,
@@ -1262,8 +1267,8 @@ const reduxDetailStyles = react_native_1.StyleSheet.create({
1262
1267
  lineHeight: 18,
1263
1268
  },
1264
1269
  highlight: {
1265
- backgroundColor: '#FEF08A',
1266
- color: '#854D0E',
1270
+ backgroundColor: AppColors_1.AppColors.yellowHighlight,
1271
+ color: AppColors_1.AppColors.amber800Warm,
1267
1272
  fontFamily: AppFonts_1.AppFonts.interBold,
1268
1273
  },
1269
1274
  });
@@ -214,10 +214,21 @@ const ReduxTab = react_1.default.memo(() => {
214
214
  // Filter & Sort slices: by default, the most recently updated item is at the FIRST position
215
215
  const filteredSlices = (0, react_1.useMemo)(() => {
216
216
  let list = sliceItems;
217
- if (reduxSearch.trim().length > 0) {
218
- const q = reduxSearch.toLowerCase();
219
- list = list.filter(item => item.name.toLowerCase().includes(q) ||
220
- (item.lastAction && item.lastAction.type.toLowerCase().includes(q)));
217
+ if (reduxSearch && reduxSearch.trim().length > 0) {
218
+ const queryTokens = reduxSearch.trim().toLowerCase().split(/\s+/).filter(Boolean);
219
+ list = list.filter(item => {
220
+ const sliceVal = reduxState ? reduxState[item.name] : null;
221
+ const searchTarget = [
222
+ item.name || '',
223
+ item.lastAction?.type || '',
224
+ item.lastAction?.payloadPreview || '',
225
+ item.lastAction?.originType || '',
226
+ item.status || '',
227
+ item.statusMessage || '',
228
+ sliceVal ? (typeof sliceVal === 'string' ? sliceVal : JSON.stringify(sliceVal)) : '',
229
+ ].join(' ').toLowerCase();
230
+ return queryTokens.every(tok => searchTarget.includes(tok));
231
+ });
221
232
  }
222
233
  return [...list].sort((a, b) => {
223
234
  if (sortMode === 'latest') {
@@ -227,12 +238,9 @@ const ReduxTab = react_1.default.memo(() => {
227
238
  }
228
239
  return a.name.localeCompare(b.name);
229
240
  }
230
- else {
231
- // Alphabetical A-Z
232
- return a.name.localeCompare(b.name);
233
- }
241
+ return a.name.localeCompare(b.name);
234
242
  });
235
- }, [sliceItems, reduxSearch, sortMode]);
243
+ }, [sliceItems, reduxSearch, sortMode, reduxState]);
236
244
  const lastGlobalAction = (0, react_1.useMemo)(() => {
237
245
  const actions = Object.values(reduxLastActionMap);
238
246
  if (actions.length === 0)
@@ -425,14 +433,16 @@ const ReduxTab = react_1.default.memo(() => {
425
433
  <react_native_1.View style={reduxTabStyles.searchRow}>
426
434
  <react_native_1.View style={styles_1.default.searchContainer}>
427
435
  <NetworkIcons_1.SearchIcon color={AppColors_1.AppColors.grayTextWeak} size={15}/>
428
- <react_native_1.TextInput placeholder="Search Redux slices or actions..." placeholderTextColor={AppColors_1.AppColors.grayTextWeak} value={reduxSearch} onChangeText={setReduxSearch} style={styles_1.default.searchInput} autoCorrect={false} autoCapitalize="none"/>
429
- {reduxSearch.length > 0 && (<react_native_1.Pressable onPress={() => setReduxSearch('')} hitSlop={10}>
436
+ <react_native_1.TextInput accessible={true} accessibilityRole="search" accessibilityLabel="Search Redux slices or actions" accessibilityHint="Type to filter state slices in real time" placeholder="Search Redux slices or actions..." placeholderTextColor={AppColors_1.AppColors.grayTextWeak} value={reduxSearch} onChangeText={setReduxSearch} style={styles_1.default.searchInput} autoCorrect={false} autoCapitalize="none"/>
437
+ {reduxSearch.length > 0 && (<react_native_1.Pressable accessible={true} accessibilityRole="button" accessibilityLabel="Clear search input" onPress={() => setReduxSearch('')} hitSlop={10}>
430
438
  <NetworkIcons_1.ClearIcon color={AppColors_1.AppColors.grayTextWeak} size={14}/>
431
439
  </react_native_1.Pressable>)}
432
440
  </react_native_1.View>
433
441
 
434
442
  {/* Pause / Resume Live Updates Button */}
435
- <react_native_1.TouchableOpacity style={[
443
+ <react_native_1.TouchableOpacity accessible={true} accessibilityRole="button" accessibilityLabel={isReduxPaused
444
+ ? 'Resume live Redux updates'
445
+ : 'Pause live Redux updates'} accessibilityHint="Toggles automatic recording of Redux state updates" style={[
436
446
  styles_1.default.toolbarBtn,
437
447
  isReduxPaused && {
438
448
  backgroundColor: `${AppColors_1.AppColors.darkOrange}1F`,
@@ -445,7 +455,9 @@ const ReduxTab = react_1.default.memo(() => {
445
455
  </react_native_1.TouchableOpacity>
446
456
 
447
457
  {/* Sort Button */}
448
- <react_native_1.TouchableOpacity style={[
458
+ <react_native_1.TouchableOpacity accessible={true} accessibilityRole="button" accessibilityLabel={`Sort order: ${sortMode === 'latest'
459
+ ? 'Recently updated first'
460
+ : 'Alphabetical A to Z'}`} accessibilityHint="Switches sorting between recently updated and alphabetical" style={[
449
461
  styles_1.default.toolbarBtn,
450
462
  sortMode === 'latest' && { backgroundColor: `${AppColors_1.AppColors.brandPurple}1F`, borderColor: AppColors_1.AppColors.brandPurple },
451
463
  ]} onPress={() => setSortMode(prev => (prev === 'latest' ? 'alpha' : 'latest'))} hitSlop={6}>
@@ -472,7 +484,7 @@ const ReduxTab = react_1.default.memo(() => {
472
484
  </react_native_1.View>
473
485
 
474
486
  {/* ─── FlatList of Slices ─── */}
475
- <react_native_1.FlatList data={filteredSlices} keyExtractor={item => item.id} renderItem={renderSliceItem} initialNumToRender={15} maxToRenderPerBatch={10} windowSize={7} removeClippedSubviews={true} ListEmptyComponent={<EmptyState_1.default isSearch={reduxSearch.length > 0}/>} ListFooterComponent={filteredSlices.length > 0 ? (<EndOfListFooter_1.default count={filteredSlices.length} label="slices"/>) : null} contentContainerStyle={[
487
+ <react_native_1.FlatList data={filteredSlices} keyExtractor={item => item.id} renderItem={renderSliceItem} initialNumToRender={12} maxToRenderPerBatch={8} windowSize={5} removeClippedSubviews={true} renderToHardwareTextureAndroid={true} ListEmptyComponent={<EmptyState_1.default isSearch={reduxSearch.length > 0}/>} ListFooterComponent={filteredSlices.length > 0 ? (<EndOfListFooter_1.default count={filteredSlices.length} label="slices"/>) : null} contentContainerStyle={[
476
488
  reduxTabStyles.listContent,
477
489
  filteredSlices.length === 0 && { flexGrow: 1 },
478
490
  ]} keyboardShouldPersistTaps="handled"/>
@@ -577,7 +589,7 @@ const reduxTabStyles = react_native_1.StyleSheet.create({
577
589
  },
578
590
  cardRecentlyUpdated: {
579
591
  borderColor: `${AppColors_1.AppColors.brandPurple}66`,
580
- backgroundColor: '#FAFAFF',
592
+ backgroundColor: AppColors_1.AppColors.purpleTintBg,
581
593
  },
582
594
  cardHeader: {
583
595
  flexDirection: 'row',
@@ -586,17 +598,17 @@ const reduxTabStyles = react_native_1.StyleSheet.create({
586
598
  marginBottom: 8,
587
599
  },
588
600
  sliceBadge: {
589
- backgroundColor: '#EDE9FE',
601
+ backgroundColor: AppColors_1.AppColors.violetSoftBg,
590
602
  paddingHorizontal: 6,
591
603
  paddingVertical: 3,
592
604
  borderRadius: 5,
593
605
  borderWidth: 1,
594
- borderColor: '#DDD6FE',
606
+ borderColor: AppColors_1.AppColors.violetSoftBorder,
595
607
  },
596
608
  sliceBadgeText: {
597
609
  fontFamily: AppFonts_1.AppFonts.interBold,
598
610
  fontSize: 9.5,
599
- color: '#6D28D9',
611
+ color: AppColors_1.AppColors.purple700,
600
612
  letterSpacing: 0.4,
601
613
  },
602
614
  sliceName: {