react-native-inapp-inspector 1.1.3 → 1.1.5

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.
package/dist/esm/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import React, { useEffect, useMemo, useRef, useState, useCallback } from 'react';
2
- import { Alert, Animated, StyleSheet, FlatList, LayoutAnimation, Modal, PanResponder, Platform, Pressable, ScrollView, Text, TextInput, View, Linking, Image, InteractionManager, ActivityIndicator, StatusBar, TouchableOpacity, UIManager, LogBox, } from 'react-native';
2
+ import { Alert, Animated, StyleSheet, FlatList, LayoutAnimation, Modal, PanResponder, Platform, Pressable, ScrollView, Text, TextInput, View, Linking, Image, InteractionManager, ActivityIndicator, StatusBar, TouchableOpacity, UIManager, LogBox, SafeAreaView, } from 'react-native';
3
3
  import Svg, { Circle, Path } from 'react-native-svg';
4
4
  import LinearGradient from 'react-native-linear-gradient';
5
5
  import { useNavigationState, NavigationContext } from '@react-navigation/native';
@@ -24,7 +24,7 @@ import AnimatedEntrance from './components/AnimatedEntrance';
24
24
  // Helpers
25
25
  import { formatDateTime, getStatusColor, getNavigationInfo, deduplicateLogs, getDomainColor, formatDisplayUrl, getFetchCommand, getCurlCommand, getSize, } from './helpers';
26
26
  // #5 — settings persistence
27
- import { loadSettings, saveSettings } from './helpers/settingsStore';
27
+ import { loadSettings, saveSettings, setCustomStorage, isPersistentStorageAvailable, clearPersistedSettings, } from './helpers/settingsStore';
28
28
  // Assets
29
29
  import { EmptyRadarIcon, FailIcon, SearchIcon, ScreenIcon, ClearIcon, SortArrowIcon, FilterIcon, InsightsIcon, GlobeIcon, DownloadIcon, CloseWhite, TrashIcon, WhiteBackNavigation, TerminalIcon, SignalIcon, AnalyticsIcon, SunIcon, MoonIcon, BrandCircleIcon, BrandSquareIcon, HtmlIcon, CssIcon, JsIcon, ClockIcon, EyeIcon, CheckIcon, SettingsIcon, RequestIcon, ResponseIcon, HeadersIcon, StatusIcon, ChevronIcon, WipeIcon, LayersIcon, UserIcon, InfoCircleIcon, WarningTriangleIcon, ErrorCircleIcon, TrendingUpIcon, } from './components/NetworkIcons';
30
30
  import ErrorBoundary from './components/ErrorBoundary';
@@ -113,7 +113,9 @@ const NavigationTracker = ({ onStateChange }) => {
113
113
  const animateNextLayout = () => {
114
114
  LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
115
115
  };
116
- const NetworkInspector = ({ enabled = true, }) => {
116
+ const NetworkInspector = ({ enabled = true, storage, }) => {
117
+ // Set custom storage synchronously during render phase
118
+ setCustomStorage(storage || null);
117
119
  const [isDark, setIsDark] = useState(false);
118
120
  const [reduxState, setReduxState] = useState(null);
119
121
  // Action timeline + per-reducer last action are kept in component state so the
@@ -195,11 +197,11 @@ const NetworkInspector = ({ enabled = true, }) => {
195
197
  const visibleConsoleLogs = useMemo(() => {
196
198
  const filtered = consoleLogs.filter(log => {
197
199
  const type = log.type;
198
- if (type === 'info' && !showConsoleLevels.info)
200
+ if (type === 'info' && !showConsoleLevels?.info)
199
201
  return false;
200
- if (type === 'warn' && !showConsoleLevels.warn)
202
+ if (type === 'warn' && !showConsoleLevels?.warn)
201
203
  return false;
202
- if (type === 'error' && !showConsoleLevels.error)
204
+ if (type === 'error' && !showConsoleLevels?.error)
203
205
  return false;
204
206
  const message = log.message || '';
205
207
  const allPrefixes = [
@@ -229,9 +231,9 @@ const NetworkInspector = ({ enabled = true, }) => {
229
231
  insights: true,
230
232
  apis: true,
231
233
  logs: true,
232
- analytics: true,
233
- webview: true,
234
- redux: true,
234
+ analytics: false,
235
+ webview: false,
236
+ redux: false,
235
237
  });
236
238
  const [maxNetworkLogs, setMaxNetworkLogs] = useState(100);
237
239
  const [webViewCaptureCssJs, setWebViewCaptureCssJs] = useState(true);
@@ -241,9 +243,36 @@ const NetworkInspector = ({ enabled = true, }) => {
241
243
  const [insightsShowConsoleAlerts, setInsightsShowConsoleAlerts] = useState(true);
242
244
  // #6 — tab the inspector opens on. Shown with a DEFAULT badge in Settings.
243
245
  const [defaultTab, setDefaultTab] = useState('apis');
244
- // #9 — when false (default), consecutive identical entries in the API and
245
- // Console lists are collapsed into one row with a ×N counter.
246
246
  const [showDuplicateLogs, setShowDuplicateLogs] = useState(false);
247
+ const resetToDefaults = async () => {
248
+ await clearPersistedSettings();
249
+ setIsDark(false);
250
+ toggleGlobalTheme(false);
251
+ setModalHeightPercent(90);
252
+ setTabVisibility({
253
+ insights: true,
254
+ apis: true,
255
+ logs: true,
256
+ analytics: false,
257
+ webview: false,
258
+ redux: false,
259
+ });
260
+ setDefaultTab('apis');
261
+ setMaxNetworkLogs(100);
262
+ setMaxConsoleLogs(200);
263
+ setShowConsoleLevels({
264
+ info: true,
265
+ warn: true,
266
+ error: true,
267
+ });
268
+ setWebViewCaptureCssJs(true);
269
+ setReduxAutoRefreshState(true);
270
+ setReduxExpandDepth(1);
271
+ setSlowRequestThreshold(1000);
272
+ setInsightsShowConsoleAlerts(true);
273
+ setShowDuplicateLogs(false);
274
+ Alert.alert('Settings Reset', 'All settings have been reset to default values.');
275
+ };
247
276
  // #5 — hydrate persisted settings once, then auto-save on any change.
248
277
  const settingsHydratedRef = useRef(false);
249
278
  useEffect(() => {
@@ -290,9 +319,9 @@ const NetworkInspector = ({ enabled = true, }) => {
290
319
  insights: true,
291
320
  apis: true,
292
321
  logs: true,
293
- analytics: true,
294
- webview: true,
295
- redux: true,
322
+ analytics: false,
323
+ webview: false,
324
+ redux: false,
296
325
  },
297
326
  ...(saved.tabVisibility || {}),
298
327
  apis: true,
@@ -619,7 +648,7 @@ const NetworkInspector = ({ enabled = true, }) => {
619
648
  // #6 — every time the inspector is opened, land on the chosen default tab.
620
649
  useEffect(() => {
621
650
  if (visible) {
622
- const target = defaultTab === 'apis' || tabVisibility[defaultTab]
651
+ const target = defaultTab === 'apis' || tabVisibility?.[defaultTab]
623
652
  ? defaultTab
624
653
  : 'apis';
625
654
  setActiveTab(target);
@@ -1289,7 +1318,8 @@ const NetworkInspector = ({ enabled = true, }) => {
1289
1318
  return (<View style={{ flex: 1, backgroundColor: AppColors.grayBackground }}>
1290
1319
  {/* Settings Header with back button */}
1291
1320
  <LinearGradient colors={[AppColors.purple, '#6B4EFF']} start={{ x: 0, y: 0 }} end={{ x: 1, y: 0 }} style={styles.headerGradient}>
1292
- <View style={[styles.header, { paddingHorizontal: 16, gap: 12 }]}>
1321
+ <SafeAreaView style={{ width: '100%' }}>
1322
+ <View style={[styles.header, { paddingHorizontal: 16, gap: 12 }]}>
1293
1323
  <TouchableScale onPress={() => {
1294
1324
  animateNextLayout();
1295
1325
  setSettingsPage(null);
@@ -1337,7 +1367,8 @@ const NetworkInspector = ({ enabled = true, }) => {
1337
1367
  </Text>
1338
1368
  </View>
1339
1369
  </View>
1340
- </LinearGradient>
1370
+ </SafeAreaView>
1371
+ </LinearGradient>
1341
1372
 
1342
1373
  <ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16, gap: 12 }}>
1343
1374
  {/* Tab list */}
@@ -1382,7 +1413,7 @@ const NetworkInspector = ({ enabled = true, }) => {
1382
1413
  </View>
1383
1414
 
1384
1415
  {settingsTabs.map((tab, idx) => {
1385
- const isVisible = tab.key === 'apis' || tabVisibility[tab.key];
1416
+ const isVisible = tab.key === 'apis' || tabVisibility?.[tab.key];
1386
1417
  const isLast = idx === settingsTabs.length - 1;
1387
1418
  const isLocked = tab.key === 'apis';
1388
1419
  return (<View key={tab.key} style={{
@@ -1762,40 +1793,63 @@ const NetworkInspector = ({ enabled = true, }) => {
1762
1793
  </View>
1763
1794
  </View>
1764
1795
 
1765
- {/* Segmented picker only visible tabs are offered */}
1796
+ {/* Grid of Default Tab Cards */}
1766
1797
  <View style={{
1767
1798
  flexDirection: 'row',
1768
1799
  flexWrap: 'wrap',
1769
- backgroundColor: AppColors.grayBackground,
1770
- borderRadius: 8,
1771
- padding: 2.5,
1772
- marginTop: 10,
1773
- borderWidth: 1,
1774
- borderColor: AppColors.dividerColor,
1775
- gap: 2,
1800
+ gap: 8,
1801
+ marginTop: 12,
1776
1802
  }}>
1777
1803
  {settingsTabs
1778
- .filter(tab => tab.key === 'apis' || tabVisibility[tab.key])
1804
+ .filter(tab => tab.key === 'apis' || tabVisibility?.[tab.key])
1779
1805
  .map(tab => {
1780
1806
  const isActive = defaultTab === tab.key;
1781
1807
  return (<TouchableScale key={tab.key} onPress={() => setDefaultTab(tab.key)} style={{
1782
- paddingVertical: 6,
1783
- paddingHorizontal: 10,
1808
+ flexDirection: 'row',
1784
1809
  alignItems: 'center',
1810
+ gap: 8,
1811
+ paddingVertical: 10,
1812
+ paddingHorizontal: 12,
1813
+ borderRadius: 10,
1814
+ borderWidth: 1.5,
1815
+ borderColor: isActive ? AppColors.purple : AppColors.grayBorderSecondary,
1816
+ backgroundColor: isActive ? 'rgba(104,75,155,0.06)' : AppColors.primaryLight,
1817
+ minWidth: '47%',
1818
+ flex: 1,
1819
+ }}>
1820
+ <View style={{
1821
+ width: 22,
1822
+ height: 22,
1785
1823
  borderRadius: 6,
1786
- backgroundColor: isActive
1787
- ? AppColors.purple
1788
- : 'transparent',
1824
+ backgroundColor: isActive ? AppColors.purple : AppColors.purpleShade50,
1825
+ alignItems: 'center',
1826
+ justifyContent: 'center',
1789
1827
  }}>
1828
+ {tab.icon === 'insights' && (<InsightsIcon color={isActive ? '#FFFFFF' : AppColors.purple} size={11}/>)}
1829
+ {tab.icon === 'apis' && (<SignalIcon color={isActive ? '#FFFFFF' : AppColors.purple} size={11}/>)}
1830
+ {tab.icon === 'logs' && (<TerminalIcon color={isActive ? '#FFFFFF' : AppColors.purple} size={11}/>)}
1831
+ {tab.icon === 'analytics' && (<AnalyticsIcon color={isActive ? '#FFFFFF' : AppColors.purple} size={11}/>)}
1832
+ {tab.icon === 'webview' && (<GlobeIcon color={isActive ? '#FFFFFF' : AppColors.purple} size={11}/>)}
1833
+ {tab.icon === 'redux' && (<TerminalIcon color={isActive ? '#FFFFFF' : AppColors.purple} size={11}/>)}
1834
+ </View>
1790
1835
  <Text style={{
1791
1836
  fontFamily: AppFonts.interBold,
1792
- fontSize: 11,
1793
- color: isActive
1794
- ? '#FFFFFF'
1795
- : AppColors.grayText,
1837
+ fontSize: 13,
1838
+ color: isActive ? AppColors.purple : AppColors.primaryBlack,
1839
+ flex: 1,
1796
1840
  }}>
1797
- {tab.label}
1841
+ {tab.label}
1798
1842
  </Text>
1843
+ {isActive && (<View style={{
1844
+ width: 14,
1845
+ height: 14,
1846
+ borderRadius: 7,
1847
+ backgroundColor: AppColors.purple,
1848
+ alignItems: 'center',
1849
+ justifyContent: 'center',
1850
+ }}>
1851
+ <CheckIcon size={8} color="#FFFFFF"/>
1852
+ </View>)}
1799
1853
  </TouchableScale>);
1800
1854
  })}
1801
1855
  </View>
@@ -1877,73 +1931,130 @@ const NetworkInspector = ({ enabled = true, }) => {
1877
1931
  }}/>
1878
1932
  </TouchableScale>
1879
1933
  </View>
1880
- </View>
1881
- </View>
1882
- </ScrollView>
1883
- </View>);
1884
- }
1885
- const goBackToMain = () => {
1886
- animateNextLayout();
1887
- setSettingsPage('main');
1888
- };
1889
- const renderSubHeader = (title, icon, rightInfo) => (<View style={{
1890
- flexDirection: 'row',
1891
- alignItems: 'center',
1892
- gap: 12,
1893
- paddingBottom: 16,
1894
- borderBottomWidth: 1,
1895
- borderBottomColor: AppColors.dividerColor,
1896
- marginBottom: 16,
1897
- }}>
1898
- <TouchableScale onPress={goBackToMain} style={{
1899
- width: 36,
1900
- height: 36,
1901
- borderRadius: 10,
1902
- backgroundColor: AppColors.purpleShade50,
1903
- borderWidth: 1,
1904
- borderColor: 'rgba(104,75,155,0.2)',
1905
- alignItems: 'center',
1906
- justifyContent: 'center',
1907
- }}>
1908
- <WhiteBackNavigation color={AppColors.purple} size={16}/>
1909
- </TouchableScale>
1910
- {icon && (<View style={{
1911
- width: 36,
1912
- height: 36,
1913
- borderRadius: 10,
1914
- backgroundColor: AppColors.purpleShade50,
1934
+
1935
+ {/* Divider */}
1936
+ <View style={{
1937
+ height: 1,
1938
+ backgroundColor: AppColors.dividerColor,
1939
+ }}/>
1940
+
1941
+ {/* Reset Settings */}
1942
+ <View style={{
1943
+ flexDirection: 'row',
1944
+ alignItems: 'center',
1945
+ paddingVertical: 12,
1946
+ paddingHorizontal: 14,
1947
+ gap: 12,
1948
+ }}>
1949
+ <View style={{
1950
+ flex: 1,
1951
+ flexDirection: 'row',
1952
+ alignItems: 'center',
1953
+ gap: 8,
1954
+ }}>
1955
+ <View style={{
1956
+ width: 20,
1957
+ height: 20,
1958
+ borderRadius: 6,
1959
+ backgroundColor: 'rgba(239,68,68,0.08)',
1915
1960
  borderWidth: 1,
1916
- borderColor: 'rgba(104,75,155,0.2)',
1961
+ borderColor: 'rgba(239,68,68,0.2)',
1917
1962
  alignItems: 'center',
1918
1963
  justifyContent: 'center',
1919
1964
  }}>
1920
- {icon}
1921
- </View>)}
1922
- <Text style={{
1923
- fontFamily: AppFonts.interBold,
1924
- fontSize: 18,
1925
- color: AppColors.primaryBlack,
1926
- flex: 1,
1927
- }}>
1928
- {title}
1929
- </Text>
1930
- {rightInfo ? (<View style={{
1931
- backgroundColor: 'rgba(104,75,155,0.08)',
1965
+ <TrashIcon color={AppColors.errorColor} size={11}/>
1966
+ </View>
1967
+ <View style={{ flex: 1 }}>
1968
+ <Text style={{
1969
+ fontFamily: AppFonts.interBold,
1970
+ fontSize: 13,
1971
+ color: AppColors.primaryBlack,
1972
+ }}>
1973
+ Reset Settings
1974
+ </Text>
1975
+ <Text style={{
1976
+ fontFamily: AppFonts.interRegular,
1977
+ fontSize: 11,
1978
+ color: AppColors.grayText,
1979
+ marginTop: 1,
1980
+ }}>
1981
+ Wipe custom configurations and load package defaults
1982
+ </Text>
1983
+ </View>
1984
+ </View>
1985
+
1986
+ <TouchableScale onPress={resetToDefaults} style={{
1932
1987
  paddingHorizontal: 10,
1933
1988
  paddingVertical: 5,
1934
- borderRadius: 8,
1989
+ borderRadius: 7,
1990
+ backgroundColor: 'rgba(255,46,87,0.08)',
1935
1991
  borderWidth: 1,
1936
- borderColor: 'rgba(104,75,155,0.15)',
1992
+ borderColor: 'rgba(255,46,87,0.2)',
1937
1993
  }}>
1938
- <Text style={{
1994
+ <Text style={{
1939
1995
  fontFamily: AppFonts.interBold,
1940
1996
  fontSize: 11,
1941
- color: AppColors.purple,
1997
+ color: AppColors.errorColor,
1942
1998
  }}>
1943
- {rightInfo}
1944
- </Text>
1945
- </View>) : null}
1946
- </View>);
1999
+ Reset
2000
+ </Text>
2001
+ </TouchableScale>
2002
+ </View>
2003
+ </View>
2004
+ </View>
2005
+
2006
+ {/* Storage Status */}
2007
+ <View style={{
2008
+ backgroundColor: isPersistentStorageAvailable()
2009
+ ? 'rgba(74,222,128,0.08)'
2010
+ : 'rgba(234,179,8,0.08)',
2011
+ borderRadius: 12,
2012
+ borderWidth: 1,
2013
+ borderColor: isPersistentStorageAvailable()
2014
+ ? 'rgba(74,222,128,0.2)'
2015
+ : 'rgba(234,179,8,0.2)',
2016
+ padding: 12,
2017
+ marginTop: 16,
2018
+ flexDirection: 'row',
2019
+ alignItems: 'center',
2020
+ gap: 10,
2021
+ }}>
2022
+ <View style={{
2023
+ width: 8,
2024
+ height: 8,
2025
+ borderRadius: 4,
2026
+ backgroundColor: isPersistentStorageAvailable() ? '#22C55E' : '#EAB308',
2027
+ }}/>
2028
+ <View style={{ flex: 1 }}>
2029
+ <Text style={{
2030
+ fontFamily: AppFonts.interBold,
2031
+ fontSize: 12,
2032
+ color: isPersistentStorageAvailable() ? '#15803D' : '#854D0E',
2033
+ }}>
2034
+ {isPersistentStorageAvailable()
2035
+ ? `Storage: Persistent (${storage ? 'Custom' : 'iOS Settings'})`
2036
+ : 'Storage: Temporary (In-Memory)'}
2037
+ </Text>
2038
+ <Text style={{
2039
+ fontFamily: AppFonts.interRegular,
2040
+ fontSize: 10.5,
2041
+ color: isPersistentStorageAvailable() ? '#166534' : '#854D0E',
2042
+ marginTop: 2,
2043
+ opacity: 0.8,
2044
+ }}>
2045
+ {isPersistentStorageAvailable()
2046
+ ? 'Your settings are saved across app restarts.'
2047
+ : 'Settings reset when closed. To persist settings, pass a storage object to <NetworkInspector storage={...} />.'}
2048
+ </Text>
2049
+ </View>
2050
+ </View>
2051
+ </ScrollView>
2052
+ </View>);
2053
+ }
2054
+ const goBackToMain = () => {
2055
+ animateNextLayout();
2056
+ setSettingsPage('main');
2057
+ };
1947
2058
  // Helper: settings row with icon + label + optional description
1948
2059
  const renderSettingRow = (opts) => (<View style={{
1949
2060
  paddingVertical: 12,
@@ -2023,9 +2134,15 @@ const NetworkInspector = ({ enabled = true, }) => {
2023
2134
  })}
2024
2135
  </View>)}
2025
2136
  </View>);
2137
+ let content = null;
2138
+ let title = '';
2139
+ let icon = null;
2140
+ let rightInfo = '';
2026
2141
  if (settingsPage === 'apis') {
2027
- return (<ScrollView style={{ flex: 1, backgroundColor: AppColors.grayBackground }} contentContainerStyle={{ padding: 16 }}>
2028
- {renderSubHeader('APIs Settings', <SignalIcon color={AppColors.purple} size={16}/>, `Total: ${logs.length}`)}
2142
+ title = 'APIs Settings';
2143
+ icon = <SignalIcon color="#FFFFFF" size={16}/>;
2144
+ rightInfo = `Total: ${logs.length}`;
2145
+ content = (<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16 }}>
2029
2146
  <View style={{
2030
2147
  backgroundColor: AppColors.primaryLight,
2031
2148
  padding: 16,
@@ -2085,9 +2202,11 @@ const NetworkInspector = ({ enabled = true, }) => {
2085
2202
  </View>
2086
2203
  </ScrollView>);
2087
2204
  }
2088
- if (settingsPage === 'logs') {
2089
- return (<ScrollView style={{ flex: 1, backgroundColor: AppColors.grayBackground }} contentContainerStyle={{ padding: 16 }}>
2090
- {renderSubHeader('Logs Settings', <TerminalIcon color={AppColors.purple} size={16}/>, `Total: ${consoleLogs.length}`)}
2205
+ else if (settingsPage === 'logs') {
2206
+ title = 'Logs Settings';
2207
+ icon = <TerminalIcon color="#FFFFFF" size={16}/>;
2208
+ rightInfo = `Total: ${consoleLogs.length}`;
2209
+ content = (<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16 }}>
2091
2210
  <View style={{
2092
2211
  backgroundColor: AppColors.primaryLight,
2093
2212
  padding: 16,
@@ -2116,7 +2235,7 @@ const NetworkInspector = ({ enabled = true, }) => {
2116
2235
  Log Levels
2117
2236
  </Text>
2118
2237
  {['info', 'warn', 'error'].map((level, li) => {
2119
- const isLvlActive = showConsoleLevels[level];
2238
+ const isLvlActive = showConsoleLevels?.[level];
2120
2239
  const levelColor = level === 'error'
2121
2240
  ? AppColors.errorColor
2122
2241
  : level === 'warn'
@@ -2197,9 +2316,11 @@ const NetworkInspector = ({ enabled = true, }) => {
2197
2316
  </View>
2198
2317
  </ScrollView>);
2199
2318
  }
2200
- if (settingsPage === 'analytics') {
2201
- return (<ScrollView style={{ flex: 1, backgroundColor: AppColors.grayBackground }} contentContainerStyle={{ padding: 16 }}>
2202
- {renderSubHeader('Analytics Settings', <AnalyticsIcon color={AppColors.purple} size={16}/>, `Events: ${analyticsEvents.length}`)}
2319
+ else if (settingsPage === 'analytics') {
2320
+ title = 'Analytics Settings';
2321
+ icon = <AnalyticsIcon color="#FFFFFF" size={16}/>;
2322
+ rightInfo = `Events: ${analyticsEvents.length}`;
2323
+ content = (<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16 }}>
2203
2324
  <View style={{
2204
2325
  backgroundColor: AppColors.primaryLight,
2205
2326
  padding: 16,
@@ -2253,9 +2374,11 @@ const NetworkInspector = ({ enabled = true, }) => {
2253
2374
  </View>
2254
2375
  </ScrollView>);
2255
2376
  }
2256
- if (settingsPage === 'webview') {
2257
- return (<ScrollView style={{ flex: 1, backgroundColor: AppColors.grayBackground }} contentContainerStyle={{ padding: 16 }}>
2258
- {renderSubHeader('WebView Settings', <GlobeIcon color={AppColors.purple} size={16}/>, `History: ${webViewNavHistory.length}`)}
2377
+ else if (settingsPage === 'webview') {
2378
+ title = 'WebView Settings';
2379
+ icon = <GlobeIcon color="#FFFFFF" size={16}/>;
2380
+ rightInfo = `History: ${webViewNavHistory.length}`;
2381
+ content = (<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16 }}>
2259
2382
  <View style={{
2260
2383
  backgroundColor: AppColors.primaryLight,
2261
2384
  padding: 16,
@@ -2325,9 +2448,11 @@ const NetworkInspector = ({ enabled = true, }) => {
2325
2448
  </View>
2326
2449
  </ScrollView>);
2327
2450
  }
2328
- if (settingsPage === 'redux') {
2329
- return (<ScrollView style={{ flex: 1, backgroundColor: AppColors.grayBackground }} contentContainerStyle={{ padding: 16 }}>
2330
- {renderSubHeader('Redux Settings', <TerminalIcon color={AppColors.purple} size={16}/>, `Reducers: ${Object.keys(reduxState || {}).length}`)}
2451
+ else if (settingsPage === 'redux') {
2452
+ title = 'Redux Settings';
2453
+ icon = <TerminalIcon color="#FFFFFF" size={16}/>;
2454
+ rightInfo = `Reducers: ${Object.keys(reduxState || {}).length}`;
2455
+ content = (<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16 }}>
2331
2456
  <View style={{
2332
2457
  backgroundColor: AppColors.primaryLight,
2333
2458
  padding: 16,
@@ -2411,53 +2536,108 @@ const NetworkInspector = ({ enabled = true, }) => {
2411
2536
  </View>
2412
2537
  </ScrollView>);
2413
2538
  }
2414
- // Default return page is Insights settings
2415
- return (<ScrollView style={{ flex: 1, backgroundColor: AppColors.grayBackground }} contentContainerStyle={{ padding: 16 }}>
2416
- {renderSubHeader('Insights Settings', <InsightsIcon color={AppColors.purple} size={16}/>)}
2417
- <View style={{
2418
- backgroundColor: AppColors.primaryLight,
2419
- padding: 16,
2420
- borderRadius: 12,
2539
+ else {
2540
+ title = 'Insights Settings';
2541
+ icon = <InsightsIcon color="#FFFFFF" size={16}/>;
2542
+ content = (<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16 }}>
2543
+ <View style={{
2544
+ backgroundColor: AppColors.primaryLight,
2545
+ padding: 16,
2546
+ borderRadius: 12,
2547
+ borderWidth: 1,
2548
+ borderColor: AppColors.grayBorderSecondary,
2549
+ gap: 4,
2550
+ }}>
2551
+ {renderSettingRow({
2552
+ icon: <SignalIcon color={AppColors.purple} size={16}/>,
2553
+ label: 'Slow Latency Warning',
2554
+ description: 'Alert threshold for slow API request duration',
2555
+ picker: {
2556
+ options: [500, 1000, 2000],
2557
+ selectedValue: slowRequestThreshold,
2558
+ onSelect: setSlowRequestThreshold,
2559
+ },
2560
+ })}
2561
+ <View style={{ height: 1, backgroundColor: AppColors.dividerColor }}/>
2562
+ {renderSettingRow({
2563
+ icon: <TerminalIcon color={AppColors.purple} size={16}/>,
2564
+ label: 'Show Console Alerts',
2565
+ description: 'Flags critical warnings or crash events on dashboard',
2566
+ isLast: true,
2567
+ onPress: () => setInsightsShowConsoleAlerts(prev => !prev),
2568
+ right: (<View style={{
2569
+ width: 22,
2570
+ height: 22,
2571
+ borderRadius: 6,
2572
+ borderWidth: 2,
2573
+ borderColor: insightsShowConsoleAlerts
2574
+ ? AppColors.purple
2575
+ : AppColors.grayTextWeak,
2576
+ backgroundColor: insightsShowConsoleAlerts
2577
+ ? 'rgba(104, 75, 155, 0.1)'
2578
+ : 'transparent',
2579
+ alignItems: 'center',
2580
+ justifyContent: 'center',
2581
+ }}>
2582
+ {insightsShowConsoleAlerts && (<CheckIcon size={12} color={AppColors.purple}/>)}
2583
+ </View>),
2584
+ })}
2585
+ </View>
2586
+ </ScrollView>);
2587
+ }
2588
+ return (<View style={{ flex: 1, backgroundColor: AppColors.grayBackground }}>
2589
+ <LinearGradient colors={[AppColors.purple, '#6B4EFF']} start={{ x: 0, y: 0 }} end={{ x: 1, y: 0 }} style={styles.headerGradient}>
2590
+ <SafeAreaView style={{ width: '100%' }}>
2591
+ <View style={[styles.header, { paddingHorizontal: 16, gap: 12 }]}>
2592
+ <TouchableScale onPress={goBackToMain} hitSlop={12} style={{
2593
+ padding: 8,
2594
+ borderRadius: 10,
2595
+ backgroundColor: 'rgba(255, 255, 255, 0.15)',
2421
2596
  borderWidth: 1,
2422
- borderColor: AppColors.grayBorderSecondary,
2423
- gap: 4,
2597
+ borderColor: 'rgba(255, 255, 255, 0.08)',
2424
2598
  }}>
2425
- {renderSettingRow({
2426
- icon: <SignalIcon color={AppColors.purple} size={16}/>,
2427
- label: 'Slow Latency Warning',
2428
- description: 'Alert threshold for slow API request duration',
2429
- picker: {
2430
- options: [500, 1000, 2000],
2431
- selectedValue: slowRequestThreshold,
2432
- onSelect: setSlowRequestThreshold,
2433
- },
2434
- })}
2435
- <View style={{ height: 1, backgroundColor: AppColors.dividerColor }}/>
2436
- {renderSettingRow({
2437
- icon: <TerminalIcon color={AppColors.purple} size={16}/>,
2438
- label: 'Show Console Alerts',
2439
- description: 'Flags critical warnings or crash events on dashboard',
2440
- isLast: true,
2441
- onPress: () => setInsightsShowConsoleAlerts(prev => !prev),
2442
- right: (<View style={{
2443
- width: 22,
2444
- height: 22,
2445
- borderRadius: 6,
2446
- borderWidth: 2,
2447
- borderColor: insightsShowConsoleAlerts
2448
- ? AppColors.purple
2449
- : AppColors.grayTextWeak,
2450
- backgroundColor: insightsShowConsoleAlerts
2451
- ? 'rgba(104, 75, 155, 0.1)'
2452
- : 'transparent',
2453
- alignItems: 'center',
2454
- justifyContent: 'center',
2455
- }}>
2456
- {insightsShowConsoleAlerts && (<CheckIcon size={12} color={AppColors.purple}/>)}
2457
- </View>),
2458
- })}
2459
- </View>
2460
- </ScrollView>);
2599
+ <WhiteBackNavigation color="#FFFFFF" size={16}/>
2600
+ </TouchableScale>
2601
+ {icon && (<View style={{
2602
+ width: 30,
2603
+ height: 30,
2604
+ borderRadius: 8,
2605
+ backgroundColor: 'rgba(255, 255, 255, 0.15)',
2606
+ alignItems: 'center',
2607
+ justifyContent: 'center',
2608
+ }}>
2609
+ {icon}
2610
+ </View>)}
2611
+ <View style={{ flex: 1 }}>
2612
+ <Text style={{
2613
+ fontFamily: AppFonts.interBold,
2614
+ fontSize: 17,
2615
+ color: '#FFFFFF',
2616
+ }}>
2617
+ {title}
2618
+ </Text>
2619
+ </View>
2620
+ {rightInfo ? (<View style={{
2621
+ backgroundColor: 'rgba(255, 255, 255, 0.15)',
2622
+ paddingHorizontal: 8,
2623
+ paddingVertical: 4,
2624
+ borderRadius: 8,
2625
+ borderWidth: 1,
2626
+ borderColor: 'rgba(255, 255, 255, 0.1)',
2627
+ }}>
2628
+ <Text style={{
2629
+ fontFamily: AppFonts.interBold,
2630
+ fontSize: 11,
2631
+ color: '#FFFFFF',
2632
+ }}>
2633
+ {rightInfo}
2634
+ </Text>
2635
+ </View>) : null}
2636
+ </View>
2637
+ </SafeAreaView>
2638
+ </LinearGradient>
2639
+ {content}
2640
+ </View>);
2461
2641
  };
2462
2642
  const renderInsightsDashboard = () => {
2463
2643
  const apiTotal = logs.length;
@@ -2493,11 +2673,11 @@ const NetworkInspector = ({ enabled = true, }) => {
2493
2673
  const totalSignals = apiTotal + logTotal + analyticsTotal + webviewTotal;
2494
2674
  const totalIssues = apiErrors + logErrors;
2495
2675
  const activeModules = [
2496
- tabVisibility.apis,
2497
- tabVisibility.logs,
2498
- tabVisibility.analytics,
2499
- tabVisibility.webview,
2500
- tabVisibility.redux,
2676
+ tabVisibility?.apis,
2677
+ tabVisibility?.logs,
2678
+ tabVisibility?.analytics,
2679
+ tabVisibility?.webview,
2680
+ tabVisibility?.redux,
2501
2681
  ].filter(Boolean).length;
2502
2682
  // Composite health score: success rate penalised by error volume and slow requests.
2503
2683
  const healthScore = totalSignals === 0
@@ -2664,7 +2844,7 @@ const NetworkInspector = ({ enabled = true, }) => {
2664
2844
  </View>
2665
2845
 
2666
2846
  {/* Module 1: APIs */}
2667
- {tabVisibility.apis && (<TouchableScale style={styles.dashboardModuleCard} onPress={() => switchActiveTab('apis')}>
2847
+ {tabVisibility?.apis && (<TouchableScale style={styles.dashboardModuleCard} onPress={() => switchActiveTab('apis')}>
2668
2848
  <View style={styles.dashboardModuleHeader}>
2669
2849
  <View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
2670
2850
  <SignalIcon color={AppColors.purple} size={18}/>
@@ -2771,7 +2951,7 @@ const NetworkInspector = ({ enabled = true, }) => {
2771
2951
  </View>)}
2772
2952
  </View>
2773
2953
  </TouchableScale>)}
2774
- {tabVisibility.logs && (<TouchableScale style={styles.dashboardModuleCard} onPress={() => switchActiveTab('logs')}>
2954
+ {tabVisibility?.logs && (<TouchableScale style={styles.dashboardModuleCard} onPress={() => switchActiveTab('logs')}>
2775
2955
  <View style={styles.dashboardModuleHeader}>
2776
2956
  <View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
2777
2957
  <TerminalIcon color="#0D9488" size={18}/>
@@ -2812,7 +2992,7 @@ const NetworkInspector = ({ enabled = true, }) => {
2812
2992
  </TouchableScale>)}
2813
2993
 
2814
2994
  {/* Module 3: Analytics */}
2815
- {tabVisibility.analytics && (<TouchableScale style={styles.dashboardModuleCard} onPress={() => switchActiveTab('analytics')}>
2995
+ {tabVisibility?.analytics && (<TouchableScale style={styles.dashboardModuleCard} onPress={() => switchActiveTab('analytics')}>
2816
2996
  <View style={styles.dashboardModuleHeader}>
2817
2997
  <View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
2818
2998
  <AnalyticsIcon color="#EA580C" size={18}/>
@@ -2849,7 +3029,7 @@ const NetworkInspector = ({ enabled = true, }) => {
2849
3029
  </TouchableScale>)}
2850
3030
 
2851
3031
  {/* Module 4: WebView */}
2852
- {tabVisibility.webview && (<TouchableScale style={styles.dashboardModuleCard} onPress={() => switchActiveTab('webview')}>
3032
+ {tabVisibility?.webview && (<TouchableScale style={styles.dashboardModuleCard} onPress={() => switchActiveTab('webview')}>
2853
3033
  <View style={styles.dashboardModuleHeader}>
2854
3034
  <View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
2855
3035
  <GlobeIcon color="#2563EB" size={18}/>
@@ -2882,7 +3062,7 @@ const NetworkInspector = ({ enabled = true, }) => {
2882
3062
  </TouchableScale>)}
2883
3063
 
2884
3064
  {/* Module 5: Redux Store */}
2885
- {tabVisibility.redux && (<TouchableScale style={styles.dashboardModuleCard} onPress={() => switchActiveTab('redux')}>
3065
+ {tabVisibility?.redux && (<TouchableScale style={styles.dashboardModuleCard} onPress={() => switchActiveTab('redux')}>
2886
3066
  <View style={styles.dashboardModuleHeader}>
2887
3067
  <View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
2888
3068
  <TerminalIcon color={AppColors.purple} size={18}/>
@@ -3195,7 +3375,8 @@ const NetworkInspector = ({ enabled = true, }) => {
3195
3375
  <StatusBar translucent backgroundColor="transparent" barStyle="light-content"/>
3196
3376
 
3197
3377
  <LinearGradient colors={[AppColors.purple, '#6B4EFF']} style={styles.headerGradient}>
3198
- <View style={styles.header}>
3378
+ <SafeAreaView style={{ width: '100%' }}>
3379
+ <View style={styles.header}>
3199
3380
  <View style={[
3200
3381
  styles.headerLeft,
3201
3382
  {
@@ -3532,7 +3713,8 @@ const NetworkInspector = ({ enabled = true, }) => {
3532
3713
  </TouchableScale>
3533
3714
  </View>
3534
3715
  </View>
3535
- </LinearGradient>
3716
+ </SafeAreaView>
3717
+ </LinearGradient>
3536
3718
 
3537
3719
  {/* ─── Horizontal Scrollable Tab Bar inside Content ─── */}
3538
3720
  {selected == null &&
@@ -3577,7 +3759,7 @@ const NetworkInspector = ({ enabled = true, }) => {
3577
3759
  icon: 'redux',
3578
3760
  },
3579
3761
  ]
3580
- .filter(tab => tabVisibility[tab.key])
3762
+ .filter(tab => tabVisibility?.[tab.key])
3581
3763
  .map(tab => {
3582
3764
  const isActive = activeTab === tab.key;
3583
3765
  const iconColor = isActive
@@ -5079,13 +5261,13 @@ const NetworkInspector = ({ enabled = true, }) => {
5079
5261
  <View style={[
5080
5262
  styles.methodBadge,
5081
5263
  {
5082
- backgroundColor: `${METHOD_COLORS[selected.method] ?? METHOD_COLORS.ALL}15`,
5264
+ backgroundColor: METHOD_COLORS[selected.method] ?? METHOD_COLORS.ALL,
5083
5265
  },
5084
5266
  ]}>
5085
5267
  <Text style={[
5086
5268
  styles.methodBadgeText,
5087
5269
  {
5088
- color: METHOD_COLORS[selected.method] ?? METHOD_COLORS.ALL,
5270
+ color: '#FFFFFF',
5089
5271
  },
5090
5272
  ]}>
5091
5273
  {selected.method}