react-native-inapp-inspector 1.0.14 → 1.0.16

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, Modal, Platform, Pressable, ScrollView, Text, TextInput, View, Linking, Image, InteractionManager, ActivityIndicator, StatusBar, TouchableOpacity, LogBox, } from 'react-native';
2
+ import { Alert, Animated, StyleSheet, FlatList, LayoutAnimation, Modal, Platform, Pressable, ScrollView, Text, TextInput, View, Linking, Image, InteractionManager, ActivityIndicator, StatusBar, TouchableOpacity, UIManager, LogBox, } 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';
@@ -20,6 +20,7 @@ import SourcePageCard from './components/SourcePageCard';
20
20
  import { ConsoleLogCard } from './components/ConsoleLogCard';
21
21
  import HighlightText from './components/HighlightText';
22
22
  import CodeSnippet from './components/CodeSnippet';
23
+ import AnimatedEntrance from './components/AnimatedEntrance';
23
24
  // Helpers
24
25
  import { formatDateTime, getStatusColor, getNavigationInfo, deduplicateLogs, getDomainColor, formatDisplayUrl, getFetchCommand, getCurlCommand, getSize, } from './helpers';
25
26
  // Assets
@@ -34,7 +35,7 @@ import { setupNetworkLogger, clearNetworkLogs, subscribeNetworkLogs, } from './c
34
35
  // Console
35
36
  import { setupConsoleLogger, clearConsoleLogs, subscribeConsoleLogs, } from './customHooks/consoleLogger';
36
37
  import { IGNORED_LOG_PREFIXES } from './customHooks/logFilters';
37
- import { subscribeAnalyticsEvents, clearAnalyticsEvents, } from './customHooks/analyticsLogger';
38
+ import { subscribeAnalyticsEvents, clearAnalyticsEvents, autoSetupAnalyticsLogger, } from './customHooks/analyticsLogger';
38
39
  import AnalyticsEventCard, { getEventColor, } from './components/AnalyticsEventCard';
39
40
  import AnalyticsDetail from './components/AnalyticsDetail';
40
41
  import { getWebViewLogs, getWebViewNavHistory, getWebViewHtml, getWebViewCss, getWebViewJs, getWebViewHtmlUrl, clearWebViewData, subscribeWebView, } from './customHooks/webViewLogger';
@@ -107,7 +108,10 @@ const NavigationTracker = ({ onStateChange }) => {
107
108
  }, [navState, onStateChange]);
108
109
  return null;
109
110
  };
110
- const NetworkInspector = ({ enabled = true }) => {
111
+ const animateNextLayout = () => {
112
+ LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
113
+ };
114
+ const NetworkInspector = ({ enabled = true, }) => {
111
115
  const [isDark, setIsDark] = useState(false);
112
116
  const [reduxState, setReduxState] = useState(null);
113
117
  const [expandedReducers, setExpandedReducers] = useState({});
@@ -122,16 +126,11 @@ const NetworkInspector = ({ enabled = true }) => {
122
126
  const [reduxActiveSubTab, setReduxActiveSubTab] = useState('timeline');
123
127
  const [apiDetailActiveTab, setApiDetailActiveTab] = useState('response');
124
128
  useEffect(() => {
125
- if (enabled && visible) {
126
- LogBox.ignoreAllLogs(true);
127
- }
128
- else {
129
- LogBox.ignoreAllLogs(false);
130
- }
129
+ LogBox.ignoreAllLogs(enabled);
131
130
  return () => {
132
131
  LogBox.ignoreAllLogs(false);
133
132
  };
134
- }, [enabled, visible]);
133
+ }, [enabled]);
135
134
  useEffect(() => {
136
135
  if (selected) {
137
136
  setApiDetailActiveTab('response');
@@ -240,12 +239,18 @@ const NetworkInspector = ({ enabled = true }) => {
240
239
  const nextVal = !prev[key];
241
240
  const newVisibility = { ...prev, [key]: nextVal };
242
241
  if (!nextVal && activeTab === key) {
242
+ animateNextLayout();
243
243
  setActiveTab('apis');
244
244
  }
245
245
  return newVisibility;
246
246
  });
247
247
  };
248
+ const switchActiveTab = useCallback((key) => {
249
+ animateNextLayout();
250
+ setActiveTab(key);
251
+ }, []);
248
252
  const navigateFromDashboard = (key) => {
253
+ animateNextLayout();
249
254
  setTabVisibility(prev => ({ ...prev, [key]: true }));
250
255
  setActiveTab(key);
251
256
  };
@@ -318,6 +323,11 @@ const NetworkInspector = ({ enabled = true }) => {
318
323
  const badgeAnim = useRef(new Animated.Value(1)).current;
319
324
  const activePulseAnim = useRef(new Animated.Value(0.4)).current;
320
325
  const unreadPulseAnim = useRef(new Animated.Value(1)).current;
326
+ useEffect(() => {
327
+ if (Platform.OS === 'android') {
328
+ UIManager.setLayoutAnimationEnabledExperimental?.(true);
329
+ }
330
+ }, []);
321
331
  useEffect(() => {
322
332
  const loop = Animated.loop(Animated.sequence([
323
333
  Animated.timing(pulseAnim, {
@@ -393,7 +403,7 @@ const NetworkInspector = ({ enabled = true }) => {
393
403
  setupNetworkLogger();
394
404
  clearNetworkLogs();
395
405
  setupConsoleLogger();
396
- // Note: setupAnalyticsLogger(analytics()) is called by the consumer at app startup
406
+ autoSetupAnalyticsLogger();
397
407
  let timeoutId;
398
408
  const unsubscribe = subscribeNetworkLogs((raw) => {
399
409
  clearTimeout(timeoutId);
@@ -777,6 +787,7 @@ const NetworkInspector = ({ enabled = true }) => {
777
787
  return Object.entries(freq).sort((a, b) => b[1] - a[1]);
778
788
  }, [filteredAnalyticsEvents]);
779
789
  function closeModal() {
790
+ animateNextLayout();
780
791
  setVisible(false);
781
792
  setSelected(null);
782
793
  setSelectedEvent(null);
@@ -894,12 +905,14 @@ const NetworkInspector = ({ enabled = true }) => {
894
905
  return next;
895
906
  });
896
907
  }, []);
897
- const renderItem = useCallback(({ item }) => {
908
+ const renderItem = useCallback(({ item, index }) => {
898
909
  if (item.type === 'header') {
899
- return (<DomainHeader pageName={item.pageName} color={item.color} stats={item.stats} activeFilters={item.activeFilters} onToggleFilter={toggleSectionFilter} isCollapsed={item.isCollapsed} onToggleCollapse={toggleSectionCollapse} isFirst={item.isFirst} timestamp={item.timestamp}/>);
910
+ return (<AnimatedEntrance index={index} distance={8}>
911
+ <DomainHeader pageName={item.pageName} color={item.color} stats={item.stats} activeFilters={item.activeFilters} onToggleFilter={toggleSectionFilter} isCollapsed={item.isCollapsed} onToggleCollapse={toggleSectionCollapse} isFirst={item.isFirst} timestamp={item.timestamp}/>
912
+ </AnimatedEntrance>);
900
913
  }
901
914
  const { log, isLast, color } = item;
902
- return (<View style={styles.treeNodeRow}>
915
+ return (<AnimatedEntrance index={index} distance={8} style={styles.treeNodeRow}>
903
916
  <View style={styles.treeLines}>
904
917
  <View style={[
905
918
  styles.modernTreeLine,
@@ -909,9 +922,12 @@ const NetworkInspector = ({ enabled = true }) => {
909
922
  {!isLast && (<View style={[styles.modernTreeBranch, { borderColor: color }]}/>)}
910
923
  </View>
911
924
  <View style={styles.treeCardWrapper}>
912
- <LogCard item={log} isSelected={selectedLogs.has(log.id)} onToggleSelect={toggleSelect} onPress={() => setSelected(log)} timelineMinStart={minStart} timelineTotalRange={totalRange} isNew={newLogIds.has(log.id)} searchStr={search}/>
925
+ <LogCard item={log} isSelected={selectedLogs.has(log.id)} onToggleSelect={toggleSelect} onPress={() => {
926
+ animateNextLayout();
927
+ setSelected(log);
928
+ }} timelineMinStart={minStart} timelineTotalRange={totalRange} isNew={newLogIds.has(log.id)} searchStr={search}/>
913
929
  </View>
914
- </View>);
930
+ </AnimatedEntrance>);
915
931
  }, [
916
932
  minStart,
917
933
  totalRange,
@@ -937,8 +953,9 @@ const NetworkInspector = ({ enabled = true }) => {
937
953
  <LinearGradient colors={[AppColors.purple, '#6B4EFF']} start={{ x: 0, y: 0 }} end={{ x: 1, y: 0 }} style={styles.headerGradient}>
938
954
  <View style={[styles.header, { paddingHorizontal: 16, gap: 12 }]}>
939
955
  <TouchableScale onPress={() => {
956
+ animateNextLayout();
940
957
  setSettingsPage(null);
941
- setActiveTab('apis');
958
+ switchActiveTab('apis');
942
959
  }} hitSlop={12} style={{
943
960
  padding: 8,
944
961
  borderRadius: 10,
@@ -949,8 +966,21 @@ const NetworkInspector = ({ enabled = true }) => {
949
966
  <WhiteBackNavigation color="#FFFFFF" size={16}/>
950
967
  </TouchableScale>
951
968
  <View style={{ flex: 1 }}>
952
- <Text style={{ fontFamily: AppFonts.interBold, fontSize: 17, color: '#FFFFFF' }}>Settings</Text>
953
- <Text style={{ fontFamily: AppFonts.interRegular, fontSize: 11, color: 'rgba(255,255,255,0.75)', marginTop: 1 }}>Manage modules and preferences</Text>
969
+ <Text style={{
970
+ fontFamily: AppFonts.interBold,
971
+ fontSize: 17,
972
+ color: '#FFFFFF',
973
+ }}>
974
+ Settings
975
+ </Text>
976
+ <Text style={{
977
+ fontFamily: AppFonts.interRegular,
978
+ fontSize: 11,
979
+ color: 'rgba(255,255,255,0.75)',
980
+ marginTop: 1,
981
+ }}>
982
+ Manage modules and preferences
983
+ </Text>
954
984
  </View>
955
985
  <View style={{
956
986
  backgroundColor: 'rgba(255, 255, 255, 0.15)',
@@ -958,17 +988,28 @@ const NetworkInspector = ({ enabled = true }) => {
958
988
  paddingVertical: 4,
959
989
  borderRadius: 8,
960
990
  borderWidth: 1,
961
- borderColor: 'rgba(255, 255, 255, 0.1)'
991
+ borderColor: 'rgba(255, 255, 255, 0.1)',
962
992
  }}>
963
- <Text style={{ fontFamily: AppFonts.interBold, fontSize: 10.5, color: '#FFFFFF' }}>v1.0.13</Text>
993
+ <Text style={{
994
+ fontFamily: AppFonts.interBold,
995
+ fontSize: 10.5,
996
+ color: '#FFFFFF',
997
+ }}>
998
+ v1.0.13
999
+ </Text>
964
1000
  </View>
965
1001
  </View>
966
1002
  </LinearGradient>
967
1003
 
968
1004
  <ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 16, gap: 12 }}>
969
-
970
1005
  {/* Tab list */}
971
- <View style={{ backgroundColor: AppColors.primaryLight, borderRadius: 12, borderWidth: 1, borderColor: AppColors.grayBorderSecondary, overflow: 'hidden' }}>
1006
+ <View style={{
1007
+ backgroundColor: AppColors.primaryLight,
1008
+ borderRadius: 12,
1009
+ borderWidth: 1,
1010
+ borderColor: AppColors.grayBorderSecondary,
1011
+ overflow: 'hidden',
1012
+ }}>
972
1013
  {/* Table Header */}
973
1014
  <View style={{
974
1015
  flexDirection: 'row',
@@ -980,8 +1021,26 @@ const NetworkInspector = ({ enabled = true }) => {
980
1021
  borderBottomColor: AppColors.dividerColor,
981
1022
  gap: 12,
982
1023
  }}>
983
- <Text style={{ fontFamily: AppFonts.interBold, fontSize: 10, color: AppColors.grayTextWeak, letterSpacing: 0.6, flex: 1 }}>MODULE</Text>
984
- <Text style={{ fontFamily: AppFonts.interBold, fontSize: 10, color: AppColors.grayTextWeak, letterSpacing: 0.6, width: 90, textAlign: 'right', paddingRight: 4 }}>VISIBILITY</Text>
1024
+ <Text style={{
1025
+ fontFamily: AppFonts.interBold,
1026
+ fontSize: 10,
1027
+ color: AppColors.grayTextWeak,
1028
+ letterSpacing: 0.6,
1029
+ flex: 1,
1030
+ }}>
1031
+ MODULE
1032
+ </Text>
1033
+ <Text style={{
1034
+ fontFamily: AppFonts.interBold,
1035
+ fontSize: 10,
1036
+ color: AppColors.grayTextWeak,
1037
+ letterSpacing: 0.6,
1038
+ width: 90,
1039
+ textAlign: 'right',
1040
+ paddingRight: 4,
1041
+ }}>
1042
+ VISIBILITY
1043
+ </Text>
985
1044
  </View>
986
1045
 
987
1046
  {settingsTabs.map((tab, idx) => {
@@ -998,27 +1057,53 @@ const NetworkInspector = ({ enabled = true }) => {
998
1057
  gap: 12,
999
1058
  }}>
1000
1059
  {/* Icon + Label — fills remaining space */}
1001
- <View style={{ flex: 1, flexDirection: 'row', alignItems: 'center', gap: 8 }}>
1060
+ <View style={{
1061
+ flex: 1,
1062
+ flexDirection: 'row',
1063
+ alignItems: 'center',
1064
+ gap: 8,
1065
+ }}>
1002
1066
  {/* Small icon tile */}
1003
1067
  <View style={{
1004
- width: 20, height: 20, borderRadius: 6,
1005
- backgroundColor: isLocked ? AppColors.grayBorderSecondary : AppColors.purpleShade50,
1068
+ width: 20,
1069
+ height: 20,
1070
+ borderRadius: 6,
1071
+ backgroundColor: isLocked
1072
+ ? AppColors.grayBorderSecondary
1073
+ : AppColors.purpleShade50,
1006
1074
  borderWidth: 1,
1007
- borderColor: isLocked ? AppColors.dividerColor : 'rgba(104,75,155,0.2)',
1008
- alignItems: 'center', justifyContent: 'center',
1075
+ borderColor: isLocked
1076
+ ? AppColors.dividerColor
1077
+ : 'rgba(104,75,155,0.2)',
1078
+ alignItems: 'center',
1079
+ justifyContent: 'center',
1009
1080
  }}>
1010
- {tab.icon === 'insights' && <InsightsIcon color={isLocked ? AppColors.grayTextWeak : AppColors.purple} size={11}/>}
1011
- {tab.icon === 'apis' && <SignalIcon color={isLocked ? AppColors.grayTextWeak : AppColors.purple} size={11}/>}
1012
- {tab.icon === 'logs' && <TerminalIcon color={isLocked ? AppColors.grayTextWeak : AppColors.purple} size={11}/>}
1013
- {tab.icon === 'analytics' && <AnalyticsIcon color={isLocked ? AppColors.grayTextWeak : AppColors.purple} size={11}/>}
1014
- {tab.icon === 'webview' && <GlobeIcon color={isLocked ? AppColors.grayTextWeak : AppColors.purple} size={11}/>}
1015
- {tab.icon === 'redux' && <TerminalIcon color={isLocked ? AppColors.grayTextWeak : AppColors.purple} size={11}/>}
1081
+ {tab.icon === 'insights' && (<InsightsIcon color={isLocked
1082
+ ? AppColors.grayTextWeak
1083
+ : AppColors.purple} size={11}/>)}
1084
+ {tab.icon === 'apis' && (<SignalIcon color={isLocked
1085
+ ? AppColors.grayTextWeak
1086
+ : AppColors.purple} size={11}/>)}
1087
+ {tab.icon === 'logs' && (<TerminalIcon color={isLocked
1088
+ ? AppColors.grayTextWeak
1089
+ : AppColors.purple} size={11}/>)}
1090
+ {tab.icon === 'analytics' && (<AnalyticsIcon color={isLocked
1091
+ ? AppColors.grayTextWeak
1092
+ : AppColors.purple} size={11}/>)}
1093
+ {tab.icon === 'webview' && (<GlobeIcon color={isLocked
1094
+ ? AppColors.grayTextWeak
1095
+ : AppColors.purple} size={11}/>)}
1096
+ {tab.icon === 'redux' && (<TerminalIcon color={isLocked
1097
+ ? AppColors.grayTextWeak
1098
+ : AppColors.purple} size={11}/>)}
1016
1099
  </View>
1017
1100
  {/* Label + Required badge */}
1018
1101
  <Text style={{
1019
1102
  fontFamily: AppFonts.interBold,
1020
1103
  fontSize: 13,
1021
- color: isLocked ? AppColors.grayText : AppColors.primaryBlack,
1104
+ color: isLocked
1105
+ ? AppColors.grayText
1106
+ : AppColors.primaryBlack,
1022
1107
  }}>
1023
1108
  {tab.label}
1024
1109
  </Text>
@@ -1033,14 +1118,28 @@ const NetworkInspector = ({ enabled = true }) => {
1033
1118
  borderColor: 'rgba(104,75,155,0.18)',
1034
1119
  gap: 3,
1035
1120
  }}>
1036
- <View style={{ width: 4, height: 4, borderRadius: 2, backgroundColor: AppColors.purple, opacity: 0.7 }}/>
1037
- <Text style={{ fontFamily: AppFonts.interBold, fontSize: 8.5, color: AppColors.purple, letterSpacing: 0.4 }}>
1121
+ <View style={{
1122
+ width: 4,
1123
+ height: 4,
1124
+ borderRadius: 2,
1125
+ backgroundColor: AppColors.purple,
1126
+ opacity: 0.7,
1127
+ }}/>
1128
+ <Text style={{
1129
+ fontFamily: AppFonts.interBold,
1130
+ fontSize: 8.5,
1131
+ color: AppColors.purple,
1132
+ letterSpacing: 0.4,
1133
+ }}>
1038
1134
  DEFAULT
1039
1135
  </Text>
1040
1136
  </View>)}
1041
1137
 
1042
1138
  {/* Settings gear icon next to label */}
1043
- <TouchableScale onPress={() => setSettingsPage(tab.key)} hitSlop={8} style={{
1139
+ <TouchableScale onPress={() => {
1140
+ animateNextLayout();
1141
+ setSettingsPage(tab.key);
1142
+ }} hitSlop={8} style={{
1044
1143
  marginLeft: 4,
1045
1144
  padding: 4,
1046
1145
  borderRadius: 6,
@@ -1055,12 +1154,18 @@ const NetworkInspector = ({ enabled = true }) => {
1055
1154
  </View>
1056
1155
 
1057
1156
  {/* Visibility Switch in VISIBILITY column */}
1058
- <View style={{ width: 90, alignItems: 'flex-end', justifyContent: 'center' }}>
1157
+ <View style={{
1158
+ width: 90,
1159
+ alignItems: 'flex-end',
1160
+ justifyContent: 'center',
1161
+ }}>
1059
1162
  <TouchableScale disabled={isLocked} onPress={() => toggleTabVisibility(tab.key)} style={{
1060
1163
  width: 38,
1061
1164
  height: 22,
1062
1165
  borderRadius: 11,
1063
- backgroundColor: isVisible ? AppColors.purple : AppColors.grayBorderSecondary,
1166
+ backgroundColor: isVisible
1167
+ ? AppColors.purple
1168
+ : AppColors.grayBorderSecondary,
1064
1169
  padding: 2,
1065
1170
  justifyContent: 'center',
1066
1171
  alignItems: isVisible ? 'flex-end' : 'flex-start',
@@ -1083,8 +1188,22 @@ const NetworkInspector = ({ enabled = true }) => {
1083
1188
 
1084
1189
  {/* Preferences Section */}
1085
1190
  <View style={{ marginTop: 8 }}>
1086
- <Text style={{ fontFamily: AppFonts.interBold, fontSize: 10, color: AppColors.grayTextWeak, letterSpacing: 0.6, marginBottom: 8 }}>PREFERENCES</Text>
1087
- <View style={{ backgroundColor: AppColors.primaryLight, borderRadius: 12, borderWidth: 1, borderColor: AppColors.grayBorderSecondary, overflow: 'hidden' }}>
1191
+ <Text style={{
1192
+ fontFamily: AppFonts.interBold,
1193
+ fontSize: 10,
1194
+ color: AppColors.grayTextWeak,
1195
+ letterSpacing: 0.6,
1196
+ marginBottom: 8,
1197
+ }}>
1198
+ PREFERENCES
1199
+ </Text>
1200
+ <View style={{
1201
+ backgroundColor: AppColors.primaryLight,
1202
+ borderRadius: 12,
1203
+ borderWidth: 1,
1204
+ borderColor: AppColors.grayBorderSecondary,
1205
+ overflow: 'hidden',
1206
+ }}>
1088
1207
  <View style={{
1089
1208
  flexDirection: 'row',
1090
1209
  alignItems: 'center',
@@ -1093,13 +1212,21 @@ const NetworkInspector = ({ enabled = true }) => {
1093
1212
  gap: 12,
1094
1213
  }}>
1095
1214
  {/* Icon + Label */}
1096
- <View style={{ flex: 1, flexDirection: 'row', alignItems: 'center', gap: 8 }}>
1215
+ <View style={{
1216
+ flex: 1,
1217
+ flexDirection: 'row',
1218
+ alignItems: 'center',
1219
+ gap: 8,
1220
+ }}>
1097
1221
  <View style={{
1098
- width: 20, height: 20, borderRadius: 6,
1222
+ width: 20,
1223
+ height: 20,
1224
+ borderRadius: 6,
1099
1225
  backgroundColor: AppColors.purpleShade50,
1100
1226
  borderWidth: 1,
1101
1227
  borderColor: 'rgba(104,75,155,0.2)',
1102
- alignItems: 'center', justifyContent: 'center',
1228
+ alignItems: 'center',
1229
+ justifyContent: 'center',
1103
1230
  }}>
1104
1231
  {isDark ? (<SunIcon color={AppColors.purple} size={11}/>) : (<MoonIcon color={AppColors.purple} size={11}/>)}
1105
1232
  </View>
@@ -1123,7 +1250,9 @@ const NetworkInspector = ({ enabled = true }) => {
1123
1250
  width: 38,
1124
1251
  height: 22,
1125
1252
  borderRadius: 11,
1126
- backgroundColor: isDark ? AppColors.purple : AppColors.grayBorderSecondary,
1253
+ backgroundColor: isDark
1254
+ ? AppColors.purple
1255
+ : AppColors.grayBorderSecondary,
1127
1256
  padding: 2,
1128
1257
  justifyContent: 'center',
1129
1258
  alignItems: isDark ? 'flex-end' : 'flex-start',
@@ -1145,7 +1274,10 @@ const NetworkInspector = ({ enabled = true }) => {
1145
1274
  </ScrollView>
1146
1275
  </View>);
1147
1276
  }
1148
- const goBackToMain = () => setSettingsPage('main');
1277
+ const goBackToMain = () => {
1278
+ animateNextLayout();
1279
+ setSettingsPage('main');
1280
+ };
1149
1281
  const renderSubHeader = (title, icon, rightInfo) => (<View style={{
1150
1282
  flexDirection: 'row',
1151
1283
  alignItems: 'center',
@@ -1168,14 +1300,23 @@ const NetworkInspector = ({ enabled = true }) => {
1168
1300
  <WhiteBackNavigation color={AppColors.purple} size={16}/>
1169
1301
  </TouchableScale>
1170
1302
  {icon && (<View style={{
1171
- width: 36, height: 36, borderRadius: 10,
1303
+ width: 36,
1304
+ height: 36,
1305
+ borderRadius: 10,
1172
1306
  backgroundColor: AppColors.purpleShade50,
1173
- borderWidth: 1, borderColor: 'rgba(104,75,155,0.2)',
1174
- alignItems: 'center', justifyContent: 'center',
1307
+ borderWidth: 1,
1308
+ borderColor: 'rgba(104,75,155,0.2)',
1309
+ alignItems: 'center',
1310
+ justifyContent: 'center',
1175
1311
  }}>
1176
1312
  {icon}
1177
1313
  </View>)}
1178
- <Text style={{ fontFamily: AppFonts.interBold, fontSize: 18, color: AppColors.primaryBlack, flex: 1 }}>
1314
+ <Text style={{
1315
+ fontFamily: AppFonts.interBold,
1316
+ fontSize: 18,
1317
+ color: AppColors.primaryBlack,
1318
+ flex: 1,
1319
+ }}>
1179
1320
  {title}
1180
1321
  </Text>
1181
1322
  {rightInfo ? (<View style={{
@@ -1186,7 +1327,11 @@ const NetworkInspector = ({ enabled = true }) => {
1186
1327
  borderWidth: 1,
1187
1328
  borderColor: 'rgba(104,75,155,0.15)',
1188
1329
  }}>
1189
- <Text style={{ fontFamily: AppFonts.interBold, fontSize: 11, color: AppColors.purple }}>
1330
+ <Text style={{
1331
+ fontFamily: AppFonts.interBold,
1332
+ fontSize: 11,
1333
+ color: AppColors.purple,
1334
+ }}>
1190
1335
  {rightInfo}
1191
1336
  </Text>
1192
1337
  </View>) : null}
@@ -1203,18 +1348,31 @@ const NetworkInspector = ({ enabled = true }) => {
1203
1348
  gap: 12,
1204
1349
  }}>
1205
1350
  <View style={{
1206
- width: 36, height: 36, borderRadius: 10,
1351
+ width: 36,
1352
+ height: 36,
1353
+ borderRadius: 10,
1207
1354
  backgroundColor: AppColors.purpleShade50,
1208
- borderWidth: 1, borderColor: 'rgba(104,75,155,0.18)',
1209
- alignItems: 'center', justifyContent: 'center',
1355
+ borderWidth: 1,
1356
+ borderColor: 'rgba(104,75,155,0.18)',
1357
+ alignItems: 'center',
1358
+ justifyContent: 'center',
1210
1359
  }}>
1211
1360
  {opts.icon}
1212
1361
  </View>
1213
1362
  <View style={{ flex: 1 }}>
1214
- <Text style={{ fontFamily: AppFonts.interBold, fontSize: 14, color: AppColors.primaryBlack }}>
1363
+ <Text style={{
1364
+ fontFamily: AppFonts.interBold,
1365
+ fontSize: 14,
1366
+ color: AppColors.primaryBlack,
1367
+ }}>
1215
1368
  {opts.label}
1216
1369
  </Text>
1217
- {opts.description ? (<Text style={{ fontFamily: AppFonts.interRegular, fontSize: 11, color: AppColors.grayText, marginTop: 1 }}>
1370
+ {opts.description ? (<Text style={{
1371
+ fontFamily: AppFonts.interRegular,
1372
+ fontSize: 11,
1373
+ color: AppColors.grayText,
1374
+ marginTop: 1,
1375
+ }}>
1218
1376
  {opts.description}
1219
1377
  </Text>) : null}
1220
1378
  </View>
@@ -1236,14 +1394,20 @@ const NetworkInspector = ({ enabled = true }) => {
1236
1394
  paddingVertical: 6,
1237
1395
  alignItems: 'center',
1238
1396
  borderRadius: 6,
1239
- backgroundColor: isActive ? AppColors.purple : 'transparent',
1397
+ backgroundColor: isActive
1398
+ ? AppColors.purple
1399
+ : 'transparent',
1240
1400
  }}>
1241
1401
  <Text style={{
1242
1402
  fontFamily: AppFonts.interBold,
1243
1403
  fontSize: 11,
1244
1404
  color: isActive ? '#FFFFFF' : AppColors.grayText,
1245
1405
  }}>
1246
- {typeof opt === 'number' && opt >= 500 && settingsPage === 'insights' ? `${opt}ms` : opt}
1406
+ {typeof opt === 'number' &&
1407
+ opt >= 500 &&
1408
+ settingsPage === 'insights'
1409
+ ? `${opt}ms`
1410
+ : opt}
1247
1411
  </Text>
1248
1412
  </TouchableScale>);
1249
1413
  })}
@@ -1252,7 +1416,14 @@ const NetworkInspector = ({ enabled = true }) => {
1252
1416
  if (settingsPage === 'apis') {
1253
1417
  return (<ScrollView style={{ flex: 1, backgroundColor: AppColors.grayBackground }} contentContainerStyle={{ padding: 16 }}>
1254
1418
  {renderSubHeader('APIs Settings', <SignalIcon color={AppColors.purple} size={16}/>, `Total: ${logs.length}`)}
1255
- <View style={{ backgroundColor: AppColors.primaryLight, padding: 16, borderRadius: 12, borderWidth: 1, borderColor: AppColors.grayBorderSecondary, gap: 4 }}>
1419
+ <View style={{
1420
+ backgroundColor: AppColors.primaryLight,
1421
+ padding: 16,
1422
+ borderRadius: 12,
1423
+ borderWidth: 1,
1424
+ borderColor: AppColors.grayBorderSecondary,
1425
+ gap: 4,
1426
+ }}>
1256
1427
  {renderSettingRow({
1257
1428
  icon: <SignalIcon color={AppColors.purple} size={16}/>,
1258
1429
  label: 'Max Request Logs',
@@ -1266,7 +1437,14 @@ const NetworkInspector = ({ enabled = true }) => {
1266
1437
  })}
1267
1438
  </View>
1268
1439
 
1269
- <View style={{ backgroundColor: AppColors.primaryLight, borderRadius: 12, borderWidth: 1, borderColor: AppColors.grayBorderSecondary, padding: 16, marginTop: 12 }}>
1440
+ <View style={{
1441
+ backgroundColor: AppColors.primaryLight,
1442
+ borderRadius: 12,
1443
+ borderWidth: 1,
1444
+ borderColor: AppColors.grayBorderSecondary,
1445
+ padding: 16,
1446
+ marginTop: 12,
1447
+ }}>
1270
1448
  {renderSettingRow({
1271
1449
  icon: <TrashIcon color={AppColors.errorColor} size={16}/>,
1272
1450
  label: 'Clear Network Logs',
@@ -1277,8 +1455,21 @@ const NetworkInspector = ({ enabled = true }) => {
1277
1455
  setSelected(null);
1278
1456
  Alert.alert('Success', 'Network logs cleared.');
1279
1457
  },
1280
- right: (<View style={{ paddingHorizontal: 10, paddingVertical: 5, borderRadius: 7, backgroundColor: 'rgba(255,46,87,0.08)', borderWidth: 1, borderColor: 'rgba(255,46,87,0.2)' }}>
1281
- <Text style={{ fontFamily: AppFonts.interBold, fontSize: 11, color: AppColors.errorColor }}>Clear</Text>
1458
+ right: (<View style={{
1459
+ paddingHorizontal: 10,
1460
+ paddingVertical: 5,
1461
+ borderRadius: 7,
1462
+ backgroundColor: 'rgba(255,46,87,0.08)',
1463
+ borderWidth: 1,
1464
+ borderColor: 'rgba(255,46,87,0.2)',
1465
+ }}>
1466
+ <Text style={{
1467
+ fontFamily: AppFonts.interBold,
1468
+ fontSize: 11,
1469
+ color: AppColors.errorColor,
1470
+ }}>
1471
+ Clear
1472
+ </Text>
1282
1473
  </View>),
1283
1474
  })}
1284
1475
  </View>
@@ -1287,7 +1478,14 @@ const NetworkInspector = ({ enabled = true }) => {
1287
1478
  if (settingsPage === 'logs') {
1288
1479
  return (<ScrollView style={{ flex: 1, backgroundColor: AppColors.grayBackground }} contentContainerStyle={{ padding: 16 }}>
1289
1480
  {renderSubHeader('Logs Settings', <TerminalIcon color={AppColors.purple} size={16}/>, `Total: ${consoleLogs.length}`)}
1290
- <View style={{ backgroundColor: AppColors.primaryLight, padding: 16, borderRadius: 12, borderWidth: 1, borderColor: AppColors.grayBorderSecondary, gap: 4 }}>
1481
+ <View style={{
1482
+ backgroundColor: AppColors.primaryLight,
1483
+ padding: 16,
1484
+ borderRadius: 12,
1485
+ borderWidth: 1,
1486
+ borderColor: AppColors.grayBorderSecondary,
1487
+ gap: 4,
1488
+ }}>
1291
1489
  {renderSettingRow({
1292
1490
  icon: <TerminalIcon color={AppColors.purple} size={16}/>,
1293
1491
  label: 'Max Console Logs',
@@ -1299,30 +1497,67 @@ const NetworkInspector = ({ enabled = true }) => {
1299
1497
  },
1300
1498
  })}
1301
1499
  <View style={{ height: 1, backgroundColor: AppColors.dividerColor }}/>
1302
- <Text style={{ fontFamily: AppFonts.interBold, fontSize: 13, color: AppColors.primaryBlack, paddingTop: 4 }}>Log Levels</Text>
1500
+ <Text style={{
1501
+ fontFamily: AppFonts.interBold,
1502
+ fontSize: 13,
1503
+ color: AppColors.primaryBlack,
1504
+ paddingTop: 4,
1505
+ }}>
1506
+ Log Levels
1507
+ </Text>
1303
1508
  {['info', 'warn', 'error'].map((level, li) => {
1304
1509
  const isLvlActive = showConsoleLevels[level];
1305
- const levelColor = level === 'error' ? AppColors.errorColor : level === 'warn' ? AppColors.warningIconGold : AppColors.skyBlue;
1510
+ const levelColor = level === 'error'
1511
+ ? AppColors.errorColor
1512
+ : level === 'warn'
1513
+ ? AppColors.warningIconGold
1514
+ : AppColors.skyBlue;
1306
1515
  return renderSettingRow({
1307
- icon: <View style={{ width: 10, height: 10, borderRadius: 5, backgroundColor: levelColor }}/>,
1516
+ icon: (<View style={{
1517
+ width: 10,
1518
+ height: 10,
1519
+ borderRadius: 5,
1520
+ backgroundColor: levelColor,
1521
+ }}/>),
1308
1522
  label: `Show ${level.charAt(0).toUpperCase() + level.slice(1)} logs`,
1309
- description: level === 'info' ? 'Informational messages' : level === 'warn' ? 'Warning messages' : 'Error messages',
1523
+ description: level === 'info'
1524
+ ? 'Informational messages'
1525
+ : level === 'warn'
1526
+ ? 'Warning messages'
1527
+ : 'Error messages',
1310
1528
  isLast: level === 'error',
1311
- onPress: () => setShowConsoleLevels(prev => ({ ...prev, [level]: !prev[level] })),
1529
+ onPress: () => setShowConsoleLevels(prev => ({
1530
+ ...prev,
1531
+ [level]: !prev[level],
1532
+ })),
1312
1533
  right: (<View style={{
1313
- width: 22, height: 22, borderRadius: 6,
1534
+ width: 22,
1535
+ height: 22,
1536
+ borderRadius: 6,
1314
1537
  borderWidth: 2,
1315
- borderColor: isLvlActive ? AppColors.purple : AppColors.grayTextWeak,
1316
- backgroundColor: isLvlActive ? 'rgba(104, 75, 155, 0.1)' : 'transparent',
1317
- alignItems: 'center', justifyContent: 'center',
1538
+ borderColor: isLvlActive
1539
+ ? AppColors.purple
1540
+ : AppColors.grayTextWeak,
1541
+ backgroundColor: isLvlActive
1542
+ ? 'rgba(104, 75, 155, 0.1)'
1543
+ : 'transparent',
1544
+ alignItems: 'center',
1545
+ justifyContent: 'center',
1318
1546
  }}>
1319
- {isLvlActive && <CheckIcon size={12} color={AppColors.purple}/>}
1547
+ {isLvlActive && (<CheckIcon size={12} color={AppColors.purple}/>)}
1320
1548
  </View>),
1321
1549
  });
1322
1550
  })}
1323
1551
  </View>
1324
1552
 
1325
- <View style={{ backgroundColor: AppColors.primaryLight, borderRadius: 12, borderWidth: 1, borderColor: AppColors.grayBorderSecondary, padding: 16, marginTop: 12 }}>
1553
+ <View style={{
1554
+ backgroundColor: AppColors.primaryLight,
1555
+ borderRadius: 12,
1556
+ borderWidth: 1,
1557
+ borderColor: AppColors.grayBorderSecondary,
1558
+ padding: 16,
1559
+ marginTop: 12,
1560
+ }}>
1326
1561
  {renderSettingRow({
1327
1562
  icon: <TrashIcon color={AppColors.errorColor} size={16}/>,
1328
1563
  label: 'Clear Console Logs',
@@ -1332,8 +1567,21 @@ const NetworkInspector = ({ enabled = true }) => {
1332
1567
  clearConsoleLogs();
1333
1568
  Alert.alert('Success', 'Console logs cleared.');
1334
1569
  },
1335
- right: (<View style={{ paddingHorizontal: 10, paddingVertical: 5, borderRadius: 7, backgroundColor: 'rgba(255,46,87,0.08)', borderWidth: 1, borderColor: 'rgba(255,46,87,0.2)' }}>
1336
- <Text style={{ fontFamily: AppFonts.interBold, fontSize: 11, color: AppColors.errorColor }}>Clear</Text>
1570
+ right: (<View style={{
1571
+ paddingHorizontal: 10,
1572
+ paddingVertical: 5,
1573
+ borderRadius: 7,
1574
+ backgroundColor: 'rgba(255,46,87,0.08)',
1575
+ borderWidth: 1,
1576
+ borderColor: 'rgba(255,46,87,0.2)',
1577
+ }}>
1578
+ <Text style={{
1579
+ fontFamily: AppFonts.interBold,
1580
+ fontSize: 11,
1581
+ color: AppColors.errorColor,
1582
+ }}>
1583
+ Clear
1584
+ </Text>
1337
1585
  </View>),
1338
1586
  })}
1339
1587
  </View>
@@ -1342,7 +1590,14 @@ const NetworkInspector = ({ enabled = true }) => {
1342
1590
  if (settingsPage === 'analytics') {
1343
1591
  return (<ScrollView style={{ flex: 1, backgroundColor: AppColors.grayBackground }} contentContainerStyle={{ padding: 16 }}>
1344
1592
  {renderSubHeader('Analytics Settings', <AnalyticsIcon color={AppColors.purple} size={16}/>, `Events: ${analyticsEvents.length}`)}
1345
- <View style={{ backgroundColor: AppColors.primaryLight, padding: 16, borderRadius: 12, borderWidth: 1, borderColor: AppColors.grayBorderSecondary, gap: 4 }}>
1593
+ <View style={{
1594
+ backgroundColor: AppColors.primaryLight,
1595
+ padding: 16,
1596
+ borderRadius: 12,
1597
+ borderWidth: 1,
1598
+ borderColor: AppColors.grayBorderSecondary,
1599
+ gap: 4,
1600
+ }}>
1346
1601
  {renderSettingRow({
1347
1602
  icon: <AnalyticsIcon color={AppColors.purple} size={16}/>,
1348
1603
  label: 'Events Captured',
@@ -1350,7 +1605,14 @@ const NetworkInspector = ({ enabled = true }) => {
1350
1605
  isLast: true,
1351
1606
  })}
1352
1607
  </View>
1353
- <View style={{ backgroundColor: AppColors.primaryLight, borderRadius: 12, borderWidth: 1, borderColor: AppColors.grayBorderSecondary, padding: 16, marginTop: 12 }}>
1608
+ <View style={{
1609
+ backgroundColor: AppColors.primaryLight,
1610
+ borderRadius: 12,
1611
+ borderWidth: 1,
1612
+ borderColor: AppColors.grayBorderSecondary,
1613
+ padding: 16,
1614
+ marginTop: 12,
1615
+ }}>
1354
1616
  {renderSettingRow({
1355
1617
  icon: <TrashIcon color={AppColors.errorColor} size={16}/>,
1356
1618
  label: 'Clear Analytics History',
@@ -1361,8 +1623,21 @@ const NetworkInspector = ({ enabled = true }) => {
1361
1623
  setSelectedEvent(null);
1362
1624
  Alert.alert('Success', 'Analytics events cleared.');
1363
1625
  },
1364
- right: (<View style={{ paddingHorizontal: 10, paddingVertical: 5, borderRadius: 7, backgroundColor: 'rgba(255,46,87,0.08)', borderWidth: 1, borderColor: 'rgba(255,46,87,0.2)' }}>
1365
- <Text style={{ fontFamily: AppFonts.interBold, fontSize: 11, color: AppColors.errorColor }}>Clear</Text>
1626
+ right: (<View style={{
1627
+ paddingHorizontal: 10,
1628
+ paddingVertical: 5,
1629
+ borderRadius: 7,
1630
+ backgroundColor: 'rgba(255,46,87,0.08)',
1631
+ borderWidth: 1,
1632
+ borderColor: 'rgba(255,46,87,0.2)',
1633
+ }}>
1634
+ <Text style={{
1635
+ fontFamily: AppFonts.interBold,
1636
+ fontSize: 11,
1637
+ color: AppColors.errorColor,
1638
+ }}>
1639
+ Clear
1640
+ </Text>
1366
1641
  </View>),
1367
1642
  })}
1368
1643
  </View>
@@ -1371,25 +1646,46 @@ const NetworkInspector = ({ enabled = true }) => {
1371
1646
  if (settingsPage === 'webview') {
1372
1647
  return (<ScrollView style={{ flex: 1, backgroundColor: AppColors.grayBackground }} contentContainerStyle={{ padding: 16 }}>
1373
1648
  {renderSubHeader('WebView Settings', <GlobeIcon color={AppColors.purple} size={16}/>, `History: ${webViewNavHistory.length}`)}
1374
- <View style={{ backgroundColor: AppColors.primaryLight, padding: 16, borderRadius: 12, borderWidth: 1, borderColor: AppColors.grayBorderSecondary, gap: 4 }}>
1649
+ <View style={{
1650
+ backgroundColor: AppColors.primaryLight,
1651
+ padding: 16,
1652
+ borderRadius: 12,
1653
+ borderWidth: 1,
1654
+ borderColor: AppColors.grayBorderSecondary,
1655
+ gap: 4,
1656
+ }}>
1375
1657
  {renderSettingRow({
1376
1658
  icon: <GlobeIcon color={AppColors.purple} size={16}/>,
1377
1659
  label: 'Capture CSS & JavaScript',
1378
1660
  description: 'Extract stylesheet and script source from pages',
1379
1661
  onPress: () => setWebViewCaptureCssJs(prev => !prev),
1380
1662
  right: (<View style={{
1381
- width: 22, height: 22, borderRadius: 6,
1663
+ width: 22,
1664
+ height: 22,
1665
+ borderRadius: 6,
1382
1666
  borderWidth: 2,
1383
- borderColor: webViewCaptureCssJs ? AppColors.purple : AppColors.grayTextWeak,
1384
- backgroundColor: webViewCaptureCssJs ? 'rgba(104, 75, 155, 0.1)' : 'transparent',
1385
- alignItems: 'center', justifyContent: 'center',
1667
+ borderColor: webViewCaptureCssJs
1668
+ ? AppColors.purple
1669
+ : AppColors.grayTextWeak,
1670
+ backgroundColor: webViewCaptureCssJs
1671
+ ? 'rgba(104, 75, 155, 0.1)'
1672
+ : 'transparent',
1673
+ alignItems: 'center',
1674
+ justifyContent: 'center',
1386
1675
  }}>
1387
- {webViewCaptureCssJs && <CheckIcon size={12} color={AppColors.purple}/>}
1676
+ {webViewCaptureCssJs && (<CheckIcon size={12} color={AppColors.purple}/>)}
1388
1677
  </View>),
1389
1678
  isLast: true,
1390
1679
  })}
1391
1680
  </View>
1392
- <View style={{ backgroundColor: AppColors.primaryLight, borderRadius: 12, borderWidth: 1, borderColor: AppColors.grayBorderSecondary, padding: 16, marginTop: 12 }}>
1681
+ <View style={{
1682
+ backgroundColor: AppColors.primaryLight,
1683
+ borderRadius: 12,
1684
+ borderWidth: 1,
1685
+ borderColor: AppColors.grayBorderSecondary,
1686
+ padding: 16,
1687
+ marginTop: 12,
1688
+ }}>
1393
1689
  {renderSettingRow({
1394
1690
  icon: <TrashIcon color={AppColors.errorColor} size={16}/>,
1395
1691
  label: 'Clear WebView Data',
@@ -1399,8 +1695,21 @@ const NetworkInspector = ({ enabled = true }) => {
1399
1695
  clearWebViewData();
1400
1696
  Alert.alert('Success', 'WebView source history cleared.');
1401
1697
  },
1402
- right: (<View style={{ paddingHorizontal: 10, paddingVertical: 5, borderRadius: 7, backgroundColor: 'rgba(255,46,87,0.08)', borderWidth: 1, borderColor: 'rgba(255,46,87,0.2)' }}>
1403
- <Text style={{ fontFamily: AppFonts.interBold, fontSize: 11, color: AppColors.errorColor }}>Clear</Text>
1698
+ right: (<View style={{
1699
+ paddingHorizontal: 10,
1700
+ paddingVertical: 5,
1701
+ borderRadius: 7,
1702
+ backgroundColor: 'rgba(255,46,87,0.08)',
1703
+ borderWidth: 1,
1704
+ borderColor: 'rgba(255,46,87,0.2)',
1705
+ }}>
1706
+ <Text style={{
1707
+ fontFamily: AppFonts.interBold,
1708
+ fontSize: 11,
1709
+ color: AppColors.errorColor,
1710
+ }}>
1711
+ Clear
1712
+ </Text>
1404
1713
  </View>),
1405
1714
  })}
1406
1715
  </View>
@@ -1409,20 +1718,34 @@ const NetworkInspector = ({ enabled = true }) => {
1409
1718
  if (settingsPage === 'redux') {
1410
1719
  return (<ScrollView style={{ flex: 1, backgroundColor: AppColors.grayBackground }} contentContainerStyle={{ padding: 16 }}>
1411
1720
  {renderSubHeader('Redux Settings', <TerminalIcon color={AppColors.purple} size={16}/>, `Reducers: ${Object.keys(reduxState || {}).length}`)}
1412
- <View style={{ backgroundColor: AppColors.primaryLight, padding: 16, borderRadius: 12, borderWidth: 1, borderColor: AppColors.grayBorderSecondary, gap: 4 }}>
1721
+ <View style={{
1722
+ backgroundColor: AppColors.primaryLight,
1723
+ padding: 16,
1724
+ borderRadius: 12,
1725
+ borderWidth: 1,
1726
+ borderColor: AppColors.grayBorderSecondary,
1727
+ gap: 4,
1728
+ }}>
1413
1729
  {renderSettingRow({
1414
1730
  icon: <TerminalIcon color={AppColors.purple} size={16}/>,
1415
1731
  label: 'Auto-refresh Store',
1416
1732
  description: 'Automatically capture Redux store state updates',
1417
1733
  onPress: () => setReduxAutoRefreshState(prev => !prev),
1418
1734
  right: (<View style={{
1419
- width: 22, height: 22, borderRadius: 6,
1735
+ width: 22,
1736
+ height: 22,
1737
+ borderRadius: 6,
1420
1738
  borderWidth: 2,
1421
- borderColor: reduxAutoRefresh ? AppColors.purple : AppColors.grayTextWeak,
1422
- backgroundColor: reduxAutoRefresh ? 'rgba(104, 75, 155, 0.1)' : 'transparent',
1423
- alignItems: 'center', justifyContent: 'center',
1739
+ borderColor: reduxAutoRefresh
1740
+ ? AppColors.purple
1741
+ : AppColors.grayTextWeak,
1742
+ backgroundColor: reduxAutoRefresh
1743
+ ? 'rgba(104, 75, 155, 0.1)'
1744
+ : 'transparent',
1745
+ alignItems: 'center',
1746
+ justifyContent: 'center',
1424
1747
  }}>
1425
- {reduxAutoRefresh && <CheckIcon size={12} color={AppColors.purple}/>}
1748
+ {reduxAutoRefresh && (<CheckIcon size={12} color={AppColors.purple}/>)}
1426
1749
  </View>),
1427
1750
  })}
1428
1751
  <View style={{ height: 1, backgroundColor: AppColors.dividerColor }}/>
@@ -1439,18 +1762,40 @@ const NetworkInspector = ({ enabled = true }) => {
1439
1762
  })}
1440
1763
  </View>
1441
1764
 
1442
- <View style={{ backgroundColor: AppColors.primaryLight, borderRadius: 12, borderWidth: 1, borderColor: AppColors.grayBorderSecondary, padding: 16, marginTop: 12 }}>
1765
+ <View style={{
1766
+ backgroundColor: AppColors.primaryLight,
1767
+ borderRadius: 12,
1768
+ borderWidth: 1,
1769
+ borderColor: AppColors.grayBorderSecondary,
1770
+ padding: 16,
1771
+ marginTop: 12,
1772
+ }}>
1443
1773
  {renderSettingRow({
1444
1774
  icon: <TrashIcon color={AppColors.errorColor} size={16}/>,
1445
1775
  label: 'Clear Redux State',
1446
- description: reduxState ? 'Reset state snapshot in inspector' : 'No store snapshot stored',
1776
+ description: reduxState
1777
+ ? 'Reset state snapshot in inspector'
1778
+ : 'No store snapshot stored',
1447
1779
  isLast: true,
1448
1780
  onPress: () => {
1449
1781
  setReduxState(null);
1450
1782
  Alert.alert('Success', 'Redux state snapshot cleared.');
1451
1783
  },
1452
- right: (<View style={{ paddingHorizontal: 10, paddingVertical: 5, borderRadius: 7, backgroundColor: 'rgba(255,46,87,0.08)', borderWidth: 1, borderColor: 'rgba(255,46,87,0.2)' }}>
1453
- <Text style={{ fontFamily: AppFonts.interBold, fontSize: 11, color: AppColors.errorColor }}>Clear</Text>
1784
+ right: (<View style={{
1785
+ paddingHorizontal: 10,
1786
+ paddingVertical: 5,
1787
+ borderRadius: 7,
1788
+ backgroundColor: 'rgba(255,46,87,0.08)',
1789
+ borderWidth: 1,
1790
+ borderColor: 'rgba(255,46,87,0.2)',
1791
+ }}>
1792
+ <Text style={{
1793
+ fontFamily: AppFonts.interBold,
1794
+ fontSize: 11,
1795
+ color: AppColors.errorColor,
1796
+ }}>
1797
+ Clear
1798
+ </Text>
1454
1799
  </View>),
1455
1800
  })}
1456
1801
  </View>
@@ -1459,7 +1804,14 @@ const NetworkInspector = ({ enabled = true }) => {
1459
1804
  // Default return page is Insights settings
1460
1805
  return (<ScrollView style={{ flex: 1, backgroundColor: AppColors.grayBackground }} contentContainerStyle={{ padding: 16 }}>
1461
1806
  {renderSubHeader('Insights Settings', <InsightsIcon color={AppColors.purple} size={16}/>)}
1462
- <View style={{ backgroundColor: AppColors.primaryLight, padding: 16, borderRadius: 12, borderWidth: 1, borderColor: AppColors.grayBorderSecondary, gap: 4 }}>
1807
+ <View style={{
1808
+ backgroundColor: AppColors.primaryLight,
1809
+ padding: 16,
1810
+ borderRadius: 12,
1811
+ borderWidth: 1,
1812
+ borderColor: AppColors.grayBorderSecondary,
1813
+ gap: 4,
1814
+ }}>
1463
1815
  {renderSettingRow({
1464
1816
  icon: <SignalIcon color={AppColors.purple} size={16}/>,
1465
1817
  label: 'Slow Latency Warning',
@@ -1478,13 +1830,20 @@ const NetworkInspector = ({ enabled = true }) => {
1478
1830
  isLast: true,
1479
1831
  onPress: () => setInsightsShowConsoleAlerts(prev => !prev),
1480
1832
  right: (<View style={{
1481
- width: 22, height: 22, borderRadius: 6,
1833
+ width: 22,
1834
+ height: 22,
1835
+ borderRadius: 6,
1482
1836
  borderWidth: 2,
1483
- borderColor: insightsShowConsoleAlerts ? AppColors.purple : AppColors.grayTextWeak,
1484
- backgroundColor: insightsShowConsoleAlerts ? 'rgba(104, 75, 155, 0.1)' : 'transparent',
1485
- alignItems: 'center', justifyContent: 'center',
1837
+ borderColor: insightsShowConsoleAlerts
1838
+ ? AppColors.purple
1839
+ : AppColors.grayTextWeak,
1840
+ backgroundColor: insightsShowConsoleAlerts
1841
+ ? 'rgba(104, 75, 155, 0.1)'
1842
+ : 'transparent',
1843
+ alignItems: 'center',
1844
+ justifyContent: 'center',
1486
1845
  }}>
1487
- {insightsShowConsoleAlerts && <CheckIcon size={12} color={AppColors.purple}/>}
1846
+ {insightsShowConsoleAlerts && (<CheckIcon size={12} color={AppColors.purple}/>)}
1488
1847
  </View>),
1489
1848
  })}
1490
1849
  </View>
@@ -1492,10 +1851,14 @@ const NetworkInspector = ({ enabled = true }) => {
1492
1851
  };
1493
1852
  const renderInsightsDashboard = () => {
1494
1853
  const apiTotal = logs.length;
1495
- const apiErrors = logs.filter(l => (l.status != null && l.status >= 400) || l.status === 0 || l.status == null).length;
1854
+ const apiErrors = logs.filter(l => (l.status != null && l.status >= 400) ||
1855
+ l.status === 0 ||
1856
+ l.status == null).length;
1496
1857
  const apiSuccess = apiTotal - apiErrors;
1497
1858
  const apiSuccessRate = apiTotal > 0 ? Math.round((apiSuccess / apiTotal) * 100) : 100;
1498
- const durations = logs.filter(l => l.duration != null).map(l => l.duration);
1859
+ const durations = logs
1860
+ .filter(l => l.duration != null)
1861
+ .map(l => l.duration);
1499
1862
  const avgTime = durations.length > 0
1500
1863
  ? Math.round(durations.reduce((a, b) => a + b, 0) / durations.length)
1501
1864
  : null;
@@ -1505,11 +1868,13 @@ const NetworkInspector = ({ enabled = true }) => {
1505
1868
  const logInfos = visibleConsoleLogs.filter(l => l.type === 'info').length;
1506
1869
  const analyticsTotal = analyticsEvents.length;
1507
1870
  const uniqueEvents = new Set(analyticsEvents.map(e => e.name)).size;
1508
- const screenViews = analyticsEvents.filter(e => e.name === 'screen_view' || e.name === 'page_view' || e.name === 'firebase_screen_class').length;
1871
+ const screenViews = analyticsEvents.filter(e => e.name === 'screen_view' ||
1872
+ e.name === 'page_view' ||
1873
+ e.name === 'firebase_screen_class').length;
1509
1874
  const webviewTotal = webViewNavHistory.length;
1510
1875
  return (<View style={styles.dashboardContainer}>
1511
1876
  {/* Module 1: APIs */}
1512
- {tabVisibility.apis && (<TouchableScale style={styles.dashboardModuleCard} onPress={() => setActiveTab('apis')}>
1877
+ {tabVisibility.apis && (<TouchableScale style={styles.dashboardModuleCard} onPress={() => switchActiveTab('apis')}>
1513
1878
  <View style={styles.dashboardModuleHeader}>
1514
1879
  <View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
1515
1880
  <SignalIcon color={AppColors.purple} size={18}/>
@@ -1523,13 +1888,19 @@ const NetworkInspector = ({ enabled = true }) => {
1523
1888
  <Text style={styles.dashboardGridLbl}>Requests</Text>
1524
1889
  </View>
1525
1890
  <View style={styles.dashboardGridItem}>
1526
- <Text style={[styles.dashboardGridVal, apiSuccessRate < 90 && { color: AppColors.warningIconGold }]}>
1891
+ <Text style={[
1892
+ styles.dashboardGridVal,
1893
+ apiSuccessRate < 90 && { color: AppColors.warningIconGold },
1894
+ ]}>
1527
1895
  {apiSuccessRate}%
1528
1896
  </Text>
1529
1897
  <Text style={styles.dashboardGridLbl}>Success Rate</Text>
1530
1898
  </View>
1531
1899
  <View style={styles.dashboardGridItem}>
1532
- <Text style={[styles.dashboardGridVal, apiErrors > 0 && { color: AppColors.errorColor }]}>
1900
+ <Text style={[
1901
+ styles.dashboardGridVal,
1902
+ apiErrors > 0 && { color: AppColors.errorColor },
1903
+ ]}>
1533
1904
  {apiErrors}
1534
1905
  </Text>
1535
1906
  <Text style={styles.dashboardGridLbl}>Errors</Text>
@@ -1544,7 +1915,7 @@ const NetworkInspector = ({ enabled = true }) => {
1544
1915
  </TouchableScale>)}
1545
1916
 
1546
1917
  {/* Module 2: Logs */}
1547
- {tabVisibility.logs && (<TouchableScale style={styles.dashboardModuleCard} onPress={() => setActiveTab('logs')}>
1918
+ {tabVisibility.logs && (<TouchableScale style={styles.dashboardModuleCard} onPress={() => switchActiveTab('logs')}>
1548
1919
  <View style={styles.dashboardModuleHeader}>
1549
1920
  <View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
1550
1921
  <TerminalIcon color="#0D9488" size={18}/>
@@ -1558,17 +1929,25 @@ const NetworkInspector = ({ enabled = true }) => {
1558
1929
  <Text style={styles.dashboardGridLbl}>Total Logs</Text>
1559
1930
  </View>
1560
1931
  <View style={styles.dashboardGridItem}>
1561
- <Text style={[styles.dashboardGridVal, { color: '#0D9488' }]}>{logInfos}</Text>
1932
+ <Text style={[styles.dashboardGridVal, { color: '#0D9488' }]}>
1933
+ {logInfos}
1934
+ </Text>
1562
1935
  <Text style={styles.dashboardGridLbl}>Info</Text>
1563
1936
  </View>
1564
1937
  <View style={styles.dashboardGridItem}>
1565
- <Text style={[styles.dashboardGridVal, logWarns > 0 && { color: AppColors.warningIconGold }]}>
1938
+ <Text style={[
1939
+ styles.dashboardGridVal,
1940
+ logWarns > 0 && { color: AppColors.warningIconGold },
1941
+ ]}>
1566
1942
  {logWarns}
1567
1943
  </Text>
1568
1944
  <Text style={styles.dashboardGridLbl}>Warnings</Text>
1569
1945
  </View>
1570
1946
  <View style={styles.dashboardGridItem}>
1571
- <Text style={[styles.dashboardGridVal, logErrors > 0 && { color: AppColors.errorColor }]}>
1947
+ <Text style={[
1948
+ styles.dashboardGridVal,
1949
+ logErrors > 0 && { color: AppColors.errorColor },
1950
+ ]}>
1572
1951
  {logErrors}
1573
1952
  </Text>
1574
1953
  <Text style={styles.dashboardGridLbl}>Errors</Text>
@@ -1577,11 +1956,13 @@ const NetworkInspector = ({ enabled = true }) => {
1577
1956
  </TouchableScale>)}
1578
1957
 
1579
1958
  {/* Module 3: Analytics */}
1580
- {tabVisibility.analytics && (<TouchableScale style={styles.dashboardModuleCard} onPress={() => setActiveTab('analytics')}>
1959
+ {tabVisibility.analytics && (<TouchableScale style={styles.dashboardModuleCard} onPress={() => switchActiveTab('analytics')}>
1581
1960
  <View style={styles.dashboardModuleHeader}>
1582
1961
  <View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
1583
1962
  <AnalyticsIcon color="#EA580C" size={18}/>
1584
- <Text style={styles.dashboardModuleTitle}>Analytics Events</Text>
1963
+ <Text style={styles.dashboardModuleTitle}>
1964
+ Analytics Events
1965
+ </Text>
1585
1966
  </View>
1586
1967
  <Text style={styles.dashboardModuleGoText}>View Details →</Text>
1587
1968
  </View>
@@ -1591,7 +1972,9 @@ const NetworkInspector = ({ enabled = true }) => {
1591
1972
  <Text style={styles.dashboardGridLbl}>Total Events</Text>
1592
1973
  </View>
1593
1974
  <View style={styles.dashboardGridItem}>
1594
- <Text style={[styles.dashboardGridVal, { color: '#EA580C' }]}>{uniqueEvents}</Text>
1975
+ <Text style={[styles.dashboardGridVal, { color: '#EA580C' }]}>
1976
+ {uniqueEvents}
1977
+ </Text>
1595
1978
  <Text style={styles.dashboardGridLbl}>Unique Names</Text>
1596
1979
  </View>
1597
1980
  <View style={styles.dashboardGridItem}>
@@ -1600,7 +1983,9 @@ const NetworkInspector = ({ enabled = true }) => {
1600
1983
  </View>
1601
1984
  <View style={styles.dashboardGridItem}>
1602
1985
  <Text style={styles.dashboardGridVal}>
1603
- {analyticsTotal > 0 ? Math.round(analyticsTotal / Math.max(1, logs.length / 5)) : 0}
1986
+ {analyticsTotal > 0
1987
+ ? Math.round(analyticsTotal / Math.max(1, logs.length / 5))
1988
+ : 0}
1604
1989
  </Text>
1605
1990
  <Text style={styles.dashboardGridLbl}>Events Ratio</Text>
1606
1991
  </View>
@@ -1608,11 +1993,13 @@ const NetworkInspector = ({ enabled = true }) => {
1608
1993
  </TouchableScale>)}
1609
1994
 
1610
1995
  {/* Module 4: WebView */}
1611
- {tabVisibility.webview && (<TouchableScale style={styles.dashboardModuleCard} onPress={() => setActiveTab('webview')}>
1996
+ {tabVisibility.webview && (<TouchableScale style={styles.dashboardModuleCard} onPress={() => switchActiveTab('webview')}>
1612
1997
  <View style={styles.dashboardModuleHeader}>
1613
1998
  <View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
1614
1999
  <GlobeIcon color="#2563EB" size={18}/>
1615
- <Text style={styles.dashboardModuleTitle}>WebView Captures</Text>
2000
+ <Text style={styles.dashboardModuleTitle}>
2001
+ WebView Captures
2002
+ </Text>
1616
2003
  </View>
1617
2004
  <Text style={styles.dashboardModuleGoText}>View Details →</Text>
1618
2005
  </View>
@@ -1622,12 +2009,16 @@ const NetworkInspector = ({ enabled = true }) => {
1622
2009
  <Text style={styles.dashboardGridLbl}>History Size</Text>
1623
2010
  </View>
1624
2011
  <View style={styles.dashboardGridItem}>
1625
- <Text style={[styles.dashboardGridVal, { color: '#16A34A' }]}>Active</Text>
2012
+ <Text style={[styles.dashboardGridVal, { color: '#16A34A' }]}>
2013
+ Active
2014
+ </Text>
1626
2015
  <Text style={styles.dashboardGridLbl}>Status</Text>
1627
2016
  </View>
1628
2017
  <View style={styles.dashboardGridItem}>
1629
2018
  <Text numberOfLines={1} style={styles.dashboardGridVal}>
1630
- {webviewTotal > 0 ? `${webViewNavHistory[0]?.title?.substring(0, 10) ?? ''}...` : '—'}
2019
+ {webviewTotal > 0
2020
+ ? `${webViewNavHistory[0]?.title?.substring(0, 10) ?? ''}...`
2021
+ : '—'}
1631
2022
  </Text>
1632
2023
  <Text style={styles.dashboardGridLbl}>Last URL</Text>
1633
2024
  </View>
@@ -1635,38 +2026,73 @@ const NetworkInspector = ({ enabled = true }) => {
1635
2026
  </TouchableScale>)}
1636
2027
 
1637
2028
  {/* Module 5: Redux Store */}
1638
- {tabVisibility.redux && (<TouchableScale style={styles.dashboardModuleCard} onPress={() => setActiveTab('redux')}>
2029
+ {tabVisibility.redux && (<TouchableScale style={styles.dashboardModuleCard} onPress={() => switchActiveTab('redux')}>
1639
2030
  <View style={styles.dashboardModuleHeader}>
1640
2031
  <View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
1641
2032
  <TerminalIcon color={AppColors.purple} size={18}/>
1642
- <Text style={styles.dashboardModuleTitle}>Redux Store State</Text>
2033
+ <Text style={styles.dashboardModuleTitle}>
2034
+ Redux Store State
2035
+ </Text>
1643
2036
  </View>
1644
2037
  <Text style={styles.dashboardModuleGoText}>View Details →</Text>
1645
2038
  </View>
1646
2039
  {reduxState ? (<View style={{ paddingHorizontal: 12, paddingBottom: 12, gap: 6 }}>
1647
- <View style={{ flexDirection: 'row', justifyContent: 'space-between', marginBottom: 4 }}>
1648
- <Text style={{ fontFamily: AppFonts.interBold, fontSize: 10, color: AppColors.grayTextWeak, letterSpacing: 0.5 }}>
2040
+ <View style={{
2041
+ flexDirection: 'row',
2042
+ justifyContent: 'space-between',
2043
+ marginBottom: 4,
2044
+ }}>
2045
+ <Text style={{
2046
+ fontFamily: AppFonts.interBold,
2047
+ fontSize: 10,
2048
+ color: AppColors.grayTextWeak,
2049
+ letterSpacing: 0.5,
2050
+ }}>
1649
2051
  REDUCER NAME
1650
2052
  </Text>
1651
- <Text style={{ fontFamily: AppFonts.interBold, fontSize: 10, color: AppColors.grayTextWeak, letterSpacing: 0.5 }}>
2053
+ <Text style={{
2054
+ fontFamily: AppFonts.interBold,
2055
+ fontSize: 10,
2056
+ color: AppColors.grayTextWeak,
2057
+ letterSpacing: 0.5,
2058
+ }}>
1652
2059
  SIZE / FIELDS
1653
2060
  </Text>
1654
2061
  </View>
1655
2062
  {Object.keys(reduxState).map(key => {
1656
2063
  const val = reduxState[key];
1657
- const fieldsCount = typeof val === 'object' && val !== null ? Object.keys(val).length : 0;
2064
+ const fieldsCount = typeof val === 'object' && val !== null
2065
+ ? Object.keys(val).length
2066
+ : 0;
1658
2067
  const sizeStr = getSize(val);
1659
- return (<View key={key} style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingVertical: 2 }}>
1660
- <Text style={{ fontFamily: AppFonts.interMedium, fontSize: 12, color: AppColors.grayTextStrong }}>
2068
+ return (<View key={key} style={{
2069
+ flexDirection: 'row',
2070
+ justifyContent: 'space-between',
2071
+ alignItems: 'center',
2072
+ paddingVertical: 2,
2073
+ }}>
2074
+ <Text style={{
2075
+ fontFamily: AppFonts.interMedium,
2076
+ fontSize: 12,
2077
+ color: AppColors.grayTextStrong,
2078
+ }}>
1661
2079
  {key}
1662
2080
  </Text>
1663
- <Text style={{ fontFamily: AppFonts.interRegular, fontSize: 11, color: AppColors.grayTextWeak }}>
2081
+ <Text style={{
2082
+ fontFamily: AppFonts.interRegular,
2083
+ fontSize: 11,
2084
+ color: AppColors.grayTextWeak,
2085
+ }}>
1664
2086
  {sizeStr} ({fieldsCount} fields)
1665
2087
  </Text>
1666
2088
  </View>);
1667
2089
  })}
1668
2090
  </View>) : (<View style={{ padding: 12, alignItems: 'center' }}>
1669
- <Text style={{ fontFamily: AppFonts.interRegular, fontSize: 12, color: AppColors.grayTextWeak }}>
2091
+ <Text style={{
2092
+ fontFamily: AppFonts.interRegular,
2093
+ fontSize: 12,
2094
+ color: AppColors.grayTextWeak,
2095
+ }}>
1670
2096
  No connected Redux store.
1671
2097
  </Text>
1672
2098
  </View>)}
@@ -1721,10 +2147,19 @@ const NetworkInspector = ({ enabled = true }) => {
1721
2147
  <TerminalIcon color={AppColors.purple} size={20}/>
1722
2148
  </View>
1723
2149
  <View style={{ flex: 1 }}>
1724
- <Text style={{ fontFamily: AppFonts.interBold, fontSize: 13, color: AppColors.primaryBlack }}>
2150
+ <Text style={{
2151
+ fontFamily: AppFonts.interBold,
2152
+ fontSize: 13,
2153
+ color: AppColors.primaryBlack,
2154
+ }}>
1725
2155
  Redux Store Snapshot
1726
2156
  </Text>
1727
- <Text style={{ fontFamily: AppFonts.interRegular, fontSize: 11, color: AppColors.grayText, marginTop: 2 }}>
2157
+ <Text style={{
2158
+ fontFamily: AppFonts.interRegular,
2159
+ fontSize: 11,
2160
+ color: AppColors.grayText,
2161
+ marginTop: 2,
2162
+ }}>
1728
2163
  Total size: {getSize(reduxState)} • {reducerKeys.length} Reducers
1729
2164
  </Text>
1730
2165
  </View>
@@ -1742,23 +2177,33 @@ const NetworkInspector = ({ enabled = true }) => {
1742
2177
  borderWidth: 1,
1743
2178
  borderColor: AppColors.dividerColor,
1744
2179
  }}>
1745
- <TouchableOpacity onPress={() => setReduxActiveSubTab('timeline')} style={{
2180
+ <TouchableOpacity onPress={() => {
2181
+ animateNextLayout();
2182
+ setReduxActiveSubTab('timeline');
2183
+ }} style={{
1746
2184
  flex: 1,
1747
2185
  paddingVertical: 6,
1748
2186
  alignItems: 'center',
1749
2187
  justifyContent: 'center',
1750
2188
  borderRadius: 8,
1751
- backgroundColor: reduxActiveSubTab === 'timeline' ? AppColors.purple : 'transparent',
2189
+ backgroundColor: reduxActiveSubTab === 'timeline'
2190
+ ? AppColors.purple
2191
+ : 'transparent',
1752
2192
  }}>
1753
2193
  <Text style={{
1754
2194
  fontFamily: AppFonts.interBold,
1755
2195
  fontSize: 11,
1756
- color: reduxActiveSubTab === 'timeline' ? '#FFFFFF' : AppColors.grayText,
2196
+ color: reduxActiveSubTab === 'timeline'
2197
+ ? '#FFFFFF'
2198
+ : AppColors.grayText,
1757
2199
  }}>
1758
2200
  ⚡ Action Timeline
1759
2201
  </Text>
1760
2202
  </TouchableOpacity>
1761
- <TouchableOpacity onPress={() => setReduxActiveSubTab('tree')} style={{
2203
+ <TouchableOpacity onPress={() => {
2204
+ animateNextLayout();
2205
+ setReduxActiveSubTab('tree');
2206
+ }} style={{
1762
2207
  flex: 1,
1763
2208
  paddingVertical: 6,
1764
2209
  alignItems: 'center',
@@ -1789,7 +2234,9 @@ const NetworkInspector = ({ enabled = true }) => {
1789
2234
  borderColor: AppColors.dividerColor,
1790
2235
  height: 36,
1791
2236
  }}>
1792
- <TextInput placeholder={reduxActiveSubTab === 'timeline' ? "Search actions or payloads..." : "Search Redux keys or values..."} placeholderTextColor={AppColors.grayTextWeak} value={reduxSearch} onChangeText={setReduxSearch} style={{
2237
+ <TextInput placeholder={reduxActiveSubTab === 'timeline'
2238
+ ? 'Search actions or payloads...'
2239
+ : 'Search Redux keys or values...'} placeholderTextColor={AppColors.grayTextWeak} value={reduxSearch} onChangeText={setReduxSearch} style={{
1793
2240
  flex: 1,
1794
2241
  fontFamily: AppFonts.interRegular,
1795
2242
  fontSize: 12,
@@ -1835,23 +2282,24 @@ const NetworkInspector = ({ enabled = true }) => {
1835
2282
  <Modal visible={visible} animationType="slide" transparent>
1836
2283
  {visible && (<ErrorBoundary onClose={closeModal}>
1837
2284
  <View style={styles.modalBackdrop}>
1838
- <Pressable style={styles.modalBackdropPressable} onPress={closeModal}/>
1839
- <View style={styles.modalContentCard}>
1840
- <StatusBar translucent backgroundColor="transparent" barStyle="light-content"/>
2285
+ <Pressable style={styles.modalBackdropPressable} onPress={closeModal}/>
2286
+ <View style={styles.modalContentCard}>
2287
+ <StatusBar translucent backgroundColor="transparent" barStyle="light-content"/>
1841
2288
 
1842
- <LinearGradient colors={[AppColors.purple, '#6B4EFF']} style={styles.headerGradient}>
1843
- <View style={styles.header}>
1844
- <View style={[
2289
+ <LinearGradient colors={[AppColors.purple, '#6B4EFF']} style={styles.headerGradient}>
2290
+ <View style={styles.header}>
2291
+ <View style={[
1845
2292
  styles.headerLeft,
1846
2293
  {
1847
2294
  flexDirection: 'row',
1848
2295
  alignItems: 'center',
1849
2296
  gap: 16,
1850
- flex: (selected == null && selectedEvent == null) ? 5 : 1,
2297
+ flex: selected == null && selectedEvent == null ? 5 : 1,
1851
2298
  },
1852
2299
  ]}>
1853
- <TouchableScale onPress={() => {
2300
+ <TouchableScale onPress={() => {
1854
2301
  requestAnimationFrame(() => {
2302
+ animateNextLayout();
1855
2303
  setSelected(null);
1856
2304
  setSelectedEvent(null);
1857
2305
  });
@@ -1860,14 +2308,19 @@ const NetworkInspector = ({ enabled = true }) => {
1860
2308
  selected == null &&
1861
2309
  selectedEvent == null && { display: 'none' },
1862
2310
  ]}>
1863
- <WhiteBackNavigation />
1864
- </TouchableScale>
2311
+ <WhiteBackNavigation />
2312
+ </TouchableScale>
1865
2313
 
1866
- {selected == null && selectedEvent == null ? (<View style={{ flexDirection: 'row', alignItems: 'center', gap: 14, flex: 1 }}>
1867
- <View style={{
1868
- width: 42,
1869
- height: 42,
1870
- borderRadius: 10,
2314
+ {selected == null && selectedEvent == null ? (<View style={{
2315
+ flexDirection: 'row',
2316
+ alignItems: 'center',
2317
+ gap: 14,
2318
+ flex: 1,
2319
+ }}>
2320
+ <View style={{
2321
+ width: 42,
2322
+ height: 42,
2323
+ borderRadius: 10,
1871
2324
  backgroundColor: 'rgba(255,255,255,0.13)',
1872
2325
  borderWidth: 1.5,
1873
2326
  borderColor: 'rgba(255,255,255,0.25)',
@@ -1878,56 +2331,80 @@ const NetworkInspector = ({ enabled = true }) => {
1878
2331
  shadowRadius: 4,
1879
2332
  shadowOffset: { width: 0, height: 2 },
1880
2333
  }}>
1881
- <BrandSquareIcon size={36}/>
1882
- </View>
1883
- <View style={{ gap: 3 }}>
1884
- <Text style={[styles.headerTitle, { fontSize: 17, letterSpacing: 0.2 }]}>RN InApp Inspector</Text>
1885
- <View style={{ flexDirection: 'row', alignItems: 'center', gap: 5 }}>
1886
- <Animated.View style={{ width: 6, height: 6, borderRadius: 3, backgroundColor: '#4ADE80', opacity: activePulseAnim }}/>
1887
- <Text style={{ fontFamily: AppFonts.interMedium, fontSize: 10, color: 'rgba(255,255,255,0.78)', letterSpacing: 0.3 }}>
1888
- Active {Platform.OS === 'ios' ? 'iOS' : 'Android'} (v1.0.13)
2334
+ <BrandSquareIcon size={36}/>
2335
+ </View>
2336
+ <View style={{ gap: 3 }}>
2337
+ <Text style={[
2338
+ styles.headerTitle,
2339
+ { fontSize: 17, letterSpacing: 0.2 },
2340
+ ]}>
2341
+ RN InApp Inspector
1889
2342
  </Text>
2343
+ <View style={{
2344
+ flexDirection: 'row',
2345
+ alignItems: 'center',
2346
+ gap: 5,
2347
+ }}>
2348
+ <Animated.View style={{
2349
+ width: 6,
2350
+ height: 6,
2351
+ borderRadius: 3,
2352
+ backgroundColor: '#4ADE80',
2353
+ opacity: activePulseAnim,
2354
+ }}/>
2355
+ <Text style={{
2356
+ fontFamily: AppFonts.interMedium,
2357
+ fontSize: 10,
2358
+ color: 'rgba(255,255,255,0.78)',
2359
+ letterSpacing: 0.3,
2360
+ }}>
2361
+ Active •{' '}
2362
+ {Platform.OS === 'ios' ? 'iOS' : 'Android'}{' '}
2363
+ (v1.0.13)
2364
+ </Text>
2365
+ </View>
1890
2366
  </View>
1891
- </View>
1892
- </View>) : null}
1893
- </View>
2367
+ </View>) : null}
2368
+ </View>
1894
2369
 
1895
- <View style={styles.headerCenter}>
1896
- {selected != null ? (<View style={styles.headerDetailCenter}>
1897
- <View style={styles.headerDetailRow}>
1898
- <View style={[
2370
+ <View style={styles.headerCenter}>
2371
+ {selected != null ? (<View style={styles.headerDetailCenter}>
2372
+ <View style={styles.headerDetailRow}>
2373
+ <View style={[
1899
2374
  styles.headerMethodBadge,
1900
2375
  {
1901
2376
  backgroundColor: METHOD_COLORS[selected.method] ??
1902
2377
  AppColors.grayText,
1903
2378
  },
1904
2379
  ]}>
1905
- <Text style={styles.headerMethodText}>
1906
- {selected.method}
2380
+ <Text style={styles.headerMethodText}>
2381
+ {selected.method}
2382
+ </Text>
2383
+ </View>
2384
+ <Text style={styles.headerDetailTitle} numberOfLines={1} ellipsizeMode="middle">
2385
+ {detailTitle}
1907
2386
  </Text>
1908
2387
  </View>
1909
- <Text style={styles.headerDetailTitle} numberOfLines={1} ellipsizeMode="middle">
1910
- {detailTitle}
1911
- </Text>
1912
- </View>
1913
- <View style={styles.headerDetailSubRow}>
1914
- <View style={[
2388
+ <View style={styles.headerDetailSubRow}>
2389
+ <View style={[
1915
2390
  styles.headerStatusDot,
1916
- { backgroundColor: getStatusColor(selected.status) },
2391
+ {
2392
+ backgroundColor: getStatusColor(selected.status),
2393
+ },
1917
2394
  ]}/>
1918
- <Text style={styles.headerSubTitle}>
1919
- {selected.status === 0
2395
+ <Text style={styles.headerSubTitle}>
2396
+ {selected.status === 0
1920
2397
  ? 'Failed'
1921
2398
  : selected.status ?? 'Pending'}{' '}
1922
- •{' '}
1923
- {selected.duration != null
2399
+ •{' '}
2400
+ {selected.duration != null
1924
2401
  ? `${selected.duration}ms`
1925
2402
  : '-'}
1926
- </Text>
1927
- </View>
1928
- </View>) : selectedEvent != null ? (<View style={styles.headerDetailCenter}>
1929
- <View style={styles.headerDetailRow}>
1930
- <View style={[
2403
+ </Text>
2404
+ </View>
2405
+ </View>) : selectedEvent != null ? (<View style={styles.headerDetailCenter}>
2406
+ <View style={styles.headerDetailRow}>
2407
+ <View style={[
1931
2408
  styles.headerMethodBadge,
1932
2409
  {
1933
2410
  backgroundColor: selectedEvent.source === 'firebase'
@@ -1935,16 +2412,18 @@ const NetworkInspector = ({ enabled = true }) => {
1935
2412
  : 'rgba(124,92,191,0.3)',
1936
2413
  },
1937
2414
  ]}>
1938
- <Text style={styles.headerMethodText}>
1939
- {selectedEvent.source === 'firebase' ? 'FB' : 'MAN'}
2415
+ <Text style={styles.headerMethodText}>
2416
+ {selectedEvent.source === 'firebase'
2417
+ ? 'FB'
2418
+ : 'MAN'}
2419
+ </Text>
2420
+ </View>
2421
+ <Text style={styles.headerDetailTitle} numberOfLines={1} ellipsizeMode="middle">
2422
+ {selectedEvent.name}
1940
2423
  </Text>
1941
2424
  </View>
1942
- <Text style={styles.headerDetailTitle} numberOfLines={1} ellipsizeMode="middle">
1943
- {selectedEvent.name}
1944
- </Text>
1945
- </View>
1946
- <View style={styles.headerDetailSubRow}>
1947
- <View style={[
2425
+ <View style={styles.headerDetailSubRow}>
2426
+ <View style={[
1948
2427
  styles.headerStatusDot,
1949
2428
  {
1950
2429
  backgroundColor: selectedEvent.source === 'firebase'
@@ -1952,68 +2431,119 @@ const NetworkInspector = ({ enabled = true }) => {
1952
2431
  : AppColors.purple,
1953
2432
  },
1954
2433
  ]}/>
1955
- <Text style={styles.headerSubTitle}>
1956
- {Object.keys(selectedEvent.params).length} param
1957
- {Object.keys(selectedEvent.params).length !== 1
2434
+ <Text style={styles.headerSubTitle}>
2435
+ {Object.keys(selectedEvent.params).length} param
2436
+ {Object.keys(selectedEvent.params).length !== 1
1958
2437
  ? 's'
1959
2438
  : ''}
1960
- {' · '}
1961
- {selectedEvent.source}
1962
- </Text>
1963
- </View>
1964
- </View>) : null}
1965
- </View>
2439
+ {' · '}
2440
+ {selectedEvent.source}
2441
+ </Text>
2442
+ </View>
2443
+ </View>) : null}
2444
+ </View>
1966
2445
 
1967
- <View style={styles.headerRight}>
1968
- {selected == null && selectedEvent == null && (<TouchableScale onPress={() => setSettingsPage('main')} hitSlop={15} style={[styles.closeButtonSquare, { marginRight: 8, backgroundColor: 'rgba(255,255,255,0.15)' }]}>
1969
- <SettingsIcon color="#FFFFFF" size={16}/>
1970
- </TouchableScale>)}
2446
+ <View style={styles.headerRight}>
2447
+ {selected == null && selectedEvent == null && (<TouchableScale onPress={() => setSettingsPage('main')} hitSlop={15} style={[
2448
+ styles.closeButtonSquare,
2449
+ {
2450
+ marginRight: 8,
2451
+ backgroundColor: 'rgba(255,255,255,0.15)',
2452
+ },
2453
+ ]}>
2454
+ <SettingsIcon color="#FFFFFF" size={16}/>
2455
+ </TouchableScale>)}
1971
2456
 
1972
- <TouchableScale onPress={closeModal} hitSlop={15} style={styles.closeButtonSquare}>
1973
- <CloseWhite size={16}/>
1974
- </TouchableScale>
2457
+ <TouchableScale onPress={closeModal} hitSlop={15} style={styles.closeButtonSquare}>
2458
+ <CloseWhite size={16}/>
2459
+ </TouchableScale>
2460
+ </View>
1975
2461
  </View>
1976
- </View>
1977
- </LinearGradient>
1978
-
1979
- {/* ─── Horizontal Scrollable Tab Bar inside Content ─── */}
1980
- {selected == null && selectedEvent == null && settingsPage === null ? (<View style={styles.tabBarContainer}>
1981
- <ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={{ paddingRight: 16 }}>
1982
- {[
1983
- { key: 'insights', label: 'Insights', count: 0, icon: 'insights' },
1984
- { key: 'apis', label: 'APIs', count: logs.length, icon: 'apis' },
1985
- { key: 'logs', label: 'Logs', count: consoleLogs.length, icon: 'logs' },
1986
- { key: 'analytics', label: 'Analytics', count: analyticsEvents.length, icon: 'analytics' },
1987
- { key: 'webview', label: 'WebView', count: webViewNavHistory.length, icon: 'webview' },
1988
- { key: 'redux', label: 'Redux', count: 0, icon: 'redux' },
1989
- ].filter(tab => tabVisibility[tab.key]).map(tab => {
2462
+ </LinearGradient>
2463
+
2464
+ {/* ─── Horizontal Scrollable Tab Bar inside Content ─── */}
2465
+ {selected == null &&
2466
+ selectedEvent == null &&
2467
+ settingsPage === null ? (<View style={styles.tabBarContainer}>
2468
+ <ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={{ paddingRight: 16 }}>
2469
+ {[
2470
+ {
2471
+ key: 'insights',
2472
+ label: 'Insights',
2473
+ count: 0,
2474
+ icon: 'insights',
2475
+ },
2476
+ {
2477
+ key: 'apis',
2478
+ label: 'APIs',
2479
+ count: logs.length,
2480
+ icon: 'apis',
2481
+ },
2482
+ {
2483
+ key: 'logs',
2484
+ label: 'Logs',
2485
+ count: consoleLogs.length,
2486
+ icon: 'logs',
2487
+ },
2488
+ {
2489
+ key: 'analytics',
2490
+ label: 'Analytics',
2491
+ count: analyticsEvents.length,
2492
+ icon: 'analytics',
2493
+ },
2494
+ {
2495
+ key: 'webview',
2496
+ label: 'WebView',
2497
+ count: webViewNavHistory.length,
2498
+ icon: 'webview',
2499
+ },
2500
+ {
2501
+ key: 'redux',
2502
+ label: 'Redux',
2503
+ count: 0,
2504
+ icon: 'redux',
2505
+ },
2506
+ ]
2507
+ .filter(tab => tabVisibility[tab.key])
2508
+ .map(tab => {
1990
2509
  const isActive = activeTab === tab.key;
1991
- const iconColor = isActive ? '#FFFFFF' : AppColors.grayText;
2510
+ const iconColor = isActive
2511
+ ? '#FFFFFF'
2512
+ : AppColors.grayText;
1992
2513
  const countLabel = tab.count > 9 ? '9+' : String(tab.count);
1993
- const hasUnreadApis = activeTab !== 'apis' && logs.length > lastReadApisCount;
1994
- const hasUnreadLogs = activeTab !== 'logs' && consoleLogs.length > lastReadLogsCount;
2514
+ const hasUnreadApis = activeTab !== 'apis' &&
2515
+ logs.length > lastReadApisCount;
2516
+ const hasUnreadLogs = activeTab !== 'logs' &&
2517
+ consoleLogs.length > lastReadLogsCount;
1995
2518
  return (<TouchableScale key={tab.key} onPress={() => {
1996
2519
  requestAnimationFrame(() => {
1997
- setActiveTab(tab.key);
2520
+ switchActiveTab(tab.key);
1998
2521
  });
1999
2522
  }} style={[
2000
2523
  styles.contentTabButton,
2001
2524
  isActive && styles.contentTabButtonActive,
2002
2525
  ]}>
2003
- <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
2004
- {tab.icon === 'insights' && <InsightsIcon color={iconColor} size={14}/>}
2005
- {tab.icon === 'apis' && <SignalIcon color={iconColor} size={14}/>}
2006
- {tab.icon === 'logs' && <TerminalIcon color={iconColor} size={14}/>}
2007
- {tab.icon === 'analytics' && <AnalyticsIcon color={iconColor} size={14}/>}
2008
- {tab.icon === 'webview' && <GlobeIcon color={iconColor} size={14}/>}
2009
- {tab.icon === 'redux' && <TerminalIcon color={iconColor} size={14}/>}
2010
- <Text numberOfLines={1} ellipsizeMode="tail" style={[
2526
+ <View style={{
2527
+ flexDirection: 'row',
2528
+ alignItems: 'center',
2529
+ gap: 6,
2530
+ }}>
2531
+ {tab.icon === 'insights' && (<InsightsIcon color={iconColor} size={14}/>)}
2532
+ {tab.icon === 'apis' && (<SignalIcon color={iconColor} size={14}/>)}
2533
+ {tab.icon === 'logs' && (<TerminalIcon color={iconColor} size={14}/>)}
2534
+ {tab.icon === 'analytics' && (<AnalyticsIcon color={iconColor} size={14}/>)}
2535
+ {tab.icon === 'webview' && (<GlobeIcon color={iconColor} size={14}/>)}
2536
+ {tab.icon === 'redux' && (<TerminalIcon color={iconColor} size={14}/>)}
2537
+ <Text numberOfLines={1} ellipsizeMode="tail" style={[
2011
2538
  styles.contentTabButtonText,
2012
- isActive && styles.contentTabButtonTextActive,
2539
+ isActive &&
2540
+ styles.contentTabButtonTextActive,
2013
2541
  ]}>
2014
- {tab.label} {tab.count > 0 ? `(${countLabel})` : ''}
2015
- </Text>
2016
- {((tab.key === 'apis' && hasUnreadApis) || (tab.key === 'logs' && hasUnreadLogs)) && (<Animated.View style={{
2542
+ {tab.label}{' '}
2543
+ {tab.count > 0 ? `(${countLabel})` : ''}
2544
+ </Text>
2545
+ {((tab.key === 'apis' && hasUnreadApis) ||
2546
+ (tab.key === 'logs' && hasUnreadLogs)) && (<Animated.View style={{
2017
2547
  width: 6,
2018
2548
  height: 6,
2019
2549
  borderRadius: 3,
@@ -2022,42 +2552,45 @@ const NetworkInspector = ({ enabled = true }) => {
2022
2552
  alignSelf: 'center',
2023
2553
  transform: [{ scale: unreadPulseAnim }],
2024
2554
  }}/>)}
2025
- </View>
2026
- </TouchableScale>);
2555
+ </View>
2556
+ </TouchableScale>);
2027
2557
  })}
2028
- </ScrollView>
2029
- </View>) : null}
2030
-
2031
-
2558
+ </ScrollView>
2559
+ </View>) : null}
2032
2560
 
2033
- {/* ─── Secondary Tab Bar for Analytics ──────────────────────── */}
2034
- {isReady && activeTab === 'analytics' && selectedEvent == null && (<View>
2035
- {/* ─── Search + Shared Toolbar for Analytics ──────────────────────── */}
2036
- <View style={[styles.toolbarRow, { marginTop: 12, marginBottom: 4 }]}>
2037
- <View style={styles.searchContainer}>
2038
- <SearchIcon color={AppColors.grayTextWeak} size={16}/>
2039
- <TextInput placeholder="Search events..." placeholderTextColor={AppColors.grayTextWeak} value={analyticsSearch} onChangeText={setAnalyticsSearch} style={styles.searchInput} autoCorrect={false} autoCapitalize="none"/>
2040
- {analyticsSearch.length > 0 && (<Pressable onPress={() => setAnalyticsSearch('')} hitSlop={10} style={styles.clearBtn}>
2041
- <ClearIcon color={AppColors.grayTextWeak} size={14}/>
2042
- </Pressable>)}
2043
- </View>
2044
- {analyticsSubTab === 'ga_events' && (<View style={styles.toolbarRight}>
2045
- <TouchableScale style={[
2561
+ {/* ─── Secondary Tab Bar for Analytics ──────────────────────── */}
2562
+ {isReady &&
2563
+ activeTab === 'analytics' &&
2564
+ selectedEvent == null && (<View>
2565
+ {/* ─── Search + Shared Toolbar for Analytics ──────────────────────── */}
2566
+ <View style={[
2567
+ styles.toolbarRow,
2568
+ { marginTop: 12, marginBottom: 4 },
2569
+ ]}>
2570
+ <View style={styles.searchContainer}>
2571
+ <SearchIcon color={AppColors.grayTextWeak} size={16}/>
2572
+ <TextInput placeholder="Search events..." placeholderTextColor={AppColors.grayTextWeak} value={analyticsSearch} onChangeText={setAnalyticsSearch} style={styles.searchInput} autoCorrect={false} autoCapitalize="none"/>
2573
+ {analyticsSearch.length > 0 && (<Pressable onPress={() => setAnalyticsSearch('')} hitSlop={10} style={styles.clearBtn}>
2574
+ <ClearIcon color={AppColors.grayTextWeak} size={14}/>
2575
+ </Pressable>)}
2576
+ </View>
2577
+ {analyticsSubTab === 'ga_events' && (<View style={styles.toolbarRight}>
2578
+ <TouchableScale style={[
2046
2579
  styles.toolbarBtn,
2047
2580
  !hideScreenView && styles.toolbarBtnActive,
2048
2581
  ]} onPress={() => setHideScreenView(prev => !prev)} hitSlop={10}>
2049
- <ScreenIcon color={!hideScreenView
2582
+ <ScreenIcon color={!hideScreenView
2050
2583
  ? AppColors.purple
2051
2584
  : AppColors.grayTextStrong} size={18}/>
2052
- </TouchableScale>
2053
- <TouchableScale style={styles.toolbarBtn} onPress={handleDelete} hitSlop={10}>
2054
- <TrashIcon color={AppColors.grayTextStrong} size={18}/>
2055
- </TouchableScale>
2056
- </View>)}
2057
- </View>
2585
+ </TouchableScale>
2586
+ <TouchableScale style={styles.toolbarBtn} onPress={handleDelete} hitSlop={10}>
2587
+ <TrashIcon color={AppColors.grayTextStrong} size={18}/>
2588
+ </TouchableScale>
2589
+ </View>)}
2590
+ </View>
2058
2591
 
2059
- {/* ─── Secondary Tab Bar for Analytics ──────────────────────── */}
2060
- <View style={{
2592
+ {/* ─── Secondary Tab Bar for Analytics ──────────────────────── */}
2593
+ <View style={{
2061
2594
  marginHorizontal: 16,
2062
2595
  marginTop: 4,
2063
2596
  marginBottom: 8,
@@ -2068,7 +2601,7 @@ const NetworkInspector = ({ enabled = true }) => {
2068
2601
  borderWidth: 1,
2069
2602
  borderColor: AppColors.grayBorderSecondary,
2070
2603
  }}>
2071
- <Pressable style={[
2604
+ <Pressable style={[
2072
2605
  {
2073
2606
  flex: 1,
2074
2607
  paddingVertical: 8,
@@ -2083,8 +2616,11 @@ const NetworkInspector = ({ enabled = true }) => {
2083
2616
  shadowOffset: { width: 0, height: 1 },
2084
2617
  elevation: 2,
2085
2618
  },
2086
- ]} onPress={() => setAnalyticsSubTab('ga_events')}>
2087
- <Text style={[
2619
+ ]} onPress={() => {
2620
+ animateNextLayout();
2621
+ setAnalyticsSubTab('ga_events');
2622
+ }}>
2623
+ <Text style={[
2088
2624
  {
2089
2625
  fontFamily: AppFonts.interMedium,
2090
2626
  fontSize: 13,
@@ -2095,14 +2631,14 @@ const NetworkInspector = ({ enabled = true }) => {
2095
2631
  color: AppColors.purple,
2096
2632
  },
2097
2633
  ]}>
2098
- GA Events (
2099
- {analyticsSearch
2634
+ GA Events (
2635
+ {analyticsSearch
2100
2636
  ? filteredAnalyticsEvents.length
2101
2637
  : analyticsEvents.length}
2102
- )
2103
- </Text>
2104
- </Pressable>
2105
- <Pressable style={[
2638
+ )
2639
+ </Text>
2640
+ </Pressable>
2641
+ <Pressable style={[
2106
2642
  {
2107
2643
  flex: 1,
2108
2644
  paddingVertical: 8,
@@ -2117,8 +2653,11 @@ const NetworkInspector = ({ enabled = true }) => {
2117
2653
  shadowOffset: { width: 0, height: 1 },
2118
2654
  elevation: 2,
2119
2655
  },
2120
- ]} onPress={() => setAnalyticsSubTab('top_events')}>
2121
- <Text style={[
2656
+ ]} onPress={() => {
2657
+ animateNextLayout();
2658
+ setAnalyticsSubTab('top_events');
2659
+ }}>
2660
+ <Text style={[
2122
2661
  {
2123
2662
  fontFamily: AppFonts.interMedium,
2124
2663
  fontSize: 13,
@@ -2129,63 +2668,63 @@ const NetworkInspector = ({ enabled = true }) => {
2129
2668
  color: AppColors.purple,
2130
2669
  },
2131
2670
  ]}>
2132
- Top Events ({topEventsArray.length})
2133
- </Text>
2134
- </Pressable>
2135
- </View>
2136
- </View>)}
2671
+ Top Events ({topEventsArray.length})
2672
+ </Text>
2673
+ </Pressable>
2674
+ </View>
2675
+ </View>)}
2137
2676
 
2138
- {isReady ? (activeTab === 'insights' ? (<ScrollView style={styles.insightsContainer} contentContainerStyle={styles.insightsContent} showsVerticalScrollIndicator={false}>
2139
- {renderInsightsDashboard()}
2140
- </ScrollView>) : activeTab === 'analytics' ? (selectedEvent != null ? (<AnalyticsDetail event={selectedEvent}/>) : analyticsSubTab === 'top_events' ? (<FlatList data={topEventsArray} keyExtractor={item => item[0]} contentContainerStyle={[
2677
+ {isReady ? (activeTab === 'insights' ? (<ScrollView style={styles.insightsContainer} contentContainerStyle={styles.insightsContent} showsVerticalScrollIndicator={false}>
2678
+ {renderInsightsDashboard()}
2679
+ </ScrollView>) : activeTab === 'analytics' ? (selectedEvent != null ? (<AnalyticsDetail event={selectedEvent}/>) : analyticsSubTab === 'top_events' ? (<FlatList data={topEventsArray} keyExtractor={item => item[0]} contentContainerStyle={[
2141
2680
  styles.listContent,
2142
2681
  { paddingHorizontal: 16, paddingTop: 16 },
2143
- ]} renderItem={({ item: [name, count] }) => {
2682
+ ]} renderItem={({ item: [name, count], index }) => {
2144
2683
  const maxCount = topEventsArray[0]?.[1] || 1;
2145
2684
  const color = getEventColor(name);
2146
- return (<View style={[
2685
+ return (<AnimatedEntrance index={index} distance={8} style={[
2147
2686
  styles.analyticsTopEventsCard,
2148
2687
  { marginBottom: 12, paddingVertical: 16 },
2149
2688
  ]}>
2150
- <View style={styles.analyticsTopEventRow}>
2151
- <View style={{
2689
+ <View style={styles.analyticsTopEventRow}>
2690
+ <View style={{
2152
2691
  flexDirection: 'row',
2153
2692
  alignItems: 'center',
2154
2693
  gap: 8,
2155
2694
  flex: 1,
2156
2695
  }}>
2157
- <View style={[
2696
+ <View style={[
2158
2697
  styles.analyticsIconCircle,
2159
2698
  { backgroundColor: `${color}1A` },
2160
2699
  ]}>
2161
- <Svg width={14} height={14} viewBox="0 0 24 24" fill={color}>
2162
- <Circle cx="12" cy="12" r="10" opacity="0.3"/>
2163
- <Path d="M7 14l3-3 4 4 6-6" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" fill="none"/>
2164
- </Svg>
2165
- </View>
2166
- <Text style={styles.analyticsTopEventName} numberOfLines={2}>
2167
- {name}
2168
- </Text>
2169
- </View>
2170
- <View style={styles.analyticsTopEventBarWrap}>
2171
- <LinearGradient colors={[color, `${color}99`]} start={{ x: 0, y: 0 }} end={{ x: 1, y: 0 }} style={[
2700
+ <Svg width={14} height={14} viewBox="0 0 24 24" fill={color}>
2701
+ <Circle cx="12" cy="12" r="10" opacity="0.3"/>
2702
+ <Path d="M7 14l3-3 4 4 6-6" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" fill="none"/>
2703
+ </Svg>
2704
+ </View>
2705
+ <Text style={styles.analyticsTopEventName} numberOfLines={2}>
2706
+ {name}
2707
+ </Text>
2708
+ </View>
2709
+ <View style={styles.analyticsTopEventBarWrap}>
2710
+ <LinearGradient colors={[color, `${color}99`]} start={{ x: 0, y: 0 }} end={{ x: 1, y: 0 }} style={[
2172
2711
  styles.analyticsTopEventBar,
2173
2712
  {
2174
2713
  width: `${Math.max(6, (count / maxCount) * 100)}%`,
2175
2714
  },
2176
2715
  ]}/>
2177
- </View>
2178
- <Text style={styles.analyticsTopEventCount}>
2179
- {count}
2180
- </Text>
2181
- </View>
2182
- </View>);
2716
+ </View>
2717
+ <Text style={styles.analyticsTopEventCount}>
2718
+ {count}
2719
+ </Text>
2720
+ </View>
2721
+ </AnimatedEntrance>);
2183
2722
  }} ListEmptyComponent={<View style={styles.emptyContainer}>
2184
- <View style={styles.emptyIconWrap}>
2185
- <EmptyRadarIcon color={AppColors.purple} size={32}/>
2186
- </View>
2187
- <Text style={styles.emptyTitle}>No Top Events</Text>
2188
- </View>}/>) : (<FlatList data={filteredAnalyticsEvents} keyExtractor={item => item.id.toString()} renderItem={({ item, index }) => {
2723
+ <View style={styles.emptyIconWrap}>
2724
+ <EmptyRadarIcon color={AppColors.purple} size={32}/>
2725
+ </View>
2726
+ <Text style={styles.emptyTitle}>No Top Events</Text>
2727
+ </View>}/>) : (<FlatList data={filteredAnalyticsEvents} keyExtractor={item => item.id.toString()} renderItem={({ item, index }) => {
2189
2728
  const prev = filteredAnalyticsEvents[index + 1];
2190
2729
  const next = filteredAnalyticsEvents[index - 1];
2191
2730
  const msSincePrev = prev
@@ -2196,7 +2735,11 @@ const NetworkInspector = ({ enabled = true }) => {
2196
2735
  ? Math.floor(next.timestamp / 60000)
2197
2736
  : -1;
2198
2737
  const showTimestamp = index === 0 || thisMin !== nextMin;
2199
- return (<AnalyticsEventCard event={item} onPress={() => setSelectedEvent(item)} isNew={newEventIds.has(item.id)} searchStr={analyticsSearch} isFirst={index === 0} isLast={index === filteredAnalyticsEvents.length - 1} msSincePrev={msSincePrev} showTimestamp={showTimestamp} computedScreenName={(() => {
2738
+ return (<AnimatedEntrance index={index} distance={8}>
2739
+ <AnalyticsEventCard event={item} onPress={() => {
2740
+ animateNextLayout();
2741
+ setSelectedEvent(item);
2742
+ }} isNew={newEventIds.has(item.id)} searchStr={analyticsSearch} isFirst={index === 0} isLast={index === filteredAnalyticsEvents.length - 1} msSincePrev={msSincePrev} showTimestamp={showTimestamp} computedScreenName={(() => {
2200
2743
  let screenName = item.screenName ||
2201
2744
  item.screenClass ||
2202
2745
  item.pageTitle ||
@@ -2214,69 +2757,71 @@ const NetworkInspector = ({ enabled = true }) => {
2214
2757
  }
2215
2758
  }
2216
2759
  return screenName;
2217
- })()}/>);
2760
+ })()}/>
2761
+ </AnimatedEntrance>);
2218
2762
  }} initialNumToRender={20} maxToRenderPerBatch={20} windowSize={5} removeClippedSubviews ListEmptyComponent={<View style={styles.emptyContainer}>
2219
- <View style={styles.emptyIconWrap}>
2220
- <EmptyRadarIcon color={AppColors.purple} size={32}/>
2221
- </View>
2222
- <Text style={styles.emptyTitle}>
2223
- {analyticsSearch.length > 0
2763
+ <View style={styles.emptyIconWrap}>
2764
+ <EmptyRadarIcon color={AppColors.purple} size={32}/>
2765
+ </View>
2766
+ <Text style={styles.emptyTitle}>
2767
+ {analyticsSearch.length > 0
2224
2768
  ? 'No matching events'
2225
2769
  : 'No analytics events yet'}
2226
- </Text>
2227
- <Text style={styles.emptySub}>
2228
- {analyticsSearch.length > 0
2770
+ </Text>
2771
+ <Text style={styles.emptySub}>
2772
+ {analyticsSearch.length > 0
2229
2773
  ? 'Try adjusting your search.'
2230
2774
  : 'Call setupAnalyticsLogger(analytics()) at app start.'}
2231
- </Text>
2232
- </View>} contentContainerStyle={[
2775
+ </Text>
2776
+ </View>} contentContainerStyle={[
2233
2777
  styles.listContent,
2234
- filteredAnalyticsEvents.length === 0 && { flexGrow: 1 },
2778
+ filteredAnalyticsEvents.length === 0 && {
2779
+ flexGrow: 1,
2780
+ },
2235
2781
  ]} keyboardShouldPersistTaps="handled"/>)) : activeTab === 'apis' && selected == null ? (<FlatList data={groupedData} keyExtractor={item => item?.id?.toString()} renderItem={renderItem} initialNumToRender={10} maxToRenderPerBatch={10} windowSize={5} removeClippedSubviews={true} ListHeaderComponent={<View style={{ marginTop: 8 }}>
2236
- <View style={styles.toolbarRow}>
2237
- <View style={styles.searchContainer}>
2238
- <SearchIcon color={AppColors.grayTextWeak} size={16}/>
2239
- <TextInput placeholder="Search endpoints..." placeholderTextColor={AppColors.grayTextWeak} value={search} onChangeText={setSearch} style={styles.searchInput} autoCorrect={false} autoCapitalize="none"/>
2240
- {search.length > 0 && (<Pressable onPress={() => setSearch('')} hitSlop={10} style={styles.clearBtn}>
2241
- <ClearIcon color={AppColors.grayTextWeak} size={14}/>
2242
- </Pressable>)}
2243
- </View>
2244
-
2245
- <View style={styles.toolbarRight}>
2246
- <TouchableScale style={styles.toolbarBtn} onPress={handleDelete} hitSlop={10}>
2247
- <TrashIcon color={AppColors.grayTextStrong} size={18}/>
2248
- {selectedLogs.size > 0 && (<View style={styles.trashBadge}>
2249
- <Text style={styles.trashBadgeText}>
2250
- {selectedLogs.size}
2251
- </Text>
2252
- </View>)}
2253
- </TouchableScale>
2782
+ <View style={styles.toolbarRow}>
2783
+ <View style={styles.searchContainer}>
2784
+ <SearchIcon color={AppColors.grayTextWeak} size={16}/>
2785
+ <TextInput placeholder="Search endpoints..." placeholderTextColor={AppColors.grayTextWeak} value={search} onChangeText={setSearch} style={styles.searchInput} autoCorrect={false} autoCapitalize="none"/>
2786
+ {search.length > 0 && (<Pressable onPress={() => setSearch('')} hitSlop={10} style={styles.clearBtn}>
2787
+ <ClearIcon color={AppColors.grayTextWeak} size={14}/>
2788
+ </Pressable>)}
2789
+ </View>
2254
2790
 
2791
+ <View style={styles.toolbarRight}>
2792
+ <TouchableScale style={styles.toolbarBtn} onPress={handleDelete} hitSlop={10}>
2793
+ <TrashIcon color={AppColors.grayTextStrong} size={18}/>
2794
+ {selectedLogs.size > 0 && (<View style={styles.trashBadge}>
2795
+ <Text style={styles.trashBadgeText}>
2796
+ {selectedLogs.size}
2797
+ </Text>
2798
+ </View>)}
2799
+ </TouchableScale>
2255
2800
 
2256
- <TouchableScale style={styles.toolbarBtn} onPress={() => setSortOrder(o => o === 'newest' ? 'oldest' : 'newest')} hitSlop={10}>
2257
- <SortArrowIcon direction={sortOrder === 'newest' ? 'down' : 'up'} color={AppColors.grayTextStrong} size={18}/>
2258
- </TouchableScale>
2801
+ <TouchableScale style={styles.toolbarBtn} onPress={() => setSortOrder(o => o === 'newest' ? 'oldest' : 'newest')} hitSlop={10}>
2802
+ <SortArrowIcon direction={sortOrder === 'newest' ? 'down' : 'up'} color={AppColors.grayTextStrong} size={18}/>
2803
+ </TouchableScale>
2259
2804
 
2260
- <TouchableScale style={[
2805
+ <TouchableScale style={[
2261
2806
  styles.toolbarBtn,
2262
2807
  filtersAccordion.isOpen &&
2263
2808
  styles.toolbarBtnActive,
2264
2809
  ]} onPress={filtersAccordion.toggleOpen} hitSlop={10}>
2265
- <FilterIcon color={filtersAccordion.isOpen
2810
+ <FilterIcon color={filtersAccordion.isOpen
2266
2811
  ? AppColors.purple
2267
2812
  : AppColors.grayTextStrong} size={18}/>
2268
- </TouchableScale>
2813
+ </TouchableScale>
2814
+ </View>
2269
2815
  </View>
2270
- </View>
2271
2816
 
2272
- <Animated.View style={[
2817
+ <Animated.View style={[
2273
2818
  filtersAccordion.bodyStyle,
2274
2819
  { overflow: 'hidden' },
2275
2820
  ]}>
2276
- <View style={styles.filtersContainer}>
2277
- <Text style={styles.filtersHeading}>STATUS</Text>
2278
- <ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={styles.statusRowContent}>
2279
- {STATUS_FILTERS.map(filter => {
2821
+ <View style={styles.filtersContainer}>
2822
+ <Text style={styles.filtersHeading}>STATUS</Text>
2823
+ <ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={styles.statusRowContent}>
2824
+ {STATUS_FILTERS.map(filter => {
2280
2825
  const isAll = filter === 'ALL';
2281
2826
  const active = isAll
2282
2827
  ? statusFilters.size === 0
@@ -2295,35 +2840,38 @@ const NetworkInspector = ({ enabled = true }) => {
2295
2840
  });
2296
2841
  }
2297
2842
  }} hitSlop={10}>
2298
- {active ? (<View style={[
2843
+ {active ? (<View style={[
2299
2844
  styles.statusFilterChip,
2300
2845
  styles.statusFilterActive,
2301
2846
  { overflow: 'hidden' },
2302
2847
  ]}>
2303
- <LinearGradient colors={[
2848
+ <LinearGradient colors={[
2304
2849
  AppColors.purpleShade50,
2305
2850
  '#EAE5FF',
2306
2851
  ]} style={StyleSheet.absoluteFill} start={{ x: 0, y: 0 }} end={{ x: 1, y: 0 }}/>
2307
- <Text style={[
2852
+ <Text style={[
2308
2853
  styles.statusFilterText,
2309
2854
  { color: AppColors.purple },
2310
2855
  ]}>
2311
- {filter}
2312
- </Text>
2313
- </View>) : (<View style={styles.statusFilterChip}>
2314
- <Text style={styles.statusFilterText}>
2315
- {filter}
2316
- </Text>
2317
- </View>)}
2318
- </TouchableScale>);
2856
+ {filter}
2857
+ </Text>
2858
+ </View>) : (<View style={styles.statusFilterChip}>
2859
+ <Text style={styles.statusFilterText}>
2860
+ {filter}
2861
+ </Text>
2862
+ </View>)}
2863
+ </TouchableScale>);
2319
2864
  })}
2320
- </ScrollView>
2865
+ </ScrollView>
2321
2866
 
2322
- <Text style={[styles.filtersHeading, { marginTop: 16 }]}>
2323
- METHOD
2324
- </Text>
2325
- <ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={styles.statusRowContent}>
2326
- {availableMethods.map(filter => {
2867
+ <Text style={[
2868
+ styles.filtersHeading,
2869
+ { marginTop: 16 },
2870
+ ]}>
2871
+ METHOD
2872
+ </Text>
2873
+ <ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={styles.statusRowContent}>
2874
+ {availableMethods.map(filter => {
2327
2875
  const isAll = filter === 'ALL';
2328
2876
  const active = isAll
2329
2877
  ? methodFilters.size === 0
@@ -2342,101 +2890,101 @@ const NetworkInspector = ({ enabled = true }) => {
2342
2890
  });
2343
2891
  }
2344
2892
  }} hitSlop={10}>
2345
- {active ? (<View style={[
2893
+ {active ? (<View style={[
2346
2894
  styles.statusFilterChip,
2347
2895
  styles.statusFilterActive,
2348
2896
  { overflow: 'hidden' },
2349
2897
  ]}>
2350
- <LinearGradient colors={[
2898
+ <LinearGradient colors={[
2351
2899
  AppColors.purpleShade50,
2352
2900
  '#EAE5FF',
2353
2901
  ]} style={StyleSheet.absoluteFill} start={{ x: 0, y: 0 }} end={{ x: 1, y: 0 }}/>
2354
- <Text style={[
2902
+ <Text style={[
2355
2903
  styles.statusFilterText,
2356
2904
  { color: AppColors.purple },
2357
2905
  ]}>
2358
- {filter}
2359
- </Text>
2360
- </View>) : (<View style={styles.statusFilterChip}>
2361
- <Text style={styles.statusFilterText}>
2362
- {filter}
2363
- </Text>
2364
- </View>)}
2365
- </TouchableScale>);
2906
+ {filter}
2907
+ </Text>
2908
+ </View>) : (<View style={styles.statusFilterChip}>
2909
+ <Text style={styles.statusFilterText}>
2910
+ {filter}
2911
+ </Text>
2912
+ </View>)}
2913
+ </TouchableScale>);
2366
2914
  })}
2367
- </ScrollView>
2368
- </View>
2369
- </Animated.View>
2915
+ </ScrollView>
2916
+ </View>
2917
+ </Animated.View>
2370
2918
 
2371
- {(search ||
2919
+ {(search ||
2372
2920
  statusFilters.size > 0 ||
2373
2921
  methodFilters.size > 0) && (<Text style={styles.resultCount}>
2374
- {filteredLogs.length === logs.length
2922
+ {filteredLogs.length === logs.length
2375
2923
  ? `${logs.length} requests`
2376
2924
  : `${filteredLogs.length} of ${logs.length} filtered requests`}
2377
- </Text>)}
2378
- </View>} ListEmptyComponent={<EmptyState isSearch={search.length > 0 || statusFilters.size > 0}/>} contentContainerStyle={[
2925
+ </Text>)}
2926
+ </View>} ListEmptyComponent={<EmptyState isSearch={search.length > 0 || statusFilters.size > 0}/>} contentContainerStyle={[
2379
2927
  styles.listContent,
2380
2928
  filteredLogs.length === 0 && { flexGrow: 1 },
2381
2929
  ]} keyboardShouldPersistTaps="handled"/>) : activeTab === 'logs' ? (<View style={{ flex: 1 }}>
2382
- <View style={{
2930
+ <View style={{
2383
2931
  backgroundColor: '#FFFFFF',
2384
2932
  borderBottomWidth: 1,
2385
2933
  borderBottomColor: AppColors.dividerColor,
2386
2934
  paddingBottom: 6,
2387
2935
  }}>
2388
- <View style={[
2936
+ <View style={[
2389
2937
  styles.toolbarRow,
2390
2938
  { marginTop: 12, marginBottom: 8 },
2391
2939
  ]}>
2392
- <View style={styles.searchContainer}>
2393
- <SearchIcon color={AppColors.grayTextWeak} size={16}/>
2394
- <TextInput placeholder="Search logs..." placeholderTextColor={AppColors.grayTextWeak} value={logSearch} onChangeText={setLogSearch} style={styles.searchInput} autoCorrect={false} autoCapitalize="none"/>
2395
- {logSearch.length > 0 && (<Pressable onPress={() => setLogSearch('')} hitSlop={10} style={styles.clearBtn}>
2396
- <ClearIcon color={AppColors.grayTextWeak} size={14}/>
2397
- </Pressable>)}
2398
- </View>
2940
+ <View style={styles.searchContainer}>
2941
+ <SearchIcon color={AppColors.grayTextWeak} size={16}/>
2942
+ <TextInput placeholder="Search logs..." placeholderTextColor={AppColors.grayTextWeak} value={logSearch} onChangeText={setLogSearch} style={styles.searchInput} autoCorrect={false} autoCapitalize="none"/>
2943
+ {logSearch.length > 0 && (<Pressable onPress={() => setLogSearch('')} hitSlop={10} style={styles.clearBtn}>
2944
+ <ClearIcon color={AppColors.grayTextWeak} size={14}/>
2945
+ </Pressable>)}
2946
+ </View>
2399
2947
 
2400
- <View style={styles.toolbarRight}>
2401
- <TouchableScale style={styles.toolbarBtn} onPress={handleDelete} hitSlop={10}>
2402
- <TrashIcon color={AppColors.grayTextStrong} size={18}/>
2403
- </TouchableScale>
2404
- </View>
2405
- </View>
2948
+ <View style={styles.toolbarRight}>
2949
+ <TouchableScale style={styles.toolbarBtn} onPress={handleDelete} hitSlop={10}>
2950
+ <TrashIcon color={AppColors.grayTextStrong} size={18}/>
2951
+ </TouchableScale>
2952
+ </View>
2953
+ </View>
2406
2954
 
2407
- <ScrollView horizontal showsHorizontalScrollIndicator={false} style={{ marginVertical: 4, maxHeight: 46 }} contentContainerStyle={{
2955
+ <ScrollView horizontal showsHorizontalScrollIndicator={false} style={{ marginVertical: 4, maxHeight: 46 }} contentContainerStyle={{
2408
2956
  paddingHorizontal: 16,
2409
2957
  paddingBottom: 4,
2410
2958
  flexDirection: 'row',
2411
2959
  alignItems: 'center',
2412
2960
  gap: 8,
2413
2961
  }}>
2414
- {(() => {
2962
+ {(() => {
2415
2963
  const active = logFilters.has('all');
2416
2964
  return (<TouchableScale onPress={() => {
2417
2965
  setLogFilters(new Set(['all']));
2418
2966
  }}>
2419
- <View style={[
2967
+ <View style={[
2420
2968
  styles.statusFilterChip,
2421
2969
  active && {
2422
2970
  borderColor: AppColors.purpleShade700,
2423
2971
  backgroundColor: '#F4EBFF',
2424
2972
  },
2425
2973
  ]}>
2426
- <Text numberOfLines={1} style={[
2974
+ <Text numberOfLines={1} style={[
2427
2975
  styles.statusFilterText,
2428
2976
  active && {
2429
2977
  color: AppColors.purpleShade700,
2430
2978
  fontFamily: AppFonts.interBold,
2431
2979
  },
2432
2980
  ]}>
2433
- All ({logCounts.all})
2434
- </Text>
2435
- </View>
2436
- </TouchableScale>);
2981
+ All ({logCounts.all})
2982
+ </Text>
2983
+ </View>
2984
+ </TouchableScale>);
2437
2985
  })()}
2438
2986
 
2439
- {(() => {
2987
+ {(() => {
2440
2988
  const active = logFilters.has('user-log');
2441
2989
  return (<TouchableScale onPress={() => {
2442
2990
  setLogFilters(prev => {
@@ -2450,27 +2998,27 @@ const NetworkInspector = ({ enabled = true }) => {
2450
2998
  return next;
2451
2999
  });
2452
3000
  }}>
2453
- <View style={[
3001
+ <View style={[
2454
3002
  styles.statusFilterChip,
2455
3003
  active && {
2456
3004
  borderColor: '#64748B',
2457
3005
  backgroundColor: '#F1F5F9',
2458
3006
  },
2459
3007
  ]}>
2460
- <Text numberOfLines={1} style={[
3008
+ <Text numberOfLines={1} style={[
2461
3009
  styles.statusFilterText,
2462
3010
  active && {
2463
3011
  color: '#334155',
2464
3012
  fontFamily: AppFonts.interBold,
2465
3013
  },
2466
3014
  ]}>
2467
- User Log ({logCounts['user-log']})
2468
- </Text>
2469
- </View>
2470
- </TouchableScale>);
3015
+ User Log ({logCounts['user-log']})
3016
+ </Text>
3017
+ </View>
3018
+ </TouchableScale>);
2471
3019
  })()}
2472
3020
 
2473
- {(() => {
3021
+ {(() => {
2474
3022
  const active = logFilters.has('info');
2475
3023
  return (<TouchableScale onPress={() => {
2476
3024
  setLogFilters(prev => {
@@ -2484,27 +3032,27 @@ const NetworkInspector = ({ enabled = true }) => {
2484
3032
  return next;
2485
3033
  });
2486
3034
  }}>
2487
- <View style={[
3035
+ <View style={[
2488
3036
  styles.statusFilterChip,
2489
3037
  active && {
2490
3038
  borderColor: AppColors.purple,
2491
3039
  backgroundColor: AppColors.purpleShade50,
2492
3040
  },
2493
3041
  ]}>
2494
- <Text numberOfLines={1} style={[
3042
+ <Text numberOfLines={1} style={[
2495
3043
  styles.statusFilterText,
2496
3044
  active && {
2497
3045
  color: AppColors.purple,
2498
3046
  fontFamily: AppFonts.interBold,
2499
3047
  },
2500
3048
  ]}>
2501
- Info ({logCounts.info})
2502
- </Text>
2503
- </View>
2504
- </TouchableScale>);
3049
+ Info ({logCounts.info})
3050
+ </Text>
3051
+ </View>
3052
+ </TouchableScale>);
2505
3053
  })()}
2506
3054
 
2507
- {(() => {
3055
+ {(() => {
2508
3056
  const active = logFilters.has('warn');
2509
3057
  return (<TouchableScale onPress={() => {
2510
3058
  setLogFilters(prev => {
@@ -2518,14 +3066,14 @@ const NetworkInspector = ({ enabled = true }) => {
2518
3066
  return next;
2519
3067
  });
2520
3068
  }}>
2521
- <View style={[
3069
+ <View style={[
2522
3070
  styles.statusFilterChip,
2523
3071
  active && {
2524
3072
  borderColor: AppColors.lightOrange,
2525
3073
  backgroundColor: '#FFFDF6',
2526
3074
  },
2527
3075
  ]}>
2528
- <Text numberOfLines={1} style={[
3076
+ <Text numberOfLines={1} style={[
2529
3077
  styles.statusFilterText,
2530
3078
  active && {
2531
3079
  color: AppColors.darkOrange ||
@@ -2533,13 +3081,13 @@ const NetworkInspector = ({ enabled = true }) => {
2533
3081
  fontFamily: AppFonts.interBold,
2534
3082
  },
2535
3083
  ]}>
2536
- Warning ({logCounts.warn})
2537
- </Text>
2538
- </View>
2539
- </TouchableScale>);
3084
+ Warning ({logCounts.warn})
3085
+ </Text>
3086
+ </View>
3087
+ </TouchableScale>);
2540
3088
  })()}
2541
3089
 
2542
- {(() => {
3090
+ {(() => {
2543
3091
  const active = logFilters.has('error');
2544
3092
  return (<TouchableScale onPress={() => {
2545
3093
  setLogFilters(prev => {
@@ -2553,27 +3101,27 @@ const NetworkInspector = ({ enabled = true }) => {
2553
3101
  return next;
2554
3102
  });
2555
3103
  }}>
2556
- <View style={[
3104
+ <View style={[
2557
3105
  styles.statusFilterChip,
2558
3106
  active && {
2559
3107
  borderColor: AppColors.errorColor,
2560
3108
  backgroundColor: '#FFF5F6',
2561
3109
  },
2562
3110
  ]}>
2563
- <Text numberOfLines={1} style={[
3111
+ <Text numberOfLines={1} style={[
2564
3112
  styles.statusFilterText,
2565
3113
  active && {
2566
3114
  color: AppColors.errorColor,
2567
3115
  fontFamily: AppFonts.interBold,
2568
3116
  },
2569
3117
  ]}>
2570
- Error ({logCounts.error})
2571
- </Text>
2572
- </View>
2573
- </TouchableScale>);
3118
+ Error ({logCounts.error})
3119
+ </Text>
3120
+ </View>
3121
+ </TouchableScale>);
2574
3122
  })()}
2575
3123
 
2576
- {(() => {
3124
+ {(() => {
2577
3125
  const active = logFilters.has('analytics');
2578
3126
  return (<TouchableScale onPress={() => {
2579
3127
  setLogFilters(prev => {
@@ -2587,29 +3135,29 @@ const NetworkInspector = ({ enabled = true }) => {
2587
3135
  return next;
2588
3136
  });
2589
3137
  }}>
2590
- <View style={[
3138
+ <View style={[
2591
3139
  styles.statusFilterChip,
2592
3140
  active && {
2593
3141
  borderColor: AppColors.skyBlue,
2594
3142
  backgroundColor: `${AppColors.skyBlue}15`,
2595
3143
  },
2596
3144
  ]}>
2597
- <Text numberOfLines={1} style={[
3145
+ <Text numberOfLines={1} style={[
2598
3146
  styles.statusFilterText,
2599
3147
  active && {
2600
3148
  color: AppColors.skyBlue,
2601
3149
  fontFamily: AppFonts.interBold,
2602
3150
  },
2603
3151
  ]}>
2604
- Analytics ({logCounts.analytics})
2605
- </Text>
2606
- </View>
2607
- </TouchableScale>);
3152
+ Analytics ({logCounts.analytics})
3153
+ </Text>
3154
+ </View>
3155
+ </TouchableScale>);
2608
3156
  })()}
2609
- </ScrollView>
2610
- </View>
3157
+ </ScrollView>
3158
+ </View>
2611
3159
 
2612
- <FlatList data={filteredConsoleLogs} keyExtractor={item => item.id.toString()} ListHeaderComponent={(() => {
3160
+ <FlatList data={filteredConsoleLogs} keyExtractor={item => item.id.toString()} ListHeaderComponent={(() => {
2613
3161
  const total = visibleConsoleLogs.length;
2614
3162
  const filtered = filteredConsoleLogs.length;
2615
3163
  const isAllSelected = logFilters.has('all') ||
@@ -2619,8 +3167,8 @@ const NetworkInspector = ({ enabled = true }) => {
2619
3167
  styles.resultCount,
2620
3168
  { marginBottom: 4, marginTop: 12 },
2621
3169
  ]}>
2622
- Showing ({filtered}/{total}) logs showing
2623
- </Text>);
3170
+ Showing ({filtered}/{total}) logs showing
3171
+ </Text>);
2624
3172
  }
2625
3173
  else {
2626
3174
  const activeFilterNames = Array.from(logFilters)
@@ -2630,31 +3178,37 @@ const NetworkInspector = ({ enabled = true }) => {
2630
3178
  return 'User Log';
2631
3179
  if (f === 'analytics')
2632
3180
  return 'Analytics';
2633
- return f.charAt(0).toUpperCase() + f.slice(1);
3181
+ return (f.charAt(0).toUpperCase() +
3182
+ f.slice(1));
2634
3183
  });
2635
3184
  return (<Text style={[
2636
3185
  styles.resultCount,
2637
3186
  { marginBottom: 4, marginTop: 12 },
2638
3187
  ]}>
2639
- Filtering with {activeFilterNames.join(', ')} (
2640
- {filtered}/{total}) logs is showing
2641
- </Text>);
3188
+ Filtering with {activeFilterNames.join(', ')} (
3189
+ {filtered}/{total}) logs is showing
3190
+ </Text>);
2642
3191
  }
2643
- })()} renderItem={({ item }) => (<ConsoleLogCard item={item} searchStr={logSearch}/>)} initialNumToRender={15} maxToRenderPerBatch={15} windowSize={7} removeClippedSubviews={true} ListEmptyComponent={<EmptyState isSearch={logSearch.length > 0 || logFilters.size > 0}/>} contentContainerStyle={[
3192
+ })()} renderItem={({ item, index }) => (<AnimatedEntrance index={index} distance={8}>
3193
+ <ConsoleLogCard item={item} searchStr={logSearch}/>
3194
+ </AnimatedEntrance>)} initialNumToRender={15} maxToRenderPerBatch={15} windowSize={7} removeClippedSubviews={true} ListEmptyComponent={<EmptyState isSearch={logSearch.length > 0 || logFilters.size > 0}/>} contentContainerStyle={[
2644
3195
  styles.listContent,
2645
3196
  filteredConsoleLogs.length === 0 && { flexGrow: 1 },
2646
3197
  ]} keyboardShouldPersistTaps="handled"/>
2647
- </View>) : activeTab === 'webview' ? (webViewNavHistory.length === 0 ? (<View style={styles.emptyContainer}>
2648
- <View style={styles.emptyIconWrap}>
2649
- <GlobeIcon color={AppColors.purple} size={32}/>
2650
- </View>
2651
- <Text style={styles.emptyTitle}>No WebView Activity</Text>
2652
- <Text style={styles.emptySub}>
2653
- Load a webpage within a connected WebView component to inspect pages, page source, and console logs.
2654
- </Text>
2655
- </View>) : (<View style={{ flex: 1 }}>
2656
- {/* ─── Current Page Address Bar (Now on top) ─── */}
2657
- {(() => {
3198
+ </View>) : activeTab === 'webview' ? (webViewNavHistory.length === 0 ? (<View style={styles.emptyContainer}>
3199
+ <View style={styles.emptyIconWrap}>
3200
+ <GlobeIcon color={AppColors.purple} size={32}/>
3201
+ </View>
3202
+ <Text style={styles.emptyTitle}>
3203
+ No WebView Activity
3204
+ </Text>
3205
+ <Text style={styles.emptySub}>
3206
+ Load a webpage within a connected WebView component to
3207
+ inspect pages, page source, and console logs.
3208
+ </Text>
3209
+ </View>) : (<View style={{ flex: 1 }}>
3210
+ {/* ─── Current Page Address Bar (Now on top) ─── */}
3211
+ {(() => {
2658
3212
  const currentUrl = webViewNavHistory[0]?.url;
2659
3213
  if (!currentUrl)
2660
3214
  return null;
@@ -2666,7 +3220,7 @@ const NetworkInspector = ({ enabled = true }) => {
2666
3220
  borderBottomWidth: 1,
2667
3221
  borderBottomColor: AppColors.dividerColor,
2668
3222
  }}>
2669
- <View style={{
3223
+ <View style={{
2670
3224
  flexDirection: 'row',
2671
3225
  alignItems: 'center',
2672
3226
  backgroundColor: AppColors.grayBackground,
@@ -2677,27 +3231,42 @@ const NetworkInspector = ({ enabled = true }) => {
2677
3231
  paddingVertical: 5,
2678
3232
  gap: 8,
2679
3233
  }}>
2680
- {/* Left: Lock and HTTPS label */}
2681
- <View style={{ flexDirection: 'row', alignItems: 'center', gap: 4 }}>
2682
- <Text style={{ fontSize: 11 }}>🔒</Text>
2683
- <Text style={{ fontFamily: AppFonts.interBold, fontSize: 9.5, color: AppColors.greenColor, letterSpacing: 0.5 }}>HTTPS</Text>
2684
- </View>
2685
- <View style={{ width: 1.5, height: 12, backgroundColor: AppColors.grayBorderSecondary }}/>
3234
+ {/* Left: Lock and HTTPS label */}
3235
+ <View style={{
3236
+ flexDirection: 'row',
3237
+ alignItems: 'center',
3238
+ gap: 4,
3239
+ }}>
3240
+ <Text style={{ fontSize: 11 }}>🔒</Text>
3241
+ <Text style={{
3242
+ fontFamily: AppFonts.interBold,
3243
+ fontSize: 9.5,
3244
+ color: AppColors.greenColor,
3245
+ letterSpacing: 0.5,
3246
+ }}>
3247
+ HTTPS
3248
+ </Text>
3249
+ </View>
3250
+ <View style={{
3251
+ width: 1.5,
3252
+ height: 12,
3253
+ backgroundColor: AppColors.grayBorderSecondary,
3254
+ }}/>
2686
3255
 
2687
- {/* Middle: URL text (Address style) */}
2688
- <View style={{ flex: 1 }}>
2689
- <HighlightText text={currentUrl} search={webViewSearch} numberOfLines={1} ellipsizeMode="tail" style={{
3256
+ {/* Middle: URL text (Address style) */}
3257
+ <View style={{ flex: 1 }}>
3258
+ <HighlightText text={currentUrl} search={webViewSearch} numberOfLines={1} ellipsizeMode="tail" style={{
2690
3259
  fontFamily: AppFonts.interMedium,
2691
3260
  fontSize: 11.5,
2692
3261
  color: AppColors.primaryBlack,
2693
3262
  }} highlightStyle={styles.highlight} detectLinks={false}/>
2694
- </View>
3263
+ </View>
2695
3264
 
2696
- {/* Right: Copy Button */}
2697
- <CopyButton value={currentUrl} label="URL"/>
3265
+ {/* Right: Copy Button */}
3266
+ <CopyButton value={currentUrl} label="URL"/>
2698
3267
 
2699
- {/* Right: Globe Icon button to open browser */}
2700
- <TouchableScale onPress={() => Linking.openURL(currentUrl)} hitSlop={8} style={{
3268
+ {/* Right: Globe Icon button to open browser */}
3269
+ <TouchableScale onPress={() => Linking.openURL(currentUrl)} hitSlop={8} style={{
2701
3270
  width: 26,
2702
3271
  height: 26,
2703
3272
  borderRadius: 13,
@@ -2707,26 +3276,26 @@ const NetworkInspector = ({ enabled = true }) => {
2707
3276
  alignItems: 'center',
2708
3277
  justifyContent: 'center',
2709
3278
  }}>
2710
- <GlobeIcon size={11} color={AppColors.purple}/>
2711
- </TouchableScale>
2712
- </View>
2713
- </View>);
3279
+ <GlobeIcon size={11} color={AppColors.purple}/>
3280
+ </TouchableScale>
3281
+ </View>
3282
+ </View>);
2714
3283
  })()}
2715
3284
 
2716
- {/* ─── WebView Sub-Tabs (Now below Address Bar) ─── */}
2717
- <View style={{
3285
+ {/* ─── WebView Sub-Tabs (Now below Address Bar) ─── */}
3286
+ <View style={{
2718
3287
  backgroundColor: AppColors.primaryLight,
2719
3288
  borderBottomWidth: 1,
2720
3289
  borderBottomColor: AppColors.dividerColor,
2721
3290
  paddingVertical: 6,
2722
3291
  }}>
2723
- <ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={{
3292
+ <ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={{
2724
3293
  paddingHorizontal: 12,
2725
3294
  flexDirection: 'row',
2726
3295
  gap: 8,
2727
3296
  }}>
2728
- {/* Sub-tab 1: Preview */}
2729
- <Pressable style={[
3297
+ {/* Sub-tab 1: Preview */}
3298
+ <Pressable style={[
2730
3299
  {
2731
3300
  paddingVertical: 6,
2732
3301
  paddingHorizontal: 14,
@@ -2742,19 +3311,28 @@ const NetworkInspector = ({ enabled = true }) => {
2742
3311
  backgroundColor: AppColors.purple,
2743
3312
  borderColor: AppColors.purple,
2744
3313
  },
2745
- ]} onPress={() => setWebViewSubTab('preview')}>
2746
- <EyeIcon color={webViewSubTab === 'preview' ? '#FFFFFF' : AppColors.grayTextWeak} size={13}/>
2747
- <Text style={{
2748
- fontFamily: webViewSubTab === 'preview' ? AppFonts.interBold : AppFonts.interMedium,
3314
+ ]} onPress={() => {
3315
+ animateNextLayout();
3316
+ setWebViewSubTab('preview');
3317
+ }}>
3318
+ <EyeIcon color={webViewSubTab === 'preview'
3319
+ ? '#FFFFFF'
3320
+ : AppColors.grayTextWeak} size={13}/>
3321
+ <Text style={{
3322
+ fontFamily: webViewSubTab === 'preview'
3323
+ ? AppFonts.interBold
3324
+ : AppFonts.interMedium,
2749
3325
  fontSize: 12,
2750
- color: webViewSubTab === 'preview' ? '#FFFFFF' : AppColors.grayTextStrong,
3326
+ color: webViewSubTab === 'preview'
3327
+ ? '#FFFFFF'
3328
+ : AppColors.grayTextStrong,
2751
3329
  }}>
2752
- Preview
2753
- </Text>
2754
- </Pressable>
3330
+ Preview
3331
+ </Text>
3332
+ </Pressable>
2755
3333
 
2756
- {/* Sub-tab 2: Page Source */}
2757
- <Pressable style={[
3334
+ {/* Sub-tab 2: Page Source */}
3335
+ <Pressable style={[
2758
3336
  {
2759
3337
  paddingVertical: 6,
2760
3338
  paddingHorizontal: 14,
@@ -2770,19 +3348,28 @@ const NetworkInspector = ({ enabled = true }) => {
2770
3348
  backgroundColor: AppColors.purple,
2771
3349
  borderColor: AppColors.purple,
2772
3350
  },
2773
- ]} onPress={() => setWebViewSubTab('html')}>
2774
- <HtmlIcon color={webViewSubTab === 'html' ? '#FFFFFF' : AppColors.grayTextWeak} size={13}/>
2775
- <Text style={{
2776
- fontFamily: webViewSubTab === 'html' ? AppFonts.interBold : AppFonts.interMedium,
3351
+ ]} onPress={() => {
3352
+ animateNextLayout();
3353
+ setWebViewSubTab('html');
3354
+ }}>
3355
+ <HtmlIcon color={webViewSubTab === 'html'
3356
+ ? '#FFFFFF'
3357
+ : AppColors.grayTextWeak} size={13}/>
3358
+ <Text style={{
3359
+ fontFamily: webViewSubTab === 'html'
3360
+ ? AppFonts.interBold
3361
+ : AppFonts.interMedium,
2777
3362
  fontSize: 12,
2778
- color: webViewSubTab === 'html' ? '#FFFFFF' : AppColors.grayTextStrong,
3363
+ color: webViewSubTab === 'html'
3364
+ ? '#FFFFFF'
3365
+ : AppColors.grayTextStrong,
2779
3366
  }}>
2780
- Page Source
2781
- </Text>
2782
- </Pressable>
3367
+ Page Source
3368
+ </Text>
3369
+ </Pressable>
2783
3370
 
2784
- {/* Sub-tab 3: History */}
2785
- <Pressable style={[
3371
+ {/* Sub-tab 3: History */}
3372
+ <Pressable style={[
2786
3373
  {
2787
3374
  paddingVertical: 6,
2788
3375
  paddingHorizontal: 14,
@@ -2798,19 +3385,28 @@ const NetworkInspector = ({ enabled = true }) => {
2798
3385
  backgroundColor: AppColors.purple,
2799
3386
  borderColor: AppColors.purple,
2800
3387
  },
2801
- ]} onPress={() => setWebViewSubTab('navigation')}>
2802
- <ClockIcon color={webViewSubTab === 'navigation' ? '#FFFFFF' : AppColors.grayTextWeak} size={13}/>
2803
- <Text style={{
2804
- fontFamily: webViewSubTab === 'navigation' ? AppFonts.interBold : AppFonts.interMedium,
3388
+ ]} onPress={() => {
3389
+ animateNextLayout();
3390
+ setWebViewSubTab('navigation');
3391
+ }}>
3392
+ <ClockIcon color={webViewSubTab === 'navigation'
3393
+ ? '#FFFFFF'
3394
+ : AppColors.grayTextWeak} size={13}/>
3395
+ <Text style={{
3396
+ fontFamily: webViewSubTab === 'navigation'
3397
+ ? AppFonts.interBold
3398
+ : AppFonts.interMedium,
2805
3399
  fontSize: 12,
2806
- color: webViewSubTab === 'navigation' ? '#FFFFFF' : AppColors.grayTextStrong,
3400
+ color: webViewSubTab === 'navigation'
3401
+ ? '#FFFFFF'
3402
+ : AppColors.grayTextStrong,
2807
3403
  }}>
2808
- History ({webViewNavHistory.length})
2809
- </Text>
2810
- </Pressable>
3404
+ History ({webViewNavHistory.length})
3405
+ </Text>
3406
+ </Pressable>
2811
3407
 
2812
- {/* Sub-tab 4: Console */}
2813
- <Pressable style={[
3408
+ {/* Sub-tab 4: Console */}
3409
+ <Pressable style={[
2814
3410
  {
2815
3411
  paddingVertical: 6,
2816
3412
  paddingHorizontal: 14,
@@ -2826,23 +3422,32 @@ const NetworkInspector = ({ enabled = true }) => {
2826
3422
  backgroundColor: AppColors.purple,
2827
3423
  borderColor: AppColors.purple,
2828
3424
  },
2829
- ]} onPress={() => setWebViewSubTab('console')}>
2830
- <TerminalIcon color={webViewSubTab === 'console' ? '#FFFFFF' : AppColors.grayTextWeak} size={13}/>
2831
- <Text style={{
2832
- fontFamily: webViewSubTab === 'console' ? AppFonts.interBold : AppFonts.interMedium,
3425
+ ]} onPress={() => {
3426
+ animateNextLayout();
3427
+ setWebViewSubTab('console');
3428
+ }}>
3429
+ <TerminalIcon color={webViewSubTab === 'console'
3430
+ ? '#FFFFFF'
3431
+ : AppColors.grayTextWeak} size={13}/>
3432
+ <Text style={{
3433
+ fontFamily: webViewSubTab === 'console'
3434
+ ? AppFonts.interBold
3435
+ : AppFonts.interMedium,
2833
3436
  fontSize: 12,
2834
- color: webViewSubTab === 'console' ? '#FFFFFF' : AppColors.grayTextStrong,
3437
+ color: webViewSubTab === 'console'
3438
+ ? '#FFFFFF'
3439
+ : AppColors.grayTextStrong,
2835
3440
  }}>
2836
- Console ({webViewLogs.length})
2837
- </Text>
2838
- </Pressable>
2839
- </ScrollView>
2840
- </View>
3441
+ Console ({webViewLogs.length})
3442
+ </Text>
3443
+ </Pressable>
3444
+ </ScrollView>
3445
+ </View>
2841
3446
 
2842
- {webViewSubTab === 'html' ? (<View style={{ flex: 1 }}>
2843
- {webViewHtml || webViewCss || webViewJs ? (<View style={{ flex: 1 }}>
2844
- {/* Clear Inspect Banner */}
2845
- {inspectedElement && (<View style={{
3447
+ {webViewSubTab === 'html' ? (<View style={{ flex: 1 }}>
3448
+ {webViewHtml || webViewCss || webViewJs ? (<View style={{ flex: 1 }}>
3449
+ {/* Clear Inspect Banner */}
3450
+ {inspectedElement && (<View style={{
2846
3451
  flexDirection: 'row',
2847
3452
  alignItems: 'center',
2848
3453
  justifyContent: 'space-between',
@@ -2852,36 +3457,44 @@ const NetworkInspector = ({ enabled = true }) => {
2852
3457
  borderBottomWidth: 1,
2853
3458
  borderBottomColor: AppColors.dividerColor,
2854
3459
  }}>
2855
- <Text style={{
3460
+ <Text style={{
2856
3461
  fontFamily: AppFonts.interMedium,
2857
3462
  fontSize: 11.5,
2858
3463
  color: AppColors.purple,
2859
3464
  flex: 1,
2860
3465
  }}>
2861
- Inspecting element:{' '}
2862
- <Text style={{ fontFamily: AppFonts.interBold }}>
2863
- &lt;{inspectedElement.tagName}
2864
- {inspectedElement.id ? ` id="${inspectedElement.id}"` : ''}
2865
- {inspectedElement.className ? ` class="${inspectedElement.className.trim().split(/\s+/)[0]}"` : ''}
2866
- &gt;
2867
- </Text>
2868
- </Text>
2869
- <Pressable onPress={() => setInspectedElement(null)} style={{
3466
+ Inspecting element:{' '}
3467
+ <Text style={{
3468
+ fontFamily: AppFonts.interBold,
3469
+ }}>
3470
+ &lt;{inspectedElement.tagName}
3471
+ {inspectedElement.id
3472
+ ? ` id="${inspectedElement.id}"`
3473
+ : ''}
3474
+ {inspectedElement.className
3475
+ ? ` class="${inspectedElement.className
3476
+ .trim()
3477
+ .split(/\s+/)[0]}"`
3478
+ : ''}
3479
+ &gt;
3480
+ </Text>
3481
+ </Text>
3482
+ <Pressable onPress={() => setInspectedElement(null)} style={{
2870
3483
  paddingHorizontal: 8,
2871
3484
  paddingVertical: 4,
2872
3485
  }}>
2873
- <Text style={{
3486
+ <Text style={{
2874
3487
  fontFamily: AppFonts.interBold,
2875
3488
  fontSize: 11,
2876
3489
  color: AppColors.purple,
2877
3490
  }}>
2878
- Clear Inspect
2879
- </Text>
2880
- </Pressable>
2881
- </View>)}
3491
+ Clear Inspect
3492
+ </Text>
3493
+ </Pressable>
3494
+ </View>)}
2882
3495
 
2883
- {/* Inner sub-tabs inside HTML source view */}
2884
- <View style={{
3496
+ {/* Inner sub-tabs inside HTML source view */}
3497
+ <View style={{
2885
3498
  flexDirection: 'row',
2886
3499
  borderBottomWidth: 1,
2887
3500
  borderBottomColor: AppColors.dividerColor,
@@ -2889,7 +3502,7 @@ const NetworkInspector = ({ enabled = true }) => {
2889
3502
  paddingHorizontal: 12,
2890
3503
  gap: 12,
2891
3504
  }}>
2892
- {['html', 'css', 'javascript'].map(tab => {
3505
+ {['html', 'css', 'javascript'].map(tab => {
2893
3506
  const active = htmlSubTab === tab;
2894
3507
  const label = tab === 'html'
2895
3508
  ? 'HTML'
@@ -2901,7 +3514,10 @@ const NetworkInspector = ({ enabled = true }) => {
2901
3514
  : tab === 'css'
2902
3515
  ? '#2563EB' // Blue
2903
3516
  : '#D97706'; // Dark Yellow/Amber
2904
- return (<Pressable key={tab} onPress={() => setHtmlSubTab(tab)} style={{
3517
+ return (<Pressable key={tab} onPress={() => {
3518
+ animateNextLayout();
3519
+ setHtmlSubTab(tab);
3520
+ }} style={{
2905
3521
  paddingVertical: 8,
2906
3522
  paddingHorizontal: 4,
2907
3523
  borderBottomWidth: 2,
@@ -2912,10 +3528,16 @@ const NetworkInspector = ({ enabled = true }) => {
2912
3528
  alignItems: 'center',
2913
3529
  gap: 4,
2914
3530
  }}>
2915
- {tab === 'html' && (<HtmlIcon color={active ? activeColor : AppColors.grayTextWeak} size={14}/>)}
2916
- {tab === 'css' && (<CssIcon color={active ? activeColor : AppColors.grayTextWeak} size={14}/>)}
2917
- {tab === 'javascript' && (<JsIcon color={active ? activeColor : AppColors.grayTextWeak} size={14}/>)}
2918
- <Text style={{
3531
+ {tab === 'html' && (<HtmlIcon color={active
3532
+ ? activeColor
3533
+ : AppColors.grayTextWeak} size={14}/>)}
3534
+ {tab === 'css' && (<CssIcon color={active
3535
+ ? activeColor
3536
+ : AppColors.grayTextWeak} size={14}/>)}
3537
+ {tab === 'javascript' && (<JsIcon color={active
3538
+ ? activeColor
3539
+ : AppColors.grayTextWeak} size={14}/>)}
3540
+ <Text style={{
2919
3541
  fontFamily: active
2920
3542
  ? AppFonts.interBold
2921
3543
  : AppFonts.interMedium,
@@ -2924,70 +3546,81 @@ const NetworkInspector = ({ enabled = true }) => {
2924
3546
  ? activeColor
2925
3547
  : AppColors.grayTextWeak,
2926
3548
  }}>
2927
- {label}
2928
- </Text>
2929
- </Pressable>);
3549
+ {label}
3550
+ </Text>
3551
+ </Pressable>);
2930
3552
  })}
2931
- </View>
2932
- <View style={{ flex: 1, padding: 12 }}>
2933
- {!isHtmlTabReady ? (<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center', minHeight: 200 }}>
2934
- <ActivityIndicator size="large" color={AppColors.purple}/>
2935
- </View>) : htmlSubTab === 'html' ? (webViewHtml ? (<CodeSnippet code={webViewHtml} language="html" search={getSearchTermForTab()}/>) : (<Text style={{
3553
+ </View>
3554
+ <View style={{ flex: 1, padding: 12 }}>
3555
+ {!isHtmlTabReady ? (<View style={{
3556
+ flex: 1,
3557
+ justifyContent: 'center',
3558
+ alignItems: 'center',
3559
+ minHeight: 200,
3560
+ }}>
3561
+ <ActivityIndicator size="large" color={AppColors.purple}/>
3562
+ </View>) : htmlSubTab === 'html' ? (webViewHtml ? (<CodeSnippet code={webViewHtml} language="html" search={getSearchTermForTab()}/>) : (<Text style={{
2936
3563
  fontFamily: 'monospace',
2937
3564
  fontSize: 11,
2938
3565
  color: '#94A3B8',
2939
3566
  padding: 12,
2940
3567
  }}>
2941
- No HTML content captured.
2942
- </Text>)) : htmlSubTab === 'css' ? (webViewCss ? (<CodeSnippet code={webViewCss} language="css" search={getSearchTermForTab()}/>) : (<Text style={{
3568
+ No HTML content captured.
3569
+ </Text>)) : htmlSubTab === 'css' ? (webViewCss ? (<CodeSnippet code={webViewCss} language="css" search={getSearchTermForTab()}/>) : (<Text style={{
2943
3570
  fontFamily: 'monospace',
2944
3571
  fontSize: 11,
2945
3572
  color: '#94A3B8',
2946
3573
  padding: 12,
2947
3574
  }}>
2948
- No CSS styles detected on this page.
2949
- </Text>)) : webViewJs ? (<CodeSnippet code={webViewJs} language="javascript" search={getSearchTermForTab()}/>) : (<Text style={{
3575
+ No CSS styles detected on this page.
3576
+ </Text>)) : webViewJs ? (<CodeSnippet code={webViewJs} language="javascript" search={getSearchTermForTab()}/>) : (<Text style={{
2950
3577
  fontFamily: 'monospace',
2951
3578
  fontSize: 11,
2952
3579
  color: '#94A3B8',
2953
3580
  padding: 12,
2954
3581
  }}>
2955
- No scripts detected on this page.
2956
- </Text>)}
2957
- </View>
2958
- </View>) : (<View style={styles.emptyContainer}>
2959
- <View style={styles.emptyIconWrap}>
2960
- <GlobeIcon color={AppColors.purple} size={32}/>
2961
- </View>
2962
- <Text style={styles.emptyTitle}>
2963
- No Page Source Captured
2964
- </Text>
2965
- <Text style={styles.emptySub}>
2966
- Load a page in the WebView to inspect its HTML, CSS,
2967
- or Javascript source.
2968
- </Text>
2969
- </View>)}
2970
- </View>) : webViewSubTab === 'navigation' ? (<FlatList data={filteredNavHistory} keyExtractor={(item, index) => `${index}-${item.timestamp}`} style={{ flex: 1, backgroundColor: AppColors.grayBackground }} ListHeaderComponent={<View style={{
3582
+ No scripts detected on this page.
3583
+ </Text>)}
3584
+ </View>
3585
+ </View>) : (<View style={styles.emptyContainer}>
3586
+ <View style={styles.emptyIconWrap}>
3587
+ <GlobeIcon color={AppColors.purple} size={32}/>
3588
+ </View>
3589
+ <Text style={styles.emptyTitle}>
3590
+ No Page Source Captured
3591
+ </Text>
3592
+ <Text style={styles.emptySub}>
3593
+ Load a page in the WebView to inspect its
3594
+ HTML, CSS, or Javascript source.
3595
+ </Text>
3596
+ </View>)}
3597
+ </View>) : webViewSubTab === 'navigation' ? (<FlatList data={filteredNavHistory} keyExtractor={(item, index) => `${index}-${item.timestamp}`} style={{
3598
+ flex: 1,
3599
+ backgroundColor: AppColors.grayBackground,
3600
+ }} ListHeaderComponent={<View style={{
2971
3601
  paddingHorizontal: 16,
2972
3602
  paddingTop: 12,
2973
3603
  paddingBottom: 8,
2974
3604
  }}>
2975
- <Text style={styles.resultCount}>
2976
- Navigation History ({webViewNavHistory.length})
2977
- </Text>
2978
- </View>} renderItem={({ item, index, }) => {
3605
+ <Text style={styles.resultCount}>
3606
+ Navigation History ({webViewNavHistory.length}
3607
+ )
3608
+ </Text>
3609
+ </View>} renderItem={({ item, index, }) => {
2979
3610
  const isLatest = index === 0;
2980
3611
  const formatNavTime = (ts) => {
2981
3612
  const d = new Date(ts);
2982
3613
  return `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}:${String(d.getSeconds()).padStart(2, '0')}`;
2983
3614
  };
2984
- return (<View style={{
3615
+ return (<AnimatedEntrance index={index} distance={8} style={{
2985
3616
  marginHorizontal: 16,
2986
3617
  marginVertical: 6,
2987
3618
  borderRadius: 12,
2988
3619
  borderWidth: 1,
2989
3620
  borderColor: AppColors.grayBorderSecondary,
2990
- backgroundColor: isLatest ? AppColors.purpleShade50 : AppColors.primaryLight,
3621
+ backgroundColor: isLatest
3622
+ ? AppColors.purpleShade50
3623
+ : AppColors.primaryLight,
2991
3624
  padding: 14,
2992
3625
  flexDirection: 'row',
2993
3626
  alignItems: 'center',
@@ -2999,23 +3632,23 @@ const NetworkInspector = ({ enabled = true }) => {
2999
3632
  shadowRadius: 4,
3000
3633
  elevation: 2,
3001
3634
  }}>
3002
- <View style={{ flex: 1, gap: 8 }}>
3003
- {/* Top row: Title and Badge */}
3004
- <View style={{
3635
+ <View style={{ flex: 1, gap: 8 }}>
3636
+ {/* Top row: Title and Badge */}
3637
+ <View style={{
3005
3638
  flexDirection: 'row',
3006
3639
  alignItems: 'center',
3007
3640
  gap: 8,
3008
3641
  flexWrap: 'wrap',
3009
3642
  }}>
3010
- <Text numberOfLines={1} ellipsizeMode="tail" style={{
3643
+ <Text numberOfLines={1} ellipsizeMode="tail" style={{
3011
3644
  fontFamily: AppFonts.interBold,
3012
3645
  fontSize: 14,
3013
3646
  color: AppColors.primaryBlack,
3014
3647
  flexShrink: 1,
3015
3648
  }}>
3016
- {item.title || 'Untitled Page'}
3017
- </Text>
3018
- {isLatest && (<View style={{
3649
+ {item.title || 'Untitled Page'}
3650
+ </Text>
3651
+ {isLatest && (<View style={{
3019
3652
  flexDirection: 'row',
3020
3653
  alignItems: 'center',
3021
3654
  gap: 4,
@@ -3024,65 +3657,100 @@ const NetworkInspector = ({ enabled = true }) => {
3024
3657
  paddingVertical: 3,
3025
3658
  borderRadius: 12,
3026
3659
  }}>
3027
- <View style={{
3660
+ <View style={{
3028
3661
  width: 6,
3029
3662
  height: 6,
3030
3663
  borderRadius: 3,
3031
3664
  backgroundColor: AppColors.greenBaggageText,
3032
3665
  }}/>
3033
- <Text style={{
3666
+ <Text style={{
3034
3667
  fontFamily: AppFonts.interBold,
3035
3668
  fontSize: 9.5,
3036
3669
  color: AppColors.greenBaggageText,
3037
3670
  }}>
3038
- Active
3039
- </Text>
3040
- </View>)}
3041
- </View>
3671
+ Active
3672
+ </Text>
3673
+ </View>)}
3674
+ </View>
3042
3675
 
3043
- {/* Middle row: URL with Globe Icon */}
3044
- <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
3045
- <GlobeIcon size={12} color={AppColors.grayTextWeak}/>
3046
- <HighlightText text={item.url} search={webViewSearch} numberOfLines={2} ellipsizeMode="tail" style={{
3676
+ {/* Middle row: URL with Globe Icon */}
3677
+ <View style={{
3678
+ flexDirection: 'row',
3679
+ alignItems: 'center',
3680
+ gap: 6,
3681
+ }}>
3682
+ <GlobeIcon size={12} color={AppColors.grayTextWeak}/>
3683
+ <HighlightText text={item.url} search={webViewSearch} numberOfLines={2} ellipsizeMode="tail" style={{
3047
3684
  fontFamily: AppFonts.interRegular,
3048
3685
  fontSize: 12,
3049
3686
  color: AppColors.grayText,
3050
3687
  flex: 1,
3051
3688
  }} highlightStyle={styles.highlight} detectLinks={true}/>
3052
- </View>
3689
+ </View>
3053
3690
 
3054
- {/* Bottom row: Time */}
3055
- <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
3056
- <ClockIcon size={11} color={AppColors.grayTextWeak}/>
3057
- <Text style={{ fontFamily: AppFonts.interRegular, fontSize: 11, color: AppColors.grayTextWeak }}>
3058
- {formatNavTime(item.timestamp)}
3059
- </Text>
3060
- </View>
3061
- </View>
3062
- <CopyButton value={item.url} label="URL"/>
3063
- </View>);
3691
+ {/* Bottom row: Time */}
3692
+ <View style={{
3693
+ flexDirection: 'row',
3694
+ alignItems: 'center',
3695
+ gap: 6,
3696
+ }}>
3697
+ <ClockIcon size={11} color={AppColors.grayTextWeak}/>
3698
+ <Text style={{
3699
+ fontFamily: AppFonts.interRegular,
3700
+ fontSize: 11,
3701
+ color: AppColors.grayTextWeak,
3702
+ }}>
3703
+ {formatNavTime(item.timestamp)}
3704
+ </Text>
3705
+ </View>
3706
+ </View>
3707
+ <CopyButton value={item.url} label="URL"/>
3708
+ </AnimatedEntrance>);
3064
3709
  }} initialNumToRender={15} maxToRenderPerBatch={15} windowSize={7} removeClippedSubviews={true} ListEmptyComponent={<EmptyState isSearch={webViewSearch.length > 0}/>} contentContainerStyle={[
3065
3710
  styles.listContent,
3066
- filteredNavHistory.length === 0 && { flexGrow: 1 },
3067
- ]} keyboardShouldPersistTaps="handled"/>) : webViewSubTab === 'console' ? (<View style={{ flex: 1, backgroundColor: AppColors.grayBackground }}>
3068
- {webViewLogs.length > 0 ? (<FlatList data={webViewLogs} keyExtractor={(item) => String(item.id)} style={{ flex: 1 }} ListHeaderComponent={<View style={{ paddingHorizontal: 16, paddingTop: 12, paddingBottom: 8, flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}>
3069
- <Text style={styles.resultCount}>
3070
- Console Logs ({webViewLogs.length})
3071
- </Text>
3072
- <TouchableScale onPress={() => clearWebViewData()} style={{ padding: 6, borderRadius: 6, backgroundColor: AppColors.primaryLight, borderWidth: 1, borderColor: AppColors.grayBorderSecondary }}>
3073
- <TrashIcon color={AppColors.errorColor} size={14}/>
3074
- </TouchableScale>
3075
- </View>} renderItem={({ item }) => {
3076
- const logColor = item.type === 'error' ? AppColors.errorColor :
3077
- item.type === 'warn' ? AppColors.warningIconGold :
3078
- item.type === 'info' ? AppColors.skyBlue :
3079
- AppColors.grayTextWeak;
3080
- const bgColor = item.type === 'error' ? 'rgba(255, 46, 87, 0.06)' :
3081
- item.type === 'warn' ? 'rgba(191, 162, 82, 0.08)' :
3082
- AppColors.primaryLight;
3711
+ filteredNavHistory.length === 0 && {
3712
+ flexGrow: 1,
3713
+ },
3714
+ ]} keyboardShouldPersistTaps="handled"/>) : webViewSubTab === 'console' ? (<View style={{
3715
+ flex: 1,
3716
+ backgroundColor: AppColors.grayBackground,
3717
+ }}>
3718
+ {webViewLogs.length > 0 ? (<FlatList data={webViewLogs} keyExtractor={(item) => String(item.id)} style={{ flex: 1 }} ListHeaderComponent={<View style={{
3719
+ paddingHorizontal: 16,
3720
+ paddingTop: 12,
3721
+ paddingBottom: 8,
3722
+ flexDirection: 'row',
3723
+ justifyContent: 'space-between',
3724
+ alignItems: 'center',
3725
+ }}>
3726
+ <Text style={styles.resultCount}>
3727
+ Console Logs ({webViewLogs.length})
3728
+ </Text>
3729
+ <TouchableScale onPress={() => clearWebViewData()} style={{
3730
+ padding: 6,
3731
+ borderRadius: 6,
3732
+ backgroundColor: AppColors.primaryLight,
3733
+ borderWidth: 1,
3734
+ borderColor: AppColors.grayBorderSecondary,
3735
+ }}>
3736
+ <TrashIcon color={AppColors.errorColor} size={14}/>
3737
+ </TouchableScale>
3738
+ </View>} renderItem={({ item }) => {
3739
+ const logColor = item.type === 'error'
3740
+ ? AppColors.errorColor
3741
+ : item.type === 'warn'
3742
+ ? AppColors.warningIconGold
3743
+ : item.type === 'info'
3744
+ ? AppColors.skyBlue
3745
+ : AppColors.grayTextWeak;
3746
+ const bgColor = item.type === 'error'
3747
+ ? 'rgba(255, 46, 87, 0.06)'
3748
+ : item.type === 'warn'
3749
+ ? 'rgba(191, 162, 82, 0.08)'
3750
+ : AppColors.primaryLight;
3083
3751
  const d = new Date(item.timestamp);
3084
3752
  const timeStr = `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}:${String(d.getSeconds()).padStart(2, '0')}`;
3085
- return (<View style={{
3753
+ return (<AnimatedEntrance index={item.id} distance={8} style={{
3086
3754
  marginHorizontal: 12,
3087
3755
  marginVertical: 3,
3088
3756
  borderRadius: 8,
@@ -3096,34 +3764,65 @@ const NetworkInspector = ({ enabled = true }) => {
3096
3764
  gap: 8,
3097
3765
  alignItems: 'flex-start',
3098
3766
  }}>
3099
- <View style={{ paddingTop: 1 }}>
3100
- <TerminalIcon color={logColor} size={11}/>
3101
- </View>
3102
- <View style={{ flex: 1, gap: 3 }}>
3103
- <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>
3104
- <Text style={{ fontFamily: AppFonts.interBold, fontSize: 10, color: logColor, textTransform: 'uppercase', letterSpacing: 0.5 }}>
3105
- {item.type}
3106
- </Text>
3107
- <Text style={{ fontFamily: AppFonts.interRegular, fontSize: 10, color: AppColors.grayTextWeak }}>
3108
- {timeStr}
3109
- </Text>
3110
- </View>
3111
- <Text style={{ fontFamily: AppFonts.interRegular, fontSize: 12, color: AppColors.primaryBlack, lineHeight: 16 }}>
3112
- {item.message}
3113
- </Text>
3767
+ <View style={{ paddingTop: 1 }}>
3768
+ <TerminalIcon color={logColor} size={11}/>
3769
+ </View>
3770
+ <View style={{ flex: 1, gap: 3 }}>
3771
+ <View style={{
3772
+ flexDirection: 'row',
3773
+ alignItems: 'center',
3774
+ justifyContent: 'space-between',
3775
+ }}>
3776
+ <Text style={{
3777
+ fontFamily: AppFonts.interBold,
3778
+ fontSize: 10,
3779
+ color: logColor,
3780
+ textTransform: 'uppercase',
3781
+ letterSpacing: 0.5,
3782
+ }}>
3783
+ {item.type}
3784
+ </Text>
3785
+ <Text style={{
3786
+ fontFamily: AppFonts.interRegular,
3787
+ fontSize: 10,
3788
+ color: AppColors.grayTextWeak,
3789
+ }}>
3790
+ {timeStr}
3791
+ </Text>
3792
+ </View>
3793
+ <Text style={{
3794
+ fontFamily: AppFonts.interRegular,
3795
+ fontSize: 12,
3796
+ color: AppColors.primaryBlack,
3797
+ lineHeight: 16,
3798
+ }}>
3799
+ {item.message}
3800
+ </Text>
3801
+ </View>
3802
+ </AnimatedEntrance>);
3803
+ }} initialNumToRender={20} maxToRenderPerBatch={20} windowSize={7} contentContainerStyle={[
3804
+ styles.listContent,
3805
+ webViewLogs.length === 0 && { flexGrow: 1 },
3806
+ ]}/>) : (<View style={styles.emptyContainer}>
3807
+ <View style={styles.emptyIconWrap}>
3808
+ <TerminalIcon color={AppColors.purple} size={32}/>
3114
3809
  </View>
3115
- </View>);
3116
- }} initialNumToRender={20} maxToRenderPerBatch={20} windowSize={7} contentContainerStyle={[styles.listContent, webViewLogs.length === 0 && { flexGrow: 1 }]}/>) : (<View style={styles.emptyContainer}>
3117
- <View style={styles.emptyIconWrap}>
3118
- <TerminalIcon color={AppColors.purple} size={32}/>
3119
- </View>
3120
- <Text style={styles.emptyTitle}>No Console Logs</Text>
3121
- <Text style={styles.emptySub}>
3122
- Console logs from the WebView will appear here.
3123
- </Text>
3124
- </View>)}
3125
- </View>) : (<View style={{ flex: 1, backgroundColor: AppColors.grayBackground }}>
3126
- {webViewHtml ? (OriginalWebView ? (<OriginalWebView source={{ html: webViewHtml, baseUrl: webViewHtmlUrl }} injectedJavaScript={previewInspectScript} onMessage={(event) => {
3810
+ <Text style={styles.emptyTitle}>
3811
+ No Console Logs
3812
+ </Text>
3813
+ <Text style={styles.emptySub}>
3814
+ Console logs from the WebView will appear
3815
+ here.
3816
+ </Text>
3817
+ </View>)}
3818
+ </View>) : (<View style={{
3819
+ flex: 1,
3820
+ backgroundColor: AppColors.grayBackground,
3821
+ }}>
3822
+ {webViewHtml ? (OriginalWebView ? (<OriginalWebView source={{
3823
+ html: webViewHtml,
3824
+ baseUrl: webViewHtmlUrl,
3825
+ }} injectedJavaScript={previewInspectScript} onMessage={(event) => {
3127
3826
  try {
3128
3827
  const data = JSON.parse(event.nativeEvent.data);
3129
3828
  if (data.type === 'preview-inspect') {
@@ -3138,27 +3837,39 @@ const NetworkInspector = ({ enabled = true }) => {
3138
3837
  }
3139
3838
  }
3140
3839
  catch (err) { }
3141
- }} style={{ flex: 1 }}/>) : (<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center', padding: 20 }}>
3142
- <Text style={{ color: AppColors.grayText, textAlign: 'center', fontFamily: AppFonts.interMedium }}>
3143
- react-native-webview is not installed in the target project. Install it to enable Preview mode.
3144
- </Text>
3145
- </View>)) : (<View style={styles.emptyContainer}>
3146
- <View style={styles.emptyIconWrap}>
3147
- <GlobeIcon color={AppColors.purple} size={32}/>
3148
- </View>
3149
- <Text style={styles.emptyTitle}>
3150
- No Preview Available
3151
- </Text>
3152
- <Text style={styles.emptySub}>
3153
- Load a page in the WebView to see its visual preview.
3154
- </Text>
3155
- </View>)}
3156
- </View>)}
3157
- </View>)) : activeTab === 'redux' ? (renderReduxTab()) : (<View style={{ flex: 1 }}>
3158
- {/* Non-scrollable details header */}
3159
- <View style={{ paddingHorizontal: 6, paddingTop: 4 }}>
3160
- <View style={styles.detailInfoBar}>
3161
- {(() => {
3840
+ }} style={{ flex: 1 }}/>) : (<View style={{
3841
+ flex: 1,
3842
+ justifyContent: 'center',
3843
+ alignItems: 'center',
3844
+ padding: 20,
3845
+ }}>
3846
+ <Text style={{
3847
+ color: AppColors.grayText,
3848
+ textAlign: 'center',
3849
+ fontFamily: AppFonts.interMedium,
3850
+ }}>
3851
+ react-native-webview is not installed in the
3852
+ target project. Install it to enable Preview
3853
+ mode.
3854
+ </Text>
3855
+ </View>)) : (<View style={styles.emptyContainer}>
3856
+ <View style={styles.emptyIconWrap}>
3857
+ <GlobeIcon color={AppColors.purple} size={32}/>
3858
+ </View>
3859
+ <Text style={styles.emptyTitle}>
3860
+ No Preview Available
3861
+ </Text>
3862
+ <Text style={styles.emptySub}>
3863
+ Load a page in the WebView to see its visual
3864
+ preview.
3865
+ </Text>
3866
+ </View>)}
3867
+ </View>)}
3868
+ </View>)) : activeTab === 'redux' ? (renderReduxTab()) : (<View style={{ flex: 1 }}>
3869
+ {/* Non-scrollable details header */}
3870
+ <View style={{ paddingHorizontal: 6, paddingTop: 4 }}>
3871
+ <View style={styles.detailInfoBar}>
3872
+ {(() => {
3162
3873
  let hostStr = '';
3163
3874
  let pathStr = detailDisplayUrl;
3164
3875
  let queryStr = '';
@@ -3187,31 +3898,29 @@ const NetworkInspector = ({ enabled = true }) => {
3187
3898
  }
3188
3899
  catch (e) { }
3189
3900
  return (<>
3190
- <View style={styles.detailInfoTop}>
3191
- <View style={{
3901
+ <View style={styles.detailInfoTop}>
3902
+ <View style={{
3192
3903
  flexDirection: 'row',
3193
3904
  alignItems: 'center',
3194
3905
  gap: 8,
3195
3906
  }}>
3196
- <View style={[
3907
+ <View style={[
3197
3908
  styles.methodBadge,
3198
3909
  {
3199
- backgroundColor: `${METHOD_COLORS[selected.method] ??
3200
- METHOD_COLORS.ALL}15`,
3910
+ backgroundColor: `${METHOD_COLORS[selected.method] ?? METHOD_COLORS.ALL}15`,
3201
3911
  },
3202
3912
  ]}>
3203
- <Text style={[
3913
+ <Text style={[
3204
3914
  styles.methodBadgeText,
3205
3915
  {
3206
- color: METHOD_COLORS[selected.method] ??
3207
- METHOD_COLORS.ALL,
3916
+ color: METHOD_COLORS[selected.method] ?? METHOD_COLORS.ALL,
3208
3917
  },
3209
3918
  ]}>
3210
- {selected.method}
3211
- </Text>
3212
- </View>
3919
+ {selected.method}
3920
+ </Text>
3921
+ </View>
3213
3922
 
3214
- {selected.status != null && (<View style={[
3923
+ {selected.status != null && (<View style={[
3215
3924
  styles.chip,
3216
3925
  {
3217
3926
  backgroundColor: selected.status === 0
@@ -3222,10 +3931,10 @@ const NetworkInspector = ({ enabled = true }) => {
3222
3931
  : `${getStatusColor(selected.status)}40`,
3223
3932
  },
3224
3933
  ]}>
3225
- {selected.status === 0 ? (<FailIcon size={8} color={AppColors.errorColor}/>) : (<Svg width={6} height={6} viewBox="0 0 10 10" fill="none">
3226
- <Circle cx="5" cy="5" r="5" fill={getStatusColor(selected.status)}/>
3227
- </Svg>)}
3228
- <Text style={[
3934
+ {selected.status === 0 ? (<FailIcon size={8} color={AppColors.errorColor}/>) : (<Svg width={6} height={6} viewBox="0 0 10 10" fill="none">
3935
+ <Circle cx="5" cy="5" r="5" fill={getStatusColor(selected.status)}/>
3936
+ </Svg>)}
3937
+ <Text style={[
3229
3938
  styles.chipText,
3230
3939
  {
3231
3940
  color: selected.status === 0
@@ -3233,29 +3942,38 @@ const NetworkInspector = ({ enabled = true }) => {
3233
3942
  : getStatusColor(selected.status),
3234
3943
  },
3235
3944
  ]}>
3236
- {selected.status === 0
3945
+ {selected.status === 0
3237
3946
  ? 'Failed'
3238
3947
  : String(selected.status)}
3239
- </Text>
3240
- </View>)}
3948
+ </Text>
3949
+ </View>)}
3241
3950
 
3242
- {selected.duration != null && (<View style={[styles.chip, { backgroundColor: 'rgba(104,75,155,0.08)', borderColor: 'rgba(104,75,155,0.18)' }]}>
3243
- <Text style={[styles.chipText, { color: AppColors.purple }]}>
3244
- {selected.duration}ms
3245
- </Text>
3246
- </View>)}
3247
- </View>
3248
- <View style={styles.detailInfoRight}>
3249
- <TouchableScale style={styles.iconSquareBtn} onPress={() => Linking.openURL(detailDisplayUrl)} hitSlop={12}>
3250
- <GlobeIcon color={AppColors.grayTextWeak} size={14}/>
3251
- </TouchableScale>
3252
- <CopyButton value={getFetchCommand(selected)} label="fetch()" iconType="fetch"/>
3253
- <CopyButton value={getCurlCommand(selected)} label="cURL" iconType="terminal"/>
3254
- <CopyButton value={detailDisplayUrl} label="URL"/>
3255
- </View>
3256
- </View>
3951
+ {selected.duration != null && (<View style={[
3952
+ styles.chip,
3953
+ {
3954
+ backgroundColor: 'rgba(104,75,155,0.08)',
3955
+ borderColor: 'rgba(104,75,155,0.18)',
3956
+ },
3957
+ ]}>
3958
+ <Text style={[
3959
+ styles.chipText,
3960
+ { color: AppColors.purple },
3961
+ ]}>
3962
+ {selected.duration}ms
3963
+ </Text>
3964
+ </View>)}
3965
+ </View>
3966
+ <View style={styles.detailInfoRight}>
3967
+ <TouchableScale style={styles.iconSquareBtn} onPress={() => Linking.openURL(detailDisplayUrl)} hitSlop={12}>
3968
+ <GlobeIcon color={AppColors.grayTextWeak} size={14}/>
3969
+ </TouchableScale>
3970
+ <CopyButton value={getFetchCommand(selected)} label="fetch()" iconType="fetch"/>
3971
+ <CopyButton value={getCurlCommand(selected)} label="cURL" iconType="terminal"/>
3972
+ <CopyButton value={detailDisplayUrl} label="URL"/>
3973
+ </View>
3974
+ </View>
3257
3975
 
3258
- <Pressable style={{
3976
+ <Pressable style={{
3259
3977
  backgroundColor: AppColors.grayBackground,
3260
3978
  borderRadius: 10,
3261
3979
  borderWidth: 1,
@@ -3263,28 +3981,59 @@ const NetworkInspector = ({ enabled = true }) => {
3263
3981
  padding: 10,
3264
3982
  marginTop: 6,
3265
3983
  }} onPress={() => Linking.openURL(detailDisplayUrl)}>
3266
- <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', marginBottom: 2 }}>
3267
- <Text style={{ fontFamily: AppFonts.interMedium, fontSize: 10, color: AppColors.grayTextWeak, flex: 1 }} numberOfLines={1}>
3268
- {hostStr || 'API Endpoint'}
3269
- </Text>
3270
- {queryStr ? (<View style={{ backgroundColor: 'rgba(104,75,155,0.08)', paddingHorizontal: 5, paddingVertical: 1, borderRadius: 4 }}>
3271
- <Text style={{ fontFamily: AppFonts.interBold, fontSize: 8.5, color: AppColors.purple }}>Query Params</Text>
3272
- </View>) : null}
3273
- </View>
3274
- <Text selectable={true} style={{ fontFamily: AppFonts.interBold, fontSize: 12, color: AppColors.primaryBlack, marginTop: 2 }} numberOfLines={2}>
3275
- {pathStr}
3276
- </Text>
3277
- {queryStr ? (<Text selectable={true} style={{ fontFamily: AppFonts.interRegular, fontSize: 10, color: AppColors.grayTextWeak, marginTop: 4 }} numberOfLines={1}>
3278
- {queryStr}
3279
- </Text>) : null}
3280
- </Pressable>
3281
- </>);
3984
+ <View style={{
3985
+ flexDirection: 'row',
3986
+ alignItems: 'center',
3987
+ justifyContent: 'space-between',
3988
+ marginBottom: 2,
3989
+ }}>
3990
+ <Text style={{
3991
+ fontFamily: AppFonts.interMedium,
3992
+ fontSize: 10,
3993
+ color: AppColors.grayTextWeak,
3994
+ flex: 1,
3995
+ }} numberOfLines={1}>
3996
+ {hostStr || 'API Endpoint'}
3997
+ </Text>
3998
+ {queryStr ? (<View style={{
3999
+ backgroundColor: 'rgba(104,75,155,0.08)',
4000
+ paddingHorizontal: 5,
4001
+ paddingVertical: 1,
4002
+ borderRadius: 4,
4003
+ }}>
4004
+ <Text style={{
4005
+ fontFamily: AppFonts.interBold,
4006
+ fontSize: 8.5,
4007
+ color: AppColors.purple,
4008
+ }}>
4009
+ Query Params
4010
+ </Text>
4011
+ </View>) : null}
4012
+ </View>
4013
+ <Text selectable={true} style={{
4014
+ fontFamily: AppFonts.interBold,
4015
+ fontSize: 12,
4016
+ color: AppColors.primaryBlack,
4017
+ marginTop: 2,
4018
+ }} numberOfLines={2}>
4019
+ {pathStr}
4020
+ </Text>
4021
+ {queryStr ? (<Text selectable={true} style={{
4022
+ fontFamily: AppFonts.interRegular,
4023
+ fontSize: 10,
4024
+ color: AppColors.grayTextWeak,
4025
+ marginTop: 4,
4026
+ }} numberOfLines={1}>
4027
+ {queryStr}
4028
+ </Text>) : null}
4029
+ </Pressable>
4030
+ </>);
3282
4031
  })()}
3283
- </View>
3284
- </View>
4032
+ </View>
4033
+ </View>
3285
4034
 
3286
- {/* Sticky Segment Control */}
3287
- <View style={{
4035
+ {/* Sticky Segment Control */}
4036
+ <View style={{
3288
4037
  flexDirection: 'row',
3289
4038
  backgroundColor: AppColors.grayBackground,
3290
4039
  borderRadius: 10,
@@ -3295,7 +4044,12 @@ const NetworkInspector = ({ enabled = true }) => {
3295
4044
  borderWidth: 1,
3296
4045
  borderColor: AppColors.dividerColor,
3297
4046
  }}>
3298
- {['metadata', 'headers', 'request', 'response'].map(tab => {
4047
+ {[
4048
+ 'metadata',
4049
+ 'headers',
4050
+ 'request',
4051
+ 'response',
4052
+ ].map(tab => {
3299
4053
  const isActive = apiDetailActiveTab === tab;
3300
4054
  if (tab === 'request' && selected.request == null)
3301
4055
  return null;
@@ -3309,7 +4063,9 @@ const NetworkInspector = ({ enabled = true }) => {
3309
4063
  return 'Response';
3310
4064
  };
3311
4065
  const getIcon = () => {
3312
- const iconColor = isActive ? '#FFFFFF' : AppColors.grayText;
4066
+ const iconColor = isActive
4067
+ ? '#FFFFFF'
4068
+ : AppColors.grayText;
3313
4069
  if (tab === 'metadata')
3314
4070
  return <StatusIcon color={iconColor}/>;
3315
4071
  if (tab === 'headers')
@@ -3318,122 +4074,139 @@ const NetworkInspector = ({ enabled = true }) => {
3318
4074
  return <RequestIcon color={iconColor}/>;
3319
4075
  return <ResponseIcon color={iconColor}/>;
3320
4076
  };
3321
- return (<TouchableOpacity key={tab} onPress={() => setApiDetailActiveTab(tab)} style={{
4077
+ return (<TouchableOpacity key={tab} onPress={() => {
4078
+ animateNextLayout();
4079
+ setApiDetailActiveTab(tab);
4080
+ }} style={{
3322
4081
  flex: 1,
3323
4082
  paddingVertical: 6,
3324
4083
  flexDirection: 'row',
3325
4084
  alignItems: 'center',
3326
4085
  justifyContent: 'center',
3327
4086
  borderRadius: 8,
3328
- backgroundColor: isActive ? AppColors.purple : 'transparent',
4087
+ backgroundColor: isActive
4088
+ ? AppColors.purple
4089
+ : 'transparent',
3329
4090
  gap: 4,
3330
4091
  }}>
3331
- {getIcon()}
3332
- <Text style={{
4092
+ {getIcon()}
4093
+ <Text style={{
3333
4094
  fontFamily: AppFonts.interBold,
3334
4095
  fontSize: 10,
3335
- color: isActive ? '#FFFFFF' : AppColors.grayText,
4096
+ color: isActive
4097
+ ? '#FFFFFF'
4098
+ : AppColors.grayText,
3336
4099
  }}>
3337
- {getLabel()}
3338
- </Text>
3339
- </TouchableOpacity>);
4100
+ {getLabel()}
4101
+ </Text>
4102
+ </TouchableOpacity>);
3340
4103
  })}
3341
- </View>
4104
+ </View>
3342
4105
 
3343
- {/* Scrollable Tab Content */}
3344
- <ScrollView style={styles.detailScroll} contentContainerStyle={{ paddingHorizontal: 6, paddingBottom: 24 }} showsVerticalScrollIndicator={true}>
3345
- {apiDetailActiveTab === 'metadata' && (<>
3346
- <MetaAccordion status={selected.status} statusColor={getStatusColor(selected.status)} duration={selected.duration} size={getSize(selected.response)} triggeredAt={formatDateTime(selected.startTime)} method={selected.method} contentType={selected.responseHeaders?.['content-type'] ||
4106
+ {/* Scrollable Tab Content */}
4107
+ <ScrollView style={styles.detailScroll} contentContainerStyle={{
4108
+ paddingHorizontal: 6,
4109
+ paddingBottom: 24,
4110
+ }} showsVerticalScrollIndicator={true}>
4111
+ {apiDetailActiveTab === 'metadata' && (<>
4112
+ <MetaAccordion status={selected.status} statusColor={getStatusColor(selected.status)} duration={selected.duration} size={getSize(selected.response)} triggeredAt={formatDateTime(selected.startTime)} method={selected.method} contentType={selected.responseHeaders?.['content-type'] ||
3347
4113
  selected.responseHeaders?.['Content-Type']} url={selected.url}/>
3348
4114
 
3349
- {(() => {
4115
+ {(() => {
3350
4116
  const routeInfo = logRouteMapRef.current.get(selected.id);
3351
4117
  if (!routeInfo || routeInfo.path === 'Navigators')
3352
4118
  return null;
3353
4119
  return <SourcePageCard routeInfo={routeInfo}/>;
3354
4120
  })()}
3355
4121
 
3356
- {(() => {
4122
+ {(() => {
3357
4123
  const cType = selected.responseHeaders?.['content-type'] ||
3358
4124
  selected.responseHeaders?.['Content-Type'];
3359
4125
  if (cType?.includes('image/')) {
3360
4126
  return (<View style={styles.imagePreviewWrapper}>
3361
- <Image source={{ uri: selected.url }} style={styles.imagePreview} resizeMode="contain"/>
3362
- <TouchableScale style={styles.imageDownloadBtn} onPress={() => Linking.openURL(selected.url)} hitSlop={10}>
3363
- <DownloadIcon color={AppColors.purple} size={18}/>
3364
- </TouchableScale>
3365
- </View>);
4127
+ <Image source={{ uri: selected.url }} style={styles.imagePreview} resizeMode="contain"/>
4128
+ <TouchableScale style={styles.imageDownloadBtn} onPress={() => Linking.openURL(selected.url)} hitSlop={10}>
4129
+ <DownloadIcon color={AppColors.purple} size={18}/>
4130
+ </TouchableScale>
4131
+ </View>);
3366
4132
  }
3367
4133
  return null;
3368
4134
  })()}
3369
- </>)}
4135
+ </>)}
3370
4136
 
3371
- {apiDetailActiveTab === 'headers' && (<>
3372
- <View style={styles.detailSearchRow}>
3373
- <View style={styles.detailSearchBox}>
3374
- <TextInput placeholder="Search headers..." placeholderTextColor={AppColors.grayTextWeak} value={detailSearch} onChangeText={setDetailSearch} style={styles.detailSearchInput} autoCorrect={false} autoCapitalize="none"/>
3375
- {detailSearch.length > 0 && (<Pressable onPress={() => setDetailSearch('')} hitSlop={10} style={{ padding: 8 }}>
3376
- <ClearIcon color={AppColors.grayTextWeak} size={14}/>
3377
- </Pressable>)}
3378
- </View>
3379
- </View>
4137
+ {apiDetailActiveTab === 'headers' && (<>
4138
+ <View style={styles.detailSearchRow}>
4139
+ <View style={styles.detailSearchBox}>
4140
+ <TextInput placeholder="Search headers..." placeholderTextColor={AppColors.grayTextWeak} value={detailSearch} onChangeText={setDetailSearch} style={styles.detailSearchInput} autoCorrect={false} autoCapitalize="none"/>
4141
+ {detailSearch.length > 0 && (<Pressable onPress={() => setDetailSearch('')} hitSlop={10} style={{ padding: 8 }}>
4142
+ <ClearIcon color={AppColors.grayTextWeak} size={14}/>
4143
+ </Pressable>)}
4144
+ </View>
4145
+ </View>
3380
4146
 
3381
- <HeadersSection title="Request Headers" headers={selected.requestHeaders} search={detailSearch} resetKey={selected.id}/>
3382
- <HeadersSection title="Response Headers" headers={selected.responseHeaders} search={detailSearch} resetKey={selected.id}/>
3383
- </>)}
4147
+ <HeadersSection title="Request Headers" headers={selected.requestHeaders} search={detailSearch} resetKey={selected.id}/>
4148
+ <HeadersSection title="Response Headers" headers={selected.responseHeaders} search={detailSearch} resetKey={selected.id}/>
4149
+ </>)}
3384
4150
 
3385
- {apiDetailActiveTab === 'request' && selected.request != null && (<>
3386
- <View style={styles.detailSearchRow}>
3387
- <View style={styles.detailSearchBox}>
3388
- <TextInput placeholder="Search request..." placeholderTextColor={AppColors.grayTextWeak} value={detailSearch} onChangeText={setDetailSearch} style={styles.detailSearchInput} autoCorrect={false} autoCapitalize="none"/>
3389
- {detailSearch.length > 0 && (<Pressable onPress={() => setDetailSearch('')} hitSlop={10} style={{ padding: 8 }}>
3390
- <ClearIcon color={AppColors.grayTextWeak} size={14}/>
3391
- </Pressable>)}
3392
- </View>
3393
- </View>
4151
+ {apiDetailActiveTab === 'request' &&
4152
+ selected.request != null && (<>
4153
+ <View style={styles.detailSearchRow}>
4154
+ <View style={styles.detailSearchBox}>
4155
+ <TextInput placeholder="Search request..." placeholderTextColor={AppColors.grayTextWeak} value={detailSearch} onChangeText={setDetailSearch} style={styles.detailSearchInput} autoCorrect={false} autoCapitalize="none"/>
4156
+ {detailSearch.length > 0 && (<Pressable onPress={() => setDetailSearch('')} hitSlop={10} style={{ padding: 8 }}>
4157
+ <ClearIcon color={AppColors.grayTextWeak} size={14}/>
4158
+ </Pressable>)}
4159
+ </View>
4160
+ </View>
3394
4161
 
3395
- <View style={styles.sectionContainer}>
3396
- <SectionHeader title="Request" value={selected.request} expanded={reqExpanded} onToggleExpand={() => setReqExpanded(v => !v)} showDiff={prevRequestData != null} isDiffing={showReqDiff} onToggleDiff={() => {
4162
+ <View style={styles.sectionContainer}>
4163
+ <SectionHeader title="Request" value={selected.request} expanded={reqExpanded} onToggleExpand={() => setReqExpanded(v => !v)} showDiff={prevRequestData != null} isDiffing={showReqDiff} onToggleDiff={() => {
3397
4164
  setShowReqDiff(v => !v);
3398
4165
  if (!reqExpanded && !showReqDiff)
3399
4166
  setReqExpanded(true);
3400
4167
  }}/>
3401
- {showReqDiff ? (<DiffViewer oldData={prevRequestData} newData={selected.request} forceOpen={reqExpanded}/>) : (<JsonViewer data={selected.request} search={detailSearch} forceOpen={reqExpanded}/>)}
3402
- </View>
3403
- </>)}
4168
+ {showReqDiff ? (<DiffViewer oldData={prevRequestData} newData={selected.request} forceOpen={reqExpanded}/>) : (<JsonViewer data={selected.request} search={detailSearch} forceOpen={reqExpanded}/>)}
4169
+ </View>
4170
+ </>)}
3404
4171
 
3405
- {apiDetailActiveTab === 'response' && (<>
3406
- <View style={styles.detailSearchRow}>
3407
- <View style={styles.detailSearchBox}>
3408
- <TextInput placeholder="Search response..." placeholderTextColor={AppColors.grayTextWeak} value={detailSearch} onChangeText={setDetailSearch} style={styles.detailSearchInput} autoCorrect={false} autoCapitalize="none"/>
3409
- {detailSearch.length > 0 && (<Pressable onPress={() => setDetailSearch('')} hitSlop={10} style={{ padding: 8 }}>
3410
- <ClearIcon color={AppColors.grayTextWeak} size={14}/>
3411
- </Pressable>)}
3412
- </View>
3413
- </View>
4172
+ {apiDetailActiveTab === 'response' && (<>
4173
+ <View style={styles.detailSearchRow}>
4174
+ <View style={styles.detailSearchBox}>
4175
+ <TextInput placeholder="Search response..." placeholderTextColor={AppColors.grayTextWeak} value={detailSearch} onChangeText={setDetailSearch} style={styles.detailSearchInput} autoCorrect={false} autoCapitalize="none"/>
4176
+ {detailSearch.length > 0 && (<Pressable onPress={() => setDetailSearch('')} hitSlop={10} style={{ padding: 8 }}>
4177
+ <ClearIcon color={AppColors.grayTextWeak} size={14}/>
4178
+ </Pressable>)}
4179
+ </View>
4180
+ </View>
3414
4181
 
3415
- <View style={styles.sectionContainer}>
3416
- <SectionHeader title="Response" value={selected.response} expanded={resExpanded} onToggleExpand={() => setResExpanded(v => !v)} showDiff={prevResponseData != null} isDiffing={showResDiff} onToggleDiff={() => {
4182
+ <View style={styles.sectionContainer}>
4183
+ <SectionHeader title="Response" value={selected.response} expanded={resExpanded} onToggleExpand={() => setResExpanded(v => !v)} showDiff={prevResponseData != null} isDiffing={showResDiff} onToggleDiff={() => {
3417
4184
  setShowResDiff(v => !v);
3418
4185
  if (!resExpanded && !showResDiff)
3419
4186
  setResExpanded(true);
3420
4187
  }}/>
3421
- {showResDiff ? (<DiffViewer oldData={prevResponseData} newData={selected.response} forceOpen={resExpanded}/>) : (<JsonViewer data={selected.response} search={detailSearch} forceOpen={resExpanded}/>)}
3422
- </View>
3423
- </>)}
3424
- </ScrollView>
3425
- </View>)) : (<View style={styles.empty}>
3426
- <ActivityIndicator size="large" color={AppColors.purple}/>
3427
- <Text style={[styles.emptySub, { marginTop: 12 }]}>
3428
- Loading logs...
3429
- </Text>
3430
- </View>)}
4188
+ {showResDiff ? (<DiffViewer oldData={prevResponseData} newData={selected.response} forceOpen={resExpanded}/>) : (<JsonViewer data={selected.response} search={detailSearch} forceOpen={resExpanded}/>)}
4189
+ </View>
4190
+ </>)}
4191
+ </ScrollView>
4192
+ </View>)) : (<View style={styles.empty}>
4193
+ <ActivityIndicator size="large" color={AppColors.purple}/>
4194
+ <Text style={[styles.emptySub, { marginTop: 12 }]}>
4195
+ Loading logs...
4196
+ </Text>
4197
+ </View>)}
3431
4198
 
3432
- {settingsPage !== null && (<View style={[StyleSheet.absoluteFill, { backgroundColor: AppColors.grayBackground, zIndex: 99999 }]}>
3433
- {renderSettings()}
3434
- </View>)}
4199
+ {settingsPage !== null && (<View style={[
4200
+ StyleSheet.absoluteFill,
4201
+ {
4202
+ backgroundColor: AppColors.grayBackground,
4203
+ zIndex: 99999,
4204
+ },
4205
+ ]}>
4206
+ {renderSettings()}
4207
+ </View>)}
4208
+ </View>
3435
4209
  </View>
3436
- </View>
3437
4210
  </ErrorBoundary>)}
3438
4211
  </Modal>
3439
4212
  </>);