react-native-inapp-inspector 2.2.9 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/dist/commonjs/components/AppHeaderLogo.js +45 -12
  2. package/dist/commonjs/components/BrandSquareIcon.js +92 -49
  3. package/dist/commonjs/components/EmptyState.d.ts +7 -2
  4. package/dist/commonjs/components/EmptyState.js +11 -5
  5. package/dist/commonjs/components/Inspector/AnalyticsTab.js +29 -18
  6. package/dist/commonjs/components/Inspector/ConsoleTab.js +6 -1
  7. package/dist/commonjs/components/Inspector/CrashDetail.js +43 -9
  8. package/dist/commonjs/components/Inspector/CrashTab.js +34 -3
  9. package/dist/commonjs/components/Inspector/DeviceInfoTab.js +80 -16
  10. package/dist/commonjs/components/Inspector/InspectorHeader.js +16 -3
  11. package/dist/commonjs/components/Inspector/MainScreen.js +7 -11
  12. package/dist/commonjs/components/Inspector/NetworkDetail.js +3 -2
  13. package/dist/commonjs/components/Inspector/NetworkTab.js +103 -4
  14. package/dist/commonjs/components/Inspector/ReduxTab.js +19 -3
  15. package/dist/commonjs/components/Inspector/SettingsPanel.js +4 -4
  16. package/dist/commonjs/components/Inspector/StorageTab.js +123 -92
  17. package/dist/commonjs/components/JsonViewer.js +2 -0
  18. package/dist/commonjs/components/MetaAccordion.js +15 -3
  19. package/dist/commonjs/components/SkeletonPlaceholder.d.ts +6 -0
  20. package/dist/commonjs/components/SkeletonPlaceholder.js +208 -0
  21. package/dist/commonjs/constants/version.d.ts +1 -1
  22. package/dist/commonjs/constants/version.js +1 -1
  23. package/dist/commonjs/customHooks/networkLogger.d.ts +3 -0
  24. package/dist/commonjs/customHooks/networkLogger.js +10 -1
  25. package/dist/commonjs/customHooks/storageInspector.d.ts +2 -2
  26. package/dist/commonjs/customHooks/storageInspector.js +17 -13
  27. package/dist/commonjs/helpers/index.d.ts +6 -0
  28. package/dist/commonjs/helpers/index.js +196 -6
  29. package/dist/commonjs/helpers/searchQueryParser.js +62 -20
  30. package/dist/commonjs/i18n/index.d.ts +13 -0
  31. package/dist/commonjs/i18n/index.js +35 -2
  32. package/dist/commonjs/index.d.ts +3 -0
  33. package/dist/commonjs/index.js +108 -35
  34. package/dist/commonjs/styles/AppColors.d.ts +4 -0
  35. package/dist/commonjs/styles/AppColors.js +11 -1
  36. package/dist/commonjs/styles/AppFonts.d.ts +11 -0
  37. package/dist/commonjs/styles/AppFonts.js +11 -1
  38. package/dist/commonjs/types/interfaces.d.ts +2 -2
  39. package/dist/esm/components/AppHeaderLogo.js +45 -12
  40. package/dist/esm/components/BrandSquareIcon.js +92 -49
  41. package/dist/esm/components/EmptyState.d.ts +7 -2
  42. package/dist/esm/components/EmptyState.js +11 -5
  43. package/dist/esm/components/Inspector/AnalyticsTab.js +30 -19
  44. package/dist/esm/components/Inspector/ConsoleTab.js +6 -1
  45. package/dist/esm/components/Inspector/CrashDetail.js +43 -9
  46. package/dist/esm/components/Inspector/CrashTab.js +34 -3
  47. package/dist/esm/components/Inspector/DeviceInfoTab.js +80 -16
  48. package/dist/esm/components/Inspector/InspectorHeader.js +17 -4
  49. package/dist/esm/components/Inspector/MainScreen.js +8 -12
  50. package/dist/esm/components/Inspector/NetworkDetail.js +3 -2
  51. package/dist/esm/components/Inspector/NetworkTab.js +103 -4
  52. package/dist/esm/components/Inspector/ReduxTab.js +19 -3
  53. package/dist/esm/components/Inspector/SettingsPanel.js +4 -4
  54. package/dist/esm/components/Inspector/StorageTab.js +124 -93
  55. package/dist/esm/components/JsonViewer.js +2 -0
  56. package/dist/esm/components/MetaAccordion.js +15 -3
  57. package/dist/esm/components/SkeletonPlaceholder.d.ts +6 -0
  58. package/dist/esm/components/SkeletonPlaceholder.js +173 -0
  59. package/dist/esm/constants/version.d.ts +1 -1
  60. package/dist/esm/constants/version.js +1 -1
  61. package/dist/esm/customHooks/networkLogger.d.ts +3 -0
  62. package/dist/esm/customHooks/networkLogger.js +8 -0
  63. package/dist/esm/customHooks/storageInspector.d.ts +2 -2
  64. package/dist/esm/customHooks/storageInspector.js +17 -13
  65. package/dist/esm/helpers/index.d.ts +6 -0
  66. package/dist/esm/helpers/index.js +193 -5
  67. package/dist/esm/helpers/searchQueryParser.js +62 -20
  68. package/dist/esm/i18n/index.d.ts +13 -0
  69. package/dist/esm/i18n/index.js +33 -3
  70. package/dist/esm/index.d.ts +3 -0
  71. package/dist/esm/index.js +97 -37
  72. package/dist/esm/styles/AppColors.d.ts +4 -0
  73. package/dist/esm/styles/AppColors.js +9 -0
  74. package/dist/esm/styles/AppFonts.d.ts +11 -0
  75. package/dist/esm/styles/AppFonts.js +9 -0
  76. package/dist/esm/types/interfaces.d.ts +2 -2
  77. package/package.json +1 -1
  78. package/src/components/AppHeaderLogo.tsx +46 -12
  79. package/src/components/BrandSquareIcon.tsx +127 -78
  80. package/src/components/EmptyState.tsx +27 -5
  81. package/src/components/Inspector/AnalyticsTab.tsx +48 -27
  82. package/src/components/Inspector/ConsoleTab.tsx +10 -0
  83. package/src/components/Inspector/CrashDetail.tsx +65 -9
  84. package/src/components/Inspector/CrashTab.tsx +41 -3
  85. package/src/components/Inspector/DeviceInfoTab.tsx +87 -16
  86. package/src/components/Inspector/InspectorHeader.tsx +24 -4
  87. package/src/components/Inspector/MainScreen.tsx +7 -11
  88. package/src/components/Inspector/NetworkDetail.tsx +4 -4
  89. package/src/components/Inspector/NetworkTab.tsx +129 -4
  90. package/src/components/Inspector/ReduxTab.tsx +26 -8
  91. package/src/components/Inspector/SettingsPanel.tsx +4 -4
  92. package/src/components/Inspector/StorageTab.tsx +191 -117
  93. package/src/components/JsonViewer.tsx +2 -0
  94. package/src/components/MetaAccordion.tsx +18 -3
  95. package/src/components/SkeletonPlaceholder.tsx +206 -0
  96. package/src/constants/version.ts +1 -1
  97. package/src/customHooks/networkLogger.ts +15 -0
  98. package/src/customHooks/storageInspector.ts +18 -14
  99. package/src/helpers/index.ts +213 -5
  100. package/src/helpers/searchQueryParser.ts +63 -20
  101. package/src/i18n/index.ts +38 -3
  102. package/src/index.tsx +124 -37
  103. package/src/styles/AppColors.ts +9 -0
  104. package/src/styles/AppFonts.ts +16 -0
  105. package/src/types/interfaces.ts +2 -2
@@ -433,9 +433,27 @@ const CrashTab = React.memo(() => {
433
433
  autoCorrect={false}
434
434
  />
435
435
  {searchQuery.length > 0 && (
436
- <TouchableOpacity onPress={() => setSearchQuery('')} hitSlop={8}>
437
- <ClearIcon color={AppColors.grayTextWeak} size={14} />
438
- </TouchableOpacity>
436
+ <View style={{flexDirection: 'row', alignItems: 'center', gap: 4}}>
437
+ <View
438
+ style={{
439
+ backgroundColor: `${AppColors.purple}20`,
440
+ borderRadius: 10,
441
+ paddingHorizontal: 6,
442
+ paddingVertical: 1.5,
443
+ }}>
444
+ <Text
445
+ style={{
446
+ color: AppColors.purple,
447
+ fontSize: 9.5,
448
+ fontFamily: AppFonts.interBold,
449
+ }}>
450
+ {filteredList.length}
451
+ </Text>
452
+ </View>
453
+ <TouchableOpacity onPress={() => setSearchQuery('')} hitSlop={8}>
454
+ <ClearIcon color={AppColors.grayTextWeak} size={14} />
455
+ </TouchableOpacity>
456
+ </View>
439
457
  )}
440
458
  </View>
441
459
 
@@ -539,6 +557,26 @@ const CrashTab = React.memo(() => {
539
557
  ? t('crash.emptySearchSubtitle')
540
558
  : t('crash.emptySubtitle')}
541
559
  </Text>
560
+ {searchQuery.length > 0 && (
561
+ <TouchableScale
562
+ onPress={() => setSearchQuery('')}
563
+ style={{
564
+ marginTop: 10,
565
+ paddingHorizontal: 14,
566
+ paddingVertical: 7,
567
+ borderRadius: 8,
568
+ backgroundColor: AppColors.purple,
569
+ }}>
570
+ <Text
571
+ style={{
572
+ fontFamily: AppFonts.interBold,
573
+ fontSize: 11.5,
574
+ color: AppColors.white,
575
+ }}>
576
+ Clear Search
577
+ </Text>
578
+ </TouchableScale>
579
+ )}
542
580
  </ScrollView>
543
581
  ) : (
544
582
  <FlatList
@@ -86,9 +86,9 @@ const InfoRow = React.memo(
86
86
  styles.infoRow,
87
87
  isLast && {borderBottomWidth: 0},
88
88
  ]}>
89
- <View style={{flex: 1, paddingRight: 8}}>
90
- <View style={{flexDirection: 'row', alignItems: 'center', gap: 6}}>
91
- <Text style={styles.infoLabel}>{label}</Text>
89
+ <View style={{flex: 1, paddingRight: 8, minWidth: 0}}>
90
+ <View style={{flexDirection: 'row', alignItems: 'center', flexWrap: 'wrap', gap: 6}}>
91
+ <Text style={styles.infoLabel} numberOfLines={2}>{label}</Text>
92
92
  {badge && (
93
93
  <View
94
94
  style={[
@@ -101,9 +101,17 @@ const InfoRow = React.memo(
101
101
  </View>
102
102
  )}
103
103
  </View>
104
- {subtext && <Text style={styles.infoSubtext}>{subtext}</Text>}
104
+ {subtext && <Text style={styles.infoSubtext} numberOfLines={2}>{subtext}</Text>}
105
105
  </View>
106
- <View style={{flexDirection: 'row', alignItems: 'center', gap: 8}}>
106
+ <View
107
+ style={{
108
+ flexDirection: 'row',
109
+ alignItems: 'center',
110
+ gap: 6,
111
+ flexShrink: 0,
112
+ maxWidth: '58%',
113
+ justifyContent: 'flex-end',
114
+ }}>
107
115
  <Text
108
116
  style={[
109
117
  styles.infoValue,
@@ -111,7 +119,7 @@ const InfoRow = React.memo(
111
119
  color: value ? AppColors.emerald500 : AppColors.errorColor,
112
120
  },
113
121
  ]}
114
- numberOfLines={2}
122
+ numberOfLines={3}
115
123
  selectable>
116
124
  {displayValue}
117
125
  </Text>
@@ -446,6 +454,16 @@ export const DeviceInfoTab = React.memo(() => {
446
454
  return text.toLowerCase().includes(search.toLowerCase().trim());
447
455
  };
448
456
 
457
+ const hasAnyMatch = useMemo(() => {
458
+ if (!search.trim()) return true;
459
+ const query = search.toLowerCase().trim();
460
+ return (
461
+ JSON.stringify(fullDeviceData).toLowerCase().includes(query) ||
462
+ ipAddress.toLowerCase().includes(query) ||
463
+ pseudoUDID.toLowerCase().includes(query)
464
+ );
465
+ }, [search, fullDeviceData, ipAddress, pseudoUDID]);
466
+
449
467
  return (
450
468
  <View style={styles.container}>
451
469
  {/* ─── Top Sub-Tabs Navigation Bar ─── */}
@@ -527,11 +545,11 @@ export const DeviceInfoTab = React.memo(() => {
527
545
  <AndroidIcon size={20} color={AppColors.white} />
528
546
  )}
529
547
  </View>
530
- <View style={{flex: 1}}>
531
- <Text style={styles.heroTitle}>
548
+ <View style={{flex: 1, minWidth: 0}}>
549
+ <Text style={styles.heroTitle} numberOfLines={1} ellipsizeMode="tail">
532
550
  {deviceMetrics?.deviceModel || (Platform.constants as any)?.Model || (Platform.OS === 'ios' ? 'Apple iPhone' : 'Android Device')}
533
551
  </Text>
534
- <Text style={styles.heroSubtitle}>
552
+ <Text style={styles.heroSubtitle} numberOfLines={1} ellipsizeMode="tail">
535
553
  {Platform.OS === 'ios' ? `iOS ${Platform.Version}` : `Android ${Platform.Version} (API ${deviceMetrics?.apiLevel || Platform.Version})`}
536
554
  </Text>
537
555
  </View>
@@ -544,22 +562,22 @@ export const DeviceInfoTab = React.memo(() => {
544
562
  {/* Quick Metrics Strip */}
545
563
  <View style={styles.heroMetricsStrip}>
546
564
  <View style={styles.heroMetricItem}>
547
- <Text style={styles.heroMetricLabel}>IP ADDRESS</Text>
548
- <Text style={styles.heroMetricValue} numberOfLines={1}>
565
+ <Text style={styles.heroMetricLabel} numberOfLines={1} ellipsizeMode="tail">IP ADDRESS</Text>
566
+ <Text style={styles.heroMetricValue} numberOfLines={1} ellipsizeMode="tail">
549
567
  {ipAddress}
550
568
  </Text>
551
569
  </View>
552
570
  <View style={styles.heroMetricDivider} />
553
571
  <View style={styles.heroMetricItem}>
554
- <Text style={styles.heroMetricLabel}>RAM (USED/TOTAL)</Text>
555
- <Text style={styles.heroMetricValue}>
572
+ <Text style={styles.heroMetricLabel} numberOfLines={1} ellipsizeMode="tail">RAM (USED/TOTAL)</Text>
573
+ <Text style={styles.heroMetricValue} numberOfLines={1} ellipsizeMode="tail">
556
574
  {usedRamMb}/{totalRamMb} MB
557
575
  </Text>
558
576
  </View>
559
577
  <View style={styles.heroMetricDivider} />
560
578
  <View style={styles.heroMetricItem}>
561
- <Text style={styles.heroMetricLabel}>UPTIME</Text>
562
- <Text style={styles.heroMetricValue}>{deviceUptime}</Text>
579
+ <Text style={styles.heroMetricLabel} numberOfLines={1} ellipsizeMode="tail">UPTIME</Text>
580
+ <Text style={styles.heroMetricValue} numberOfLines={1} ellipsizeMode="tail">{deviceUptime}</Text>
563
581
  </View>
564
582
  </View>
565
583
  </View>
@@ -947,6 +965,22 @@ export const DeviceInfoTab = React.memo(() => {
947
965
  </View>
948
966
  )}
949
967
 
968
+ {/* Empty search results state */}
969
+ {search.trim().length > 0 && !hasAnyMatch && (
970
+ <View style={styles.emptySearchCard}>
971
+ <SearchIcon size={26} color={AppColors.grayTextWeak} />
972
+ <Text style={styles.emptySearchTitle}>No matching device specifications</Text>
973
+ <Text style={styles.emptySearchSubtitle}>
974
+ No properties matched &ldquo;{search}&rdquo;
975
+ </Text>
976
+ <TouchableScale
977
+ onPress={() => setSearch('')}
978
+ style={styles.clearSearchBtn}>
979
+ <Text style={styles.clearSearchBtnText}>Clear Search</Text>
980
+ </TouchableScale>
981
+ </View>
982
+ )}
983
+
950
984
  <View style={{height: 48}} />
951
985
  </ScrollView>
952
986
  </View>
@@ -1120,6 +1154,8 @@ const styles = StyleSheet.create({
1120
1154
  heroMetricItem: {
1121
1155
  flex: 1,
1122
1156
  alignItems: 'center',
1157
+ paddingHorizontal: 2,
1158
+ minWidth: 0,
1123
1159
  },
1124
1160
  heroMetricLabel: {
1125
1161
  fontFamily: AppFonts.interBold,
@@ -1191,7 +1227,7 @@ const styles = StyleSheet.create({
1191
1227
  fontFamily: AppFonts.interBold,
1192
1228
  fontSize: 12,
1193
1229
  color: AppColors.primaryBlack,
1194
- maxWidth: 180,
1230
+ flexShrink: 1,
1195
1231
  textAlign: 'right',
1196
1232
  },
1197
1233
  rowCopyBtn: {
@@ -1199,6 +1235,41 @@ const styles = StyleSheet.create({
1199
1235
  borderRadius: 4,
1200
1236
  backgroundColor: AppColors.grayBackground,
1201
1237
  },
1238
+ emptySearchCard: {
1239
+ backgroundColor: AppColors.primaryLight,
1240
+ borderRadius: 14,
1241
+ borderWidth: 1,
1242
+ borderColor: AppColors.grayBorderSecondary,
1243
+ padding: 24,
1244
+ alignItems: 'center',
1245
+ justifyContent: 'center',
1246
+ gap: 8,
1247
+ marginTop: 8,
1248
+ },
1249
+ emptySearchTitle: {
1250
+ fontFamily: AppFonts.interBold,
1251
+ fontSize: 13.5,
1252
+ color: AppColors.primaryBlack,
1253
+ textAlign: 'center',
1254
+ },
1255
+ emptySearchSubtitle: {
1256
+ fontFamily: AppFonts.interRegular,
1257
+ fontSize: 11.5,
1258
+ color: AppColors.grayTextWeak,
1259
+ textAlign: 'center',
1260
+ },
1261
+ clearSearchBtn: {
1262
+ marginTop: 6,
1263
+ paddingHorizontal: 14,
1264
+ paddingVertical: 6.5,
1265
+ borderRadius: 8,
1266
+ backgroundColor: AppColors.purple,
1267
+ },
1268
+ clearSearchBtnText: {
1269
+ fontFamily: AppFonts.interBold,
1270
+ fontSize: 11,
1271
+ color: AppColors.white,
1272
+ },
1202
1273
  });
1203
1274
 
1204
1275
  export default DeviceInfoTab;
@@ -18,7 +18,14 @@ import {AppFonts} from '../../styles/AppFonts';
18
18
  import {METHOD_COLORS} from '../../constants';
19
19
  import {LIB_VERSION} from '../../constants';
20
20
  import {Method} from '../../types';
21
- import {getStatusColor, getAppName, formatTime, getSize} from '../../helpers';
21
+ import {
22
+ getStatusColor,
23
+ getAppName,
24
+ formatTime,
25
+ getSize,
26
+ getAppVersionAndBuild,
27
+ } from '../../helpers';
28
+ import {getNativeDeviceMetrics} from '../../native/NativeInspector';
22
29
  import {UpdateAvailableModal} from './UpdateAvailableModal';
23
30
  import {
24
31
  WhiteBackNavigation,
@@ -71,6 +78,21 @@ const InspectorHeader = React.memo(() => {
71
78
  } = useInspector();
72
79
 
73
80
  const [showUpdateModal, setShowUpdateModal] = React.useState<boolean>(false);
81
+ const [appVersionString, setAppVersionString] = React.useState<string>(() => {
82
+ return getAppVersionAndBuild().formatted;
83
+ });
84
+
85
+ React.useEffect(() => {
86
+ getNativeDeviceMetrics()
87
+ .then(metrics => {
88
+ if (metrics?.appVersion) {
89
+ const v = metrics.appVersion;
90
+ const b = metrics.appBuild || '1';
91
+ setAppVersionString(`${v} (${b})`);
92
+ }
93
+ })
94
+ .catch(() => {});
95
+ }, []);
74
96
 
75
97
  const envConfig = useMemo(() => {
76
98
  const rawEnv = (environment || (__DEV__ ? 'DEV' : 'PROD')).trim();
@@ -375,9 +397,7 @@ const InspectorHeader = React.memo(() => {
375
397
  letterSpacing: 0.1,
376
398
  }}
377
399
  numberOfLines={1}>
378
- {Platform.OS === 'ios'
379
- ? `iOS ${Platform.Version}`
380
- : `Android ${Platform.Version}`}
400
+ {appVersionString}
381
401
  </Text>
382
402
  </View>
383
403
 
@@ -21,6 +21,7 @@ import LogDetail from './LogDetail';
21
21
  import ConsoleTab from './ConsoleTab';
22
22
  import AnalyticsTab from './AnalyticsTab';
23
23
  import AnalyticsDetail from '../AnalyticsDetail';
24
+ import SkeletonPlaceholder from '../SkeletonPlaceholder';
24
25
  import ReduxTab from './ReduxTab';
25
26
  import ReduxDetail from './ReduxDetail';
26
27
  import BundleTab from './BundleTab';
@@ -51,7 +52,7 @@ const MainScreen = () => {
51
52
  settingsPage,
52
53
  activeTab,
53
54
  isReady,
54
- isEnabled,
55
+ enabled,
55
56
  useNativeFab,
56
57
  hasNavigationContext,
57
58
  setNavState,
@@ -104,7 +105,7 @@ const MainScreen = () => {
104
105
  return (
105
106
  <>
106
107
  {(Platform.OS === 'ios' || Platform.OS === 'android') &&
107
- isEnabled &&
108
+ enabled &&
108
109
  !visible &&
109
110
  !useNativeFab && <FabLauncher />}
110
111
  <Modal
@@ -137,11 +138,11 @@ const MainScreen = () => {
137
138
  <InspectorHeader />
138
139
 
139
140
  <View style={{flex: 1}}>
141
+ {/* ─── Horizontal Scrollable Tab Bar inside Content (Always visible) ─── */}
142
+ {!isDetailActive && <TabBar />}
143
+
140
144
  {isReady ? (
141
145
  <View style={{flex: 1}}>
142
- {/* ─── Horizontal Scrollable Tab Bar inside Content ─── */}
143
- {!isDetailActive && <TabBar />}
144
-
145
146
  {/* Persistent List Layer - Never unmounted, preserves 100% native scroll with smooth tab transition */}
146
147
  <Animated.View
147
148
  style={[
@@ -207,12 +208,7 @@ const MainScreen = () => {
207
208
  )}
208
209
  </View>
209
210
  ) : (
210
- <View style={styles.empty}>
211
- <ActivityIndicator size="large" color={AppColors.purple} />
212
- <Text style={[styles.emptySub, {marginTop: 12}]}>
213
- Loading logs...
214
- </Text>
215
- </View>
211
+ <SkeletonPlaceholder />
216
212
  )}
217
213
 
218
214
  {/* Settings Panel Layer - Rendered on top with smooth slide & spring transition */}
@@ -568,10 +568,10 @@ const NetworkDetail = React.memo(() => {
568
568
  />
569
569
 
570
570
  {(() => {
571
- const routeInfo = logRouteMapRef.current.get(
572
- selected.id,
573
- );
574
- if (!routeInfo || routeInfo.path === 'Navigators')
571
+ const routeInfo =
572
+ logRouteMapRef.current.get(selected.id) ||
573
+ (selected as any)?.routeInfo;
574
+ if (!routeInfo || !routeInfo.path || routeInfo.path === 'Navigators')
575
575
  return null;
576
576
  return <SourcePageCard routeInfo={routeInfo} />;
577
577
  })()}
@@ -665,7 +665,7 @@ const NetworkTab = React.memo(() => {
665
665
  style={{
666
666
  backgroundColor: `${AppColors.purple}20`,
667
667
  borderRadius: 10,
668
- paddingHorizontal: 5,
668
+ paddingHorizontal: 6,
669
669
  paddingVertical: 1.5,
670
670
  }}>
671
671
  <Text
@@ -759,19 +759,130 @@ const NetworkTab = React.memo(() => {
759
759
  styles.toolbarBtnActive,
760
760
  ]}
761
761
  onPress={filtersAccordion.toggleOpen}
762
- hitSlop={10}>
762
+ hitSlop={6}>
763
763
  <FilterIcon
764
764
  color={
765
765
  filtersAccordion.isOpen
766
766
  ? AppColors.purple
767
767
  : AppColors.grayTextStrong
768
768
  }
769
- size={18}
769
+ size={16}
770
770
  />
771
+ {(statusFilters.size > 0 ||
772
+ methodFilters.size > 0) && (
773
+ <View style={styles.activeFilterDot} />
774
+ )}
771
775
  </TouchableScale>
772
776
  </View>
773
777
  </View>
774
778
 
779
+ {/* Quick Search Scope & Filter Tags Row */}
780
+ <View style={{paddingBottom: 4}}>
781
+ <ScrollView
782
+ horizontal
783
+ showsHorizontalScrollIndicator={false}
784
+ contentContainerStyle={{
785
+ paddingHorizontal: 12,
786
+ flexDirection: 'row',
787
+ alignItems: 'center',
788
+ gap: 5,
789
+ paddingVertical: 2,
790
+ }}>
791
+ {/* Search Scopes */}
792
+ {SEARCH_SCOPES.map(s => {
793
+ const isActive = searchScope === s.id;
794
+ return (
795
+ <TouchableScale
796
+ key={`scope_${s.id}`}
797
+ onPress={() => setSearchScope(s.id)}
798
+ hitSlop={6}
799
+ style={{
800
+ flexDirection: 'row',
801
+ alignItems: 'center',
802
+ gap: 3.5,
803
+ paddingHorizontal: 7.5,
804
+ paddingVertical: 3.5,
805
+ borderRadius: 6,
806
+ backgroundColor: isActive
807
+ ? AppColors.purple
808
+ : AppColors.grayBackground,
809
+ borderWidth: 1,
810
+ borderColor: isActive
811
+ ? AppColors.purple
812
+ : AppColors.grayBorderSecondary,
813
+ }}>
814
+ {s.icon(isActive ? AppColors.white : AppColors.grayTextWeak)}
815
+ <Text
816
+ style={{
817
+ fontFamily: AppFonts.interBold,
818
+ fontSize: 10,
819
+ color: isActive ? AppColors.white : AppColors.grayText,
820
+ }}>
821
+ {s.label}
822
+ </Text>
823
+ </TouchableScale>
824
+ );
825
+ })}
826
+
827
+ <View
828
+ style={{
829
+ width: 1,
830
+ height: 14,
831
+ backgroundColor: AppColors.dividerColor,
832
+ marginHorizontal: 3,
833
+ }}
834
+ />
835
+
836
+ {/* Quick Query Suggestions */}
837
+ {QUICK_API_QUERY_TAGS.map(q => {
838
+ const isActive = search.includes(q.query);
839
+ return (
840
+ <TouchableScale
841
+ key={`quick_${q.query}`}
842
+ onPress={() => {
843
+ setSearch(prev => {
844
+ if (prev.includes(q.query)) {
845
+ return prev
846
+ .replace(q.query, '')
847
+ .replace(/\s+/g, ' ')
848
+ .trim();
849
+ }
850
+ return `${prev} ${q.query}`.trim();
851
+ });
852
+ }}
853
+ hitSlop={6}
854
+ style={{
855
+ flexDirection: 'row',
856
+ alignItems: 'center',
857
+ gap: 3.5,
858
+ paddingHorizontal: 7.5,
859
+ paddingVertical: 3.5,
860
+ borderRadius: 6,
861
+ backgroundColor: isActive
862
+ ? `${AppColors.purple}20`
863
+ : AppColors.grayBackground,
864
+ borderWidth: 1,
865
+ borderColor: isActive
866
+ ? AppColors.purple
867
+ : AppColors.grayBorderSecondary,
868
+ }}>
869
+ {q.icon(isActive ? AppColors.purple : AppColors.grayTextWeak)}
870
+ <Text
871
+ style={{
872
+ fontFamily: AppFonts.interMedium,
873
+ fontSize: 10,
874
+ color: isActive
875
+ ? AppColors.purple
876
+ : AppColors.grayText,
877
+ }}>
878
+ {q.label}
879
+ </Text>
880
+ </TouchableScale>
881
+ );
882
+ })}
883
+ </ScrollView>
884
+ </View>
885
+
775
886
  {/* Quick Filter Horizontal Chips Bar with Live Counts */}
776
887
  <View style={{marginBottom: 8}}>
777
888
  <ScrollView
@@ -1138,8 +1249,22 @@ const NetworkTab = React.memo(() => {
1138
1249
  ListEmptyComponent={
1139
1250
  <EmptyState
1140
1251
  isSearch={
1141
- search.length > 0 || statusFilters.size > 0
1252
+ search.length > 0 ||
1253
+ statusFilters.size > 0 ||
1254
+ methodFilters.size > 0
1255
+ }
1256
+ searchQuery={search}
1257
+ customTitle={
1258
+ search.length > 0
1259
+ ? 'No matching API requests'
1260
+ : 'No network activity'
1142
1261
  }
1262
+ onClearSearch={() => {
1263
+ setSearch('');
1264
+ setStatusFilters(new Set());
1265
+ setMethodFilters(new Set());
1266
+ setSearchScope('all');
1267
+ }}
1143
1268
  />
1144
1269
  }
1145
1270
  ListFooterComponent={
@@ -548,14 +548,32 @@ const ReduxTab = React.memo(() => {
548
548
  autoCapitalize="none"
549
549
  />
550
550
  {reduxSearch.length > 0 && (
551
- <Pressable
552
- accessible={true}
553
- accessibilityRole="button"
554
- accessibilityLabel="Clear search input"
555
- onPress={() => setReduxSearch('')}
556
- hitSlop={10}>
557
- <ClearIcon color={AppColors.grayTextWeak} size={14} />
558
- </Pressable>
551
+ <View style={{flexDirection: 'row', alignItems: 'center', gap: 4}}>
552
+ <View
553
+ style={{
554
+ backgroundColor: `${AppColors.purple}20`,
555
+ borderRadius: 10,
556
+ paddingHorizontal: 6,
557
+ paddingVertical: 1.5,
558
+ }}>
559
+ <Text
560
+ style={{
561
+ color: AppColors.purple,
562
+ fontSize: 9.5,
563
+ fontFamily: AppFonts.interBold,
564
+ }}>
565
+ {filteredSlices.length}
566
+ </Text>
567
+ </View>
568
+ <Pressable
569
+ accessible={true}
570
+ accessibilityRole="button"
571
+ accessibilityLabel="Clear search input"
572
+ onPress={() => setReduxSearch('')}
573
+ hitSlop={10}>
574
+ <ClearIcon color={AppColors.grayTextWeak} size={14} />
575
+ </Pressable>
576
+ </View>
559
577
  )}
560
578
  </View>
561
579
 
@@ -207,8 +207,8 @@ const SettingsPanel = () => {
207
207
  bundle: Boolean(tabVisibility?.bundle),
208
208
  performance: Boolean(tabVisibility?.performance),
209
209
  crash: Boolean(tabVisibility?.crash),
210
- device: Boolean(tabVisibility?.device ?? true),
211
- storage: Boolean(tabVisibility?.storage ?? true),
210
+ device: Boolean(tabVisibility?.device),
211
+ storage: Boolean(tabVisibility?.storage),
212
212
  }));
213
213
 
214
214
  // Synchronize staged state with tabVisibility when tabVisibility updates
@@ -221,8 +221,8 @@ const SettingsPanel = () => {
221
221
  bundle: Boolean(tabVisibility?.bundle),
222
222
  performance: Boolean(tabVisibility?.performance),
223
223
  crash: Boolean(tabVisibility?.crash),
224
- device: Boolean(tabVisibility?.device ?? true),
225
- storage: Boolean(tabVisibility?.storage ?? true),
224
+ device: Boolean(tabVisibility?.device),
225
+ storage: Boolean(tabVisibility?.storage),
226
226
  });
227
227
  }, [tabVisibility]);
228
228