react-native-inapp-inspector 2.2.9 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) 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/NetworkDetail.js +3 -2
  12. package/dist/commonjs/components/Inspector/NetworkTab.js +103 -4
  13. package/dist/commonjs/components/Inspector/ReduxTab.js +19 -3
  14. package/dist/commonjs/components/Inspector/SettingsPanel.js +4 -4
  15. package/dist/commonjs/components/Inspector/StorageTab.js +19 -3
  16. package/dist/commonjs/components/JsonViewer.js +2 -0
  17. package/dist/commonjs/components/MetaAccordion.js +15 -3
  18. package/dist/commonjs/constants/version.d.ts +1 -1
  19. package/dist/commonjs/constants/version.js +1 -1
  20. package/dist/commonjs/customHooks/networkLogger.d.ts +3 -0
  21. package/dist/commonjs/customHooks/networkLogger.js +10 -1
  22. package/dist/commonjs/helpers/index.d.ts +6 -0
  23. package/dist/commonjs/helpers/index.js +196 -6
  24. package/dist/commonjs/helpers/searchQueryParser.js +62 -20
  25. package/dist/commonjs/i18n/index.d.ts +13 -0
  26. package/dist/commonjs/i18n/index.js +35 -2
  27. package/dist/commonjs/index.d.ts +3 -0
  28. package/dist/commonjs/index.js +80 -11
  29. package/dist/commonjs/styles/AppColors.d.ts +4 -0
  30. package/dist/commonjs/styles/AppColors.js +11 -1
  31. package/dist/commonjs/styles/AppFonts.d.ts +11 -0
  32. package/dist/commonjs/styles/AppFonts.js +11 -1
  33. package/dist/commonjs/types/interfaces.d.ts +1 -0
  34. package/dist/esm/components/AppHeaderLogo.js +45 -12
  35. package/dist/esm/components/BrandSquareIcon.js +92 -49
  36. package/dist/esm/components/EmptyState.d.ts +7 -2
  37. package/dist/esm/components/EmptyState.js +11 -5
  38. package/dist/esm/components/Inspector/AnalyticsTab.js +30 -19
  39. package/dist/esm/components/Inspector/ConsoleTab.js +6 -1
  40. package/dist/esm/components/Inspector/CrashDetail.js +43 -9
  41. package/dist/esm/components/Inspector/CrashTab.js +34 -3
  42. package/dist/esm/components/Inspector/DeviceInfoTab.js +80 -16
  43. package/dist/esm/components/Inspector/InspectorHeader.js +17 -4
  44. package/dist/esm/components/Inspector/NetworkDetail.js +3 -2
  45. package/dist/esm/components/Inspector/NetworkTab.js +103 -4
  46. package/dist/esm/components/Inspector/ReduxTab.js +19 -3
  47. package/dist/esm/components/Inspector/SettingsPanel.js +4 -4
  48. package/dist/esm/components/Inspector/StorageTab.js +19 -3
  49. package/dist/esm/components/JsonViewer.js +2 -0
  50. package/dist/esm/components/MetaAccordion.js +15 -3
  51. package/dist/esm/constants/version.d.ts +1 -1
  52. package/dist/esm/constants/version.js +1 -1
  53. package/dist/esm/customHooks/networkLogger.d.ts +3 -0
  54. package/dist/esm/customHooks/networkLogger.js +8 -0
  55. package/dist/esm/helpers/index.d.ts +6 -0
  56. package/dist/esm/helpers/index.js +193 -5
  57. package/dist/esm/helpers/searchQueryParser.js +62 -20
  58. package/dist/esm/i18n/index.d.ts +13 -0
  59. package/dist/esm/i18n/index.js +33 -3
  60. package/dist/esm/index.d.ts +3 -0
  61. package/dist/esm/index.js +68 -12
  62. package/dist/esm/styles/AppColors.d.ts +4 -0
  63. package/dist/esm/styles/AppColors.js +9 -0
  64. package/dist/esm/styles/AppFonts.d.ts +11 -0
  65. package/dist/esm/styles/AppFonts.js +9 -0
  66. package/dist/esm/types/interfaces.d.ts +1 -0
  67. package/package.json +1 -1
  68. package/src/components/AppHeaderLogo.tsx +46 -12
  69. package/src/components/BrandSquareIcon.tsx +127 -78
  70. package/src/components/EmptyState.tsx +27 -5
  71. package/src/components/Inspector/AnalyticsTab.tsx +48 -27
  72. package/src/components/Inspector/ConsoleTab.tsx +10 -0
  73. package/src/components/Inspector/CrashDetail.tsx +65 -9
  74. package/src/components/Inspector/CrashTab.tsx +41 -3
  75. package/src/components/Inspector/DeviceInfoTab.tsx +87 -16
  76. package/src/components/Inspector/InspectorHeader.tsx +24 -4
  77. package/src/components/Inspector/NetworkDetail.tsx +4 -4
  78. package/src/components/Inspector/NetworkTab.tsx +129 -4
  79. package/src/components/Inspector/ReduxTab.tsx +26 -8
  80. package/src/components/Inspector/SettingsPanel.tsx +4 -4
  81. package/src/components/Inspector/StorageTab.tsx +21 -3
  82. package/src/components/JsonViewer.tsx +2 -0
  83. package/src/components/MetaAccordion.tsx +18 -3
  84. package/src/constants/version.ts +1 -1
  85. package/src/customHooks/networkLogger.ts +15 -0
  86. package/src/helpers/index.ts +213 -5
  87. package/src/helpers/searchQueryParser.ts +63 -20
  88. package/src/i18n/index.ts +38 -3
  89. package/src/index.tsx +89 -11
  90. package/src/styles/AppColors.ts +9 -0
  91. package/src/styles/AppFonts.ts +16 -0
  92. package/src/types/interfaces.ts +1 -0
package/dist/esm/index.js CHANGED
@@ -8,12 +8,12 @@ import ErrorBoundary from './components/ErrorBoundary';
8
8
  import MainScreen from './components/Inspector/MainScreen';
9
9
  import { InspectorContext, animateNextLayout, } from './components/Inspector/InspectorContext';
10
10
  // Helpers
11
- import { formatDisplayUrl, getNavigationInfo, deduplicateLogs, getDomainColor, getEventCategory, matchNetworkLogQuery, setupMemoryWarningHandler, } from './helpers';
11
+ import { formatDisplayUrl, getNavigationInfo, getLogPageName, deduplicateLogs, getDomainColor, getEventCategory, matchNetworkLogQuery, setupMemoryWarningHandler, } from './helpers';
12
12
  // #5 — settings persistence
13
13
  import { loadSettings, saveSettings, setCustomStorage, clearPersistedSettings, calculateRamBasedLimits, } from './helpers/settingsStore';
14
14
  import { getNativeSystemMetrics, pushNativeLogRecord, fetchNativeCachedPage, } from './native/NativeInspector';
15
15
  // Network
16
- import { setupNetworkLogger, clearNetworkLogs, subscribeNetworkLogs, setNetworkModuleEnabled, setMaxNetworkLogsLimit, } from './customHooks/networkLogger';
16
+ import { setupNetworkLogger, clearNetworkLogs, subscribeNetworkLogs, setNetworkModuleEnabled, setMaxNetworkLogsLimit, setRouteInfoProvider, } from './customHooks/networkLogger';
17
17
  // Console
18
18
  import { setupConsoleLogger, clearConsoleLogs, subscribeConsoleLogs, setConsoleModuleEnabled, } from './customHooks/consoleLogger';
19
19
  import { IGNORED_LOG_PREFIXES } from './customHooks/logFilters';
@@ -183,8 +183,8 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
183
183
  bundle: false,
184
184
  performance: false,
185
185
  crash: false,
186
- device: true,
187
- storage: true,
186
+ device: false,
187
+ storage: false,
188
188
  });
189
189
  const [maxNetworkLogs, setMaxNetworkLogs] = useState(100);
190
190
  const [maxAnalyticsEventsLimit, setMaxAnalyticsEventsLimit] = useState(75);
@@ -239,8 +239,8 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
239
239
  bundle: false,
240
240
  performance: false,
241
241
  crash: false,
242
- device: true,
243
- storage: true,
242
+ device: false,
243
+ storage: false,
244
244
  });
245
245
  setDefaultTab('apis');
246
246
  setIsAutoRamLimitEnabled(true);
@@ -314,6 +314,8 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
314
314
  bundle: false,
315
315
  performance: false,
316
316
  crash: false,
317
+ device: false,
318
+ storage: false,
317
319
  },
318
320
  ...(saved.tabVisibility || {}),
319
321
  apis: true,
@@ -539,12 +541,21 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
539
541
  const navigationContext = React.useContext(NavigationContext);
540
542
  const hasNavigationContext = navigationContext !== undefined;
541
543
  const currentRouteRef = useRef({
542
- path: 'Navigators',
544
+ path: '',
543
545
  params: null,
544
546
  });
547
+ useEffect(() => {
548
+ setRouteInfoProvider(() => currentRouteRef.current);
549
+ return () => {
550
+ setRouteInfoProvider(null);
551
+ };
552
+ }, []);
545
553
  useEffect(() => {
546
554
  if (navState) {
547
- currentRouteRef.current = getNavigationInfo(navState);
555
+ const info = getNavigationInfo(navState);
556
+ if (info?.path) {
557
+ currentRouteRef.current = info;
558
+ }
548
559
  }
549
560
  }, [navState]);
550
561
  useEffect(() => {
@@ -553,9 +564,22 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
553
564
  const updateState = () => {
554
565
  try {
555
566
  if (typeof navigationRef.isReady === 'function' && navigationRef.isReady()) {
567
+ const currentRoute = typeof navigationRef.getCurrentRoute === 'function'
568
+ ? navigationRef.getCurrentRoute()
569
+ : null;
570
+ if (currentRoute?.name) {
571
+ currentRouteRef.current = {
572
+ path: currentRoute.name,
573
+ params: currentRoute.params || null,
574
+ };
575
+ }
556
576
  const state = navigationRef.getRootState();
557
577
  if (state) {
558
578
  setNavState(state);
579
+ const routeInfo = getNavigationInfo(state);
580
+ if (routeInfo?.path) {
581
+ currentRouteRef.current = routeInfo;
582
+ }
559
583
  }
560
584
  }
561
585
  }
@@ -867,7 +891,9 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
867
891
  if (freshIds.size > 0) {
868
892
  freshIds.forEach(id => {
869
893
  if (!logRouteMapRef.current.has(id)) {
870
- logRouteMapRef.current.set(id, currentRouteRef.current);
894
+ const matchedLog = deduped.find(l => l.id === id);
895
+ const resolvedRoute = matchedLog?.routeInfo || currentRouteRef.current;
896
+ logRouteMapRef.current.set(id, resolvedRoute);
871
897
  }
872
898
  });
873
899
  setNewLogIds(freshIds);
@@ -880,6 +906,12 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
880
906
  const deduped = deduplicateLogs(raw);
881
907
  const incoming = new Set(deduped.map(l => l.id));
882
908
  prevLogIdsRef.current = incoming;
909
+ deduped.forEach(l => {
910
+ if (!logRouteMapRef.current.has(l.id)) {
911
+ const resolvedRoute = l?.routeInfo || currentRouteRef.current;
912
+ logRouteMapRef.current.set(l.id, resolvedRoute);
913
+ }
914
+ });
883
915
  setLogs(deduped);
884
916
  }
885
917
  else {
@@ -1151,8 +1183,8 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
1151
1183
  const groups = [];
1152
1184
  for (let i = 0; i < filteredLogs.length; i++) {
1153
1185
  const log = filteredLogs[i];
1154
- const routeInfo = logRouteMapRef.current.get(log.id);
1155
- const pageName = routeInfo?.path || 'Navigators';
1186
+ const routeInfo = logRouteMapRef.current.get(log.id) || log?.routeInfo;
1187
+ const pageName = getLogPageName(log, routeInfo);
1156
1188
  if (groups.length === 0 ||
1157
1189
  groups[groups.length - 1].pageName !== pageName) {
1158
1190
  groups.push({ pageName, color: getDomainColor(pageName), logs: [] });
@@ -1405,7 +1437,28 @@ const NetworkInspector = ({ enabled = true, isEnabled = true, storage, navigatio
1405
1437
  log.type || '',
1406
1438
  log.sourceMethod || '',
1407
1439
  ].join(' ').toLowerCase();
1408
- return queryTokens.every(token => searchTarget.includes(token));
1440
+ return queryTokens.every(token => {
1441
+ if (token.startsWith('level:') || token.startsWith('is:') || token.startsWith('type:')) {
1442
+ const flag = token.replace(/^(level:|is:|type:)/, '').trim();
1443
+ if (flag === 'error' || flag === 'err')
1444
+ return log.type === 'error';
1445
+ if (flag === 'warn' || flag === 'warning')
1446
+ return log.type === 'warn';
1447
+ if (flag === 'info')
1448
+ return log.type === 'info';
1449
+ if (flag === 'log')
1450
+ return log.sourceMethod === 'log';
1451
+ if (flag === 'analytics')
1452
+ return (log.message || '').toLowerCase().includes('[analytics');
1453
+ }
1454
+ if (token === 'error' || token === 'err') {
1455
+ return log.type === 'error' || searchTarget.includes(token);
1456
+ }
1457
+ if (token === 'warn' || token === 'warning') {
1458
+ return log.type === 'warn' || searchTarget.includes(token);
1459
+ }
1460
+ return searchTarget.includes(token);
1461
+ });
1409
1462
  });
1410
1463
  }
1411
1464
  // #7 — apply sort order (newest/oldest first)
@@ -1824,3 +1877,6 @@ export { setMaxPerformanceEventsLimit, getMaxPerformanceEventsLimit, prunePerfor
1824
1877
  export { BrandSquareIcon, BrandCircleIcon, } from './components/NetworkIcons';
1825
1878
  export { connectAsyncStorage, connectMMKV, isAsyncStorageConnected, isMMKVConnected, getRegisteredMMKVInstanceIds, fetchStorageEntries, setStorageEntry, removeStorageEntry, clearStorageDriver, subscribeToStorageChanges, } from './customHooks/storageInspector';
1826
1879
  export { ActiveTab, Method, StatusFilter, SortOrder, LocalFilter, ModalAnimationType, SettingsPage, SettingsSubTab, LogFilter, ConsoleLogType, AnalyticsEventSource, GAEventCategory, StackFrameType, DiffResultType, BundleSubTab, PerformanceSubTab, CrashType, CrashExportFormat, CrashDetailSubTab, CrashFilterType, BreadcrumbType, } from './types';
1880
+ export { AppFonts, setAppFonts, } from './styles/AppFonts';
1881
+ export { AppColors, setAppColors, getThemeColors, updateAppColorsTheme, } from './styles/AppColors';
1882
+ export { t, i18n, useTranslation, setLanguage, addTranslations, setTranslations, I18nextProvider, } from './i18n';
@@ -344,4 +344,8 @@ export declare const getThemeColors: (isDark: boolean) => {
344
344
  };
345
345
  export declare const AppColors: typeof LightColors;
346
346
  export declare const updateAppColorsTheme: (isDark: boolean) => void;
347
+ /**
348
+ * Configure or override specific color tokens used throughout the inspector.
349
+ */
350
+ export declare const setAppColors: (customColors: Partial<typeof LightColors>) => void;
347
351
  export default AppColors;
@@ -384,4 +384,13 @@ Object.assign(AppColors, LightColors);
384
384
  export const updateAppColorsTheme = (isDark) => {
385
385
  Object.assign(AppColors, getThemeColors(isDark));
386
386
  };
387
+ /**
388
+ * Configure or override specific color tokens used throughout the inspector.
389
+ */
390
+ export const setAppColors = (customColors) => {
391
+ if (!customColors || typeof customColors !== 'object')
392
+ return;
393
+ Object.assign(AppColors, customColors);
394
+ Object.assign(LightColors, customColors);
395
+ };
387
396
  export default AppColors;
@@ -1,7 +1,18 @@
1
+ export interface AppFontConfig {
2
+ interRegular?: string;
3
+ interMedium?: string;
4
+ interBold?: string;
5
+ Sfprotext?: string;
6
+ }
1
7
  export declare const AppFonts: {
2
8
  interRegular: string;
3
9
  interMedium: string;
4
10
  interBold: string;
5
11
  Sfprotext: string;
6
12
  };
13
+ /**
14
+ * Configure or override font families used across all inspector screens.
15
+ * Useful when integrating into apps with custom font setups or system fonts.
16
+ */
17
+ export declare const setAppFonts: (customFonts: Partial<typeof AppFonts>) => void;
7
18
  export default AppFonts;
@@ -4,4 +4,13 @@ export const AppFonts = {
4
4
  interBold: 'Inter-Bold',
5
5
  Sfprotext: 'Inter-Regular',
6
6
  };
7
+ /**
8
+ * Configure or override font families used across all inspector screens.
9
+ * Useful when integrating into apps with custom font setups or system fonts.
10
+ */
11
+ export const setAppFonts = (customFonts) => {
12
+ if (!customFonts || typeof customFonts !== 'object')
13
+ return;
14
+ Object.assign(AppFonts, customFonts);
15
+ };
7
16
  export default AppFonts;
@@ -106,6 +106,7 @@ export interface NetworkLog {
106
106
  /** Client identifier: axios, fetch, xhr, apollo, etc. */
107
107
  client?: 'axios' | 'fetch' | 'xhr' | 'apollo' | 'graphql' | string;
108
108
  caller?: string;
109
+ routeInfo?: RouteInfo;
109
110
  }
110
111
  export interface RouteInfo {
111
112
  path: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-inapp-inspector",
3
- "version": "2.2.9",
3
+ "version": "2.3.0",
4
4
  "description": "The zero-config, all-in-one in-app debugger for React Native & Expo. Inspect Network (fetch/axios), Console logs, Stack Traces, Redux State, Firebase Analytics, and JS Bundle size directly on device.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -34,8 +34,12 @@ export const AppHeaderLogo: React.FC<AppHeaderLogoProps> = ({
34
34
  return (
35
35
  <View
36
36
  style={[
37
- logoStyles.container,
38
- {width: size, height: size, padding: 0, borderRadius: Math.round(size * 0.28)},
37
+ logoStyles.svgContainer,
38
+ {
39
+ width: size,
40
+ height: size,
41
+ borderRadius: Math.round(size * 0.23),
42
+ },
39
43
  ]}>
40
44
  {clonedIcon}
41
45
  </View>
@@ -44,8 +48,12 @@ export const AppHeaderLogo: React.FC<AppHeaderLogoProps> = ({
44
48
  return (
45
49
  <View
46
50
  style={[
47
- logoStyles.container,
48
- {width: size, height: size, borderRadius: Math.round(size * 0.28)},
51
+ logoStyles.imageContainer,
52
+ {
53
+ width: size,
54
+ height: size,
55
+ borderRadius: Math.round(size * 0.23),
56
+ },
49
57
  ]}>
50
58
  <Image
51
59
  source={customIcon as ImageSourcePropType}
@@ -65,7 +73,15 @@ export const AppHeaderLogo: React.FC<AppHeaderLogoProps> = ({
65
73
  });
66
74
 
67
75
  return (
68
- <View style={[logoStyles.container, {width: size, height: size}]}>
76
+ <View
77
+ style={[
78
+ logoStyles.imageContainer,
79
+ {
80
+ width: size,
81
+ height: size,
82
+ borderRadius: Math.round(size * 0.23),
83
+ },
84
+ ]}>
69
85
  <Image
70
86
  source={nativeSource}
71
87
  style={logoStyles.image}
@@ -80,8 +96,12 @@ export const AppHeaderLogo: React.FC<AppHeaderLogoProps> = ({
80
96
  return (
81
97
  <View
82
98
  style={[
83
- logoStyles.container,
84
- {width: size, height: size, padding: 0, borderRadius: Math.round(size * 0.28)},
99
+ logoStyles.svgContainer,
100
+ {
101
+ width: size,
102
+ height: size,
103
+ borderRadius: Math.round(size * 0.23),
104
+ },
85
105
  ]}>
86
106
  <BrandSquareIcon size={size} />
87
107
  </View>
@@ -89,20 +109,34 @@ export const AppHeaderLogo: React.FC<AppHeaderLogoProps> = ({
89
109
  };
90
110
 
91
111
  const logoStyles = StyleSheet.create({
92
- container: {
93
- borderRadius: 10,
112
+ svgContainer: {
113
+ overflow: 'hidden',
114
+ alignItems: 'center',
115
+ justifyContent: 'center',
116
+ shadowColor: '#000',
117
+ shadowOffset: {width: 0, height: 2},
118
+ shadowOpacity: 0.25,
119
+ shadowRadius: 3,
120
+ elevation: 3,
121
+ },
122
+ imageContainer: {
94
123
  overflow: 'hidden',
95
124
  backgroundColor: 'rgba(255, 255, 255, 0.15)',
96
125
  borderWidth: 1,
97
- borderColor: 'rgba(255, 255, 255, 0.28)',
98
- padding: 2.5,
126
+ borderColor: 'rgba(255, 255, 255, 0.35)',
127
+ padding: 2,
99
128
  alignItems: 'center',
100
129
  justifyContent: 'center',
130
+ shadowColor: '#000',
131
+ shadowOffset: {width: 0, height: 2},
132
+ shadowOpacity: 0.25,
133
+ shadowRadius: 3,
134
+ elevation: 3,
101
135
  },
102
136
  image: {
103
137
  width: '100%',
104
138
  height: '100%',
105
- borderRadius: 7.5,
139
+ borderRadius: 8,
106
140
  },
107
141
  });
108
142
 
@@ -8,7 +8,6 @@ import Svg, {
8
8
  RadialGradient,
9
9
  Stop,
10
10
  Rect,
11
- Ellipse,
12
11
  Line,
13
12
  } from 'react-native-svg';
14
13
 
@@ -16,117 +15,167 @@ export const BrandSquareIcon = ({size = 56}: {size?: number}) => {
16
15
  return (
17
16
  <Svg width={size} height={size} viewBox="0 0 256 256" fill="none">
18
17
  <Defs>
18
+ {/* App Tile Background - Crisp Royal Deep Violet to Midnight */}
19
19
  <LinearGradient id="bs_tile" x1="0" y1="0" x2="256" y2="256" gradientUnits="userSpaceOnUse">
20
- <Stop offset="0" stopColor="#1E1B4B" />
21
- <Stop offset="1" stopColor="#0F172A" />
20
+ <Stop offset="0%" stopColor="#1E1B4B" />
21
+ <Stop offset="50%" stopColor="#18182F" />
22
+ <Stop offset="100%" stopColor="#0B0F19" />
22
23
  </LinearGradient>
23
- <LinearGradient id="bs_beam" x1="0" y1="0" x2="256" y2="256" gradientUnits="userSpaceOnUse">
24
- <Stop offset="0" stopColor="#818CF8" />
25
- <Stop offset="0.5" stopColor="#A855F7" />
26
- <Stop offset="1" stopColor="#EC4899" />
24
+
25
+ {/* Outer Neon Cyber Border */}
26
+ <LinearGradient id="bs_border" x1="0" y1="0" x2="256" y2="256" gradientUnits="userSpaceOnUse">
27
+ <Stop offset="0%" stopColor="#818CF8" stopOpacity={0.9} />
28
+ <Stop offset="45%" stopColor="#C084FC" stopOpacity={0.8} />
29
+ <Stop offset="80%" stopColor="#F472B6" stopOpacity={0.85} />
30
+ <Stop offset="100%" stopColor="#38BDF8" stopOpacity={0.9} />
27
31
  </LinearGradient>
28
- <LinearGradient id="bs_body" x1="0" y1="0" x2="0" y2="256" gradientUnits="userSpaceOnUse">
29
- <Stop offset="0" stopColor="#312E81" />
30
- <Stop offset="1" stopColor="#1E1B4B" />
32
+
33
+ {/* Ambient Top Glow */}
34
+ <RadialGradient id="bs_top_glow" cx="50%" cy="15%" r="65%">
35
+ <Stop offset="0%" stopColor="#818CF8" stopOpacity={0.35} />
36
+ <Stop offset="60%" stopColor="#A855F7" stopOpacity={0.12} />
37
+ <Stop offset="100%" stopColor="#1E1B4B" stopOpacity={0} />
38
+ </RadialGradient>
39
+
40
+ {/* Owl Outer Body Gradient - High Contrast */}
41
+ <LinearGradient id="bs_body" x1="64" y1="40" x2="192" y2="220" gradientUnits="userSpaceOnUse">
42
+ <Stop offset="0%" stopColor="#3730A3" />
43
+ <Stop offset="50%" stopColor="#2E1065" />
44
+ <Stop offset="100%" stopColor="#17123A" />
31
45
  </LinearGradient>
32
- <RadialGradient id="bs_iris" cx="40%" cy="40%" r="60%">
33
- <Stop offset="0" stopColor="#FDE047" />
34
- <Stop offset="0.7" stopColor="#F59E0B" />
35
- <Stop offset="1" stopColor="#D97706" />
46
+
47
+ {/* Neon Cyber Beam Stroke */}
48
+ <LinearGradient id="bs_beam" x1="40" y1="40" x2="220" y2="220" gradientUnits="userSpaceOnUse">
49
+ <Stop offset="0%" stopColor="#67E8F9" />
50
+ <Stop offset="35%" stopColor="#818CF8" />
51
+ <Stop offset="70%" stopColor="#C084FC" />
52
+ <Stop offset="100%" stopColor="#F472B6" />
53
+ </LinearGradient>
54
+
55
+ {/* Eyes Radiant Iris */}
56
+ <RadialGradient id="bs_iris" cx="38%" cy="36%" r="65%">
57
+ <Stop offset="0%" stopColor="#FEF08A" />
58
+ <Stop offset="40%" stopColor="#FBBF24" />
59
+ <Stop offset="80%" stopColor="#F59E0B" />
60
+ <Stop offset="100%" stopColor="#D97706" />
36
61
  </RadialGradient>
37
- <LinearGradient id="bs_beak" x1="0" y1="0" x2="0" y2="20" gradientUnits="userSpaceOnUse">
38
- <Stop offset="0" stopColor="#FBBF24" />
39
- <Stop offset="1" stopColor="#F97316" />
62
+
63
+ {/* Beak Gradient */}
64
+ <LinearGradient id="bs_beak" x1="124" y1="120" x2="132" y2="136" gradientUnits="userSpaceOnUse">
65
+ <Stop offset="0%" stopColor="#FDE047" />
66
+ <Stop offset="100%" stopColor="#EA580C" />
40
67
  </LinearGradient>
68
+
69
+ {/* Diagnostic Lens Glass Flare */}
70
+ <RadialGradient id="bs_lens" cx="40%" cy="35%" r="65%">
71
+ <Stop offset="0%" stopColor="#A5F3FC" stopOpacity={0.4} />
72
+ <Stop offset="50%" stopColor="#38BDF8" stopOpacity={0.18} />
73
+ <Stop offset="100%" stopColor="#0284C7" stopOpacity={0.05} />
74
+ </RadialGradient>
41
75
  </Defs>
42
76
 
43
- {/* Rounded App Tile Background */}
44
- <Rect x="8" y="8" width="240" height="240" rx="54" fill="url(#bs_tile)" />
77
+ {/* ── 1. Rounded App Tile Base (Full Edge-to-Edge) ── */}
78
+ <Rect x="2" y="2" width="252" height="252" rx="58" fill="url(#bs_tile)" />
79
+ <Rect x="2" y="2" width="252" height="252" rx="58" fill="url(#bs_top_glow)" />
45
80
  <Rect
46
- x="8"
47
- y="8"
48
- width="240"
49
- height="240"
50
- rx="54"
81
+ x="2"
82
+ y="2"
83
+ width="252"
84
+ height="252"
85
+ rx="58"
51
86
  fill="none"
52
- stroke="url(#bs_beam)"
53
- strokeWidth="3"
54
- strokeOpacity={0.4}
87
+ stroke="url(#bs_border)"
88
+ strokeWidth="3.5"
55
89
  />
56
90
 
57
- {/* Owl Outer Body Contour */}
91
+ {/* ── 2. Owl Outer Wings (Layered behind) ── */}
58
92
  <Path
59
- d="M64 148 C60 102 72 68 92 56 L100 40 L118 60 Q128 55 138 60 L156 40 L164 56 C184 68 196 102 192 148 C196 178 182 202 150 210 C138 214 118 214 106 210 C74 202 60 178 64 148 Z"
60
- fill="url(#bs_body)"
93
+ d="M62 120 C42 152 46 194 76 210 C68 178 66 146 80 120 Z"
94
+ fill="#1E1B4B"
61
95
  stroke="url(#bs_beam)"
62
- strokeWidth="4"
63
- strokeLinejoin="round"
96
+ strokeWidth="3"
97
+ strokeOpacity={0.75}
64
98
  />
65
-
66
- {/* Wings */}
67
99
  <Path
68
- d="M74 124 C58 154 60 190 86 204 C79 176 77 148 88 126 Z"
100
+ d="M194 120 C214 152 210 194 180 210 C188 178 190 146 176 120 Z"
69
101
  fill="#1E1B4B"
70
- stroke="#818CF8"
71
- strokeWidth="2.5"
72
- strokeOpacity={0.6}
102
+ stroke="url(#bs_beam)"
103
+ strokeWidth="3"
104
+ strokeOpacity={0.75}
73
105
  />
106
+
107
+ {/* ── 3. Owl Body Contour ── */}
74
108
  <Path
75
- d="M182 124 C198 154 196 190 170 204 C177 176 179 148 168 126 Z"
76
- fill="#1E1B4B"
77
- stroke="#818CF8"
78
- strokeWidth="2.5"
79
- strokeOpacity={0.6}
109
+ d="M58 146 C54 98 68 64 88 52 L98 34 L118 56 Q128 50 138 56 L158 34 L168 52 C188 64 202 98 198 146 C202 180 186 208 152 216 C140 220 116 220 104 216 C70 208 54 180 58 146 Z"
110
+ fill="url(#bs_body)"
111
+ stroke="url(#bs_beam)"
112
+ strokeWidth="4.5"
113
+ strokeLinejoin="round"
80
114
  />
81
115
 
82
- {/* Belly Screen Plate */}
116
+ {/* Inner Ear Highlights */}
117
+ <Path d="M98 42 L112 56 L102 57 Z" fill="#67E8F9" opacity={0.6} />
118
+ <Path d="M158 42 L144 56 L154 57 Z" fill="#F472B6" opacity={0.6} />
119
+
120
+ {/* ── 4. Chest Terminal & Code Braces ── */}
83
121
  <Rect
84
- x="105"
85
- y="160"
86
- width="46"
87
- height="34"
88
- rx="8"
89
- fill="#0B0F19"
122
+ x="98"
123
+ y="162"
124
+ width="60"
125
+ height="38"
126
+ rx="10"
127
+ fill="#050811"
90
128
  stroke="url(#bs_beam)"
91
- strokeWidth="2"
129
+ strokeWidth="2.5"
92
130
  />
93
- {/* Code Emblem on Chest */}
94
- <G stroke="#A5B4FC" strokeWidth="2.8" strokeLinecap="round" strokeLinejoin="round" fill="none">
95
- <Path d="M120 171 L114 177 L120 183" />
96
- <Path d="M136 171 L142 177 L136 183" />
97
- <Path d="M130 169 L126 185" stroke="#EC4899" />
131
+ <G strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" fill="none">
132
+ <Path d="M116 174 L108 181 L116 188" stroke="#38BDF8" />
133
+ <Path d="M140 174 L148 181 L140 188" stroke="#38BDF8" />
134
+ <Path d="M131 171 L125 191" stroke="#F43F5E" />
98
135
  </G>
99
136
 
100
- {/* Facial Disc & Mask */}
137
+ {/* ── 5. Facial Mask Plate ── */}
101
138
  <Path
102
- d="M128 92 C148 70 186 78 186 108 C186 134 160 152 128 152 C96 152 70 134 70 108 C70 78 108 70 128 92 Z"
103
- fill="#2E1065"
104
- stroke="#818CF8"
105
- strokeWidth="2"
106
- strokeOpacity={0.6}
139
+ d="M128 88 C152 64 192 72 192 104 C192 132 162 152 128 152 C94 152 64 132 64 104 C64 72 104 64 128 88 Z"
140
+ fill="#1E1B4B"
141
+ stroke="url(#bs_beam)"
142
+ strokeWidth="2.8"
107
143
  />
108
144
 
109
- {/* Right Eye (Normal Cute Eye) */}
110
- <Circle cx="154" cy="108" r="17" fill="url(#bs_iris)" stroke="#0F172A" strokeWidth="2" />
111
- <Circle cx="154" cy="108" r="8.5" fill="#0F172A" />
112
- <Circle cx="157" cy="105" r="3" fill="#FFFFFF" />
113
- <Circle cx="151" cy="111" r="1.5" fill="#FFFFFF" opacity={0.8} />
145
+ {/* ── 6. Right Eye (Glowing Owl Eye) ── */}
146
+ <Circle cx="156" cy="106" r="19" fill="url(#bs_iris)" stroke="#0F172A" strokeWidth="2.5" />
147
+ <Circle cx="156" cy="106" r="9.5" fill="#0B0F19" />
148
+ <Circle cx="159.5" cy="102.5" r="3.5" fill="#FFFFFF" />
149
+ <Circle cx="153" cy="109.5" r="1.8" fill="#FFFFFF" opacity={0.85} />
150
+
151
+ {/* ── 7. Left Eye (Diagnostic Magnifier Lens) ── */}
152
+ {/* Magnifier Glass Glow */}
153
+ <Circle cx="94" cy="104" r="30" fill="url(#bs_lens)" stroke="url(#bs_beam)" strokeWidth="6" />
154
+ <Line x1="72" y1="126" x2="48" y2="152" stroke="#0B0F19" strokeWidth="13" strokeLinecap="round" />
155
+ <Line x1="72" y1="126" x2="48" y2="152" stroke="url(#bs_beam)" strokeWidth="8" strokeLinecap="round" />
114
156
 
115
- {/* Left Eye (Diagnostic Magnifier Lens) */}
116
- <Circle cx="96" cy="104" r="20" fill="url(#bs_iris)" stroke="#0F172A" strokeWidth="2" />
117
- <Circle cx="96" cy="104" r="10" fill="#0F172A" />
118
- <Circle cx="99.5" cy="100.5" r="3.5" fill="#FFFFFF" />
119
- <Circle cx="92" cy="107" r="1.8" fill="#FFFFFF" opacity={0.8} />
157
+ {/* Eye Inside Magnifier */}
158
+ <Circle cx="94" cy="104" r="21" fill="url(#bs_iris)" stroke="#0F172A" strokeWidth="2.5" />
159
+ <Circle cx="94" cy="104" r="10.5" fill="#0B0F19" />
160
+ <Circle cx="98" cy="100" r="4" fill="#FFFFFF" />
161
+ <Circle cx="90.5" cy="107.5" r="2" fill="#FFFFFF" opacity={0.85} />
120
162
 
121
- {/* Magnifier Glass Ring & Handle */}
122
- <Line x1="76" y1="124" x2="55" y2="148" stroke="#0F172A" strokeWidth="12" strokeLinecap="round" />
123
- <Line x1="76" y1="124" x2="55" y2="148" stroke="url(#bs_beam)" strokeWidth="7" strokeLinecap="round" />
124
- <Circle cx="96" cy="104" r="28" fill="#38BDF8" fillOpacity={0.12} stroke="url(#bs_beam)" strokeWidth="6" />
163
+ {/* Lens Reflection Highlight */}
164
+ <Path
165
+ d="M74 94 A26 26 0 0 1 106 78"
166
+ stroke="#FFFFFF"
167
+ strokeWidth="3"
168
+ strokeLinecap="round"
169
+ opacity={0.65}
170
+ fill="none"
171
+ />
125
172
 
126
- {/* Beak */}
173
+ {/* ── 8. Beak ── */}
127
174
  <Path
128
- d="M123.5 123 Q128 121 132.5 123 Q131 132 128 134.5 Q125 132 123.5 123 Z"
175
+ d="M123 120 Q128 118 133 120 Q131.5 133 128 137 Q124.5 133 123 120 Z"
129
176
  fill="url(#bs_beak)"
177
+ stroke="#78350F"
178
+ strokeWidth="1"
130
179
  />
131
180
  </Svg>
132
181
  );
@@ -12,7 +12,21 @@ import {EmptyRadarIcon} from './NetworkIcons';
12
12
  import {AppColors} from '../styles/AppColors';
13
13
  import styles from '../styles';
14
14
 
15
- const EmptyState = React.memo(function EmptyState({isSearch}: {isSearch?: boolean}) {
15
+ interface EmptyStateProps {
16
+ isSearch?: boolean;
17
+ searchQuery?: string;
18
+ customTitle?: string;
19
+ customSub?: string;
20
+ onClearSearch?: () => void;
21
+ }
22
+
23
+ const EmptyState = React.memo(function EmptyState({
24
+ isSearch,
25
+ searchQuery,
26
+ customTitle,
27
+ customSub,
28
+ onClearSearch,
29
+ }: EmptyStateProps) {
16
30
  const iconPulse = useRef(new Animated.Value(1)).current;
17
31
 
18
32
  useEffect(() => {
@@ -54,13 +68,21 @@ const EmptyState = React.memo(function EmptyState({isSearch}: {isSearch?: boolea
54
68
  <EmptyRadarIcon color={AppColors.purple} size={32} />
55
69
  </Animated.View>
56
70
  <Text style={styles.emptyTitle}>
57
- {isSearch ? 'No matching APIs' : 'No network activity'}
71
+ {customTitle || (isSearch ? 'No matching results' : 'No network activity')}
58
72
  </Text>
59
73
  <Text style={styles.emptySub}>
60
- {isSearch
61
- ? 'Try adjusting your filters or search.'
62
- : 'Listening for incoming API calls...'}
74
+ {customSub ||
75
+ (isSearch
76
+ ? searchQuery
77
+ ? `No items matched "${searchQuery}"`
78
+ : 'Try adjusting your filters or search keywords.'
79
+ : 'Listening for incoming API calls...')}
63
80
  </Text>
81
+ {isSearch && onClearSearch && (
82
+ <TouchableScale style={styles.reloadBtn} onPress={onClearSearch}>
83
+ <Text style={styles.reloadBtnText}>Clear Search & Filters</Text>
84
+ </TouchableScale>
85
+ )}
64
86
  {!isSearch && (
65
87
  <TouchableScale style={styles.reloadBtn} onPress={handleReload}>
66
88
  <Text style={styles.reloadBtnText}>Reload App</Text>