react-native-inapp-inspector 2.3.8 → 2.3.10

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 (82) hide show
  1. package/dist/commonjs/components/Inspector/AnalyticsTab.js +27 -2
  2. package/dist/commonjs/components/Inspector/BundleTab.js +3 -0
  3. package/dist/commonjs/components/Inspector/ConsoleTab.js +24 -1
  4. package/dist/commonjs/components/Inspector/CrashTab.js +30 -1
  5. package/dist/commonjs/components/Inspector/FeatureUnderDevNotice.d.ts +7 -0
  6. package/dist/commonjs/components/Inspector/FeatureUnderDevNotice.js +111 -0
  7. package/dist/commonjs/components/Inspector/InspectorHeader.js +225 -108
  8. package/dist/commonjs/components/Inspector/NetworkDetail.js +2 -1
  9. package/dist/commonjs/components/Inspector/NetworkFilterModal.d.ts +19 -0
  10. package/dist/commonjs/components/Inspector/NetworkFilterModal.js +648 -0
  11. package/dist/commonjs/components/Inspector/NetworkTab.js +89 -147
  12. package/dist/commonjs/components/Inspector/NpmUpdateToast.js +1 -1
  13. package/dist/commonjs/components/Inspector/PerformanceTab.js +3 -0
  14. package/dist/commonjs/components/Inspector/ReduxTab.js +24 -1
  15. package/dist/commonjs/components/Inspector/SettingsPanel.js +73 -88
  16. package/dist/commonjs/components/Inspector/StorageTab.js +37 -11
  17. package/dist/commonjs/components/Inspector/UpdateAvailableModal.js +2 -0
  18. package/dist/commonjs/components/LogCard.js +35 -26
  19. package/dist/commonjs/components/NetworkIcons.d.ts +1 -0
  20. package/dist/commonjs/components/NetworkIcons.js +6 -1
  21. package/dist/commonjs/constants/index.js +3 -0
  22. package/dist/commonjs/constants/version.d.ts +1 -1
  23. package/dist/commonjs/constants/version.js +1 -1
  24. package/dist/commonjs/helpers/telemetry.js +34 -0
  25. package/dist/commonjs/index.js +3 -0
  26. package/dist/commonjs/styles/index.d.ts +11 -0
  27. package/dist/commonjs/styles/index.js +20 -5
  28. package/dist/commonjs/types/enums.d.ts +3 -0
  29. package/dist/commonjs/types/enums.js +3 -0
  30. package/dist/esm/components/Inspector/AnalyticsTab.js +29 -4
  31. package/dist/esm/components/Inspector/BundleTab.js +3 -0
  32. package/dist/esm/components/Inspector/ConsoleTab.js +26 -3
  33. package/dist/esm/components/Inspector/CrashTab.js +32 -3
  34. package/dist/esm/components/Inspector/FeatureUnderDevNotice.d.ts +7 -0
  35. package/dist/esm/components/Inspector/FeatureUnderDevNotice.js +74 -0
  36. package/dist/esm/components/Inspector/InspectorHeader.js +226 -109
  37. package/dist/esm/components/Inspector/NetworkDetail.js +2 -1
  38. package/dist/esm/components/Inspector/NetworkFilterModal.d.ts +19 -0
  39. package/dist/esm/components/Inspector/NetworkFilterModal.js +607 -0
  40. package/dist/esm/components/Inspector/NetworkTab.js +91 -149
  41. package/dist/esm/components/Inspector/NpmUpdateToast.js +1 -1
  42. package/dist/esm/components/Inspector/PerformanceTab.js +3 -0
  43. package/dist/esm/components/Inspector/ReduxTab.js +26 -3
  44. package/dist/esm/components/Inspector/SettingsPanel.js +73 -88
  45. package/dist/esm/components/Inspector/StorageTab.js +39 -13
  46. package/dist/esm/components/Inspector/UpdateAvailableModal.js +2 -0
  47. package/dist/esm/components/LogCard.js +36 -27
  48. package/dist/esm/components/NetworkIcons.d.ts +1 -0
  49. package/dist/esm/components/NetworkIcons.js +4 -0
  50. package/dist/esm/constants/index.js +3 -0
  51. package/dist/esm/constants/version.d.ts +1 -1
  52. package/dist/esm/constants/version.js +1 -1
  53. package/dist/esm/helpers/telemetry.js +34 -0
  54. package/dist/esm/index.js +3 -0
  55. package/dist/esm/styles/index.d.ts +11 -0
  56. package/dist/esm/styles/index.js +20 -5
  57. package/dist/esm/types/enums.d.ts +3 -0
  58. package/dist/esm/types/enums.js +3 -0
  59. package/package.json +2 -2
  60. package/src/components/Inspector/AnalyticsTab.tsx +71 -43
  61. package/src/components/Inspector/BundleTab.tsx +3 -0
  62. package/src/components/Inspector/ConsoleTab.tsx +28 -1
  63. package/src/components/Inspector/CrashTab.tsx +45 -14
  64. package/src/components/Inspector/FeatureUnderDevNotice.tsx +94 -0
  65. package/src/components/Inspector/InspectorHeader.tsx +257 -115
  66. package/src/components/Inspector/NetworkDetail.tsx +2 -1
  67. package/src/components/Inspector/NetworkFilterModal.tsx +710 -0
  68. package/src/components/Inspector/NetworkTab.tsx +127 -204
  69. package/src/components/Inspector/NpmUpdateToast.tsx +1 -1
  70. package/src/components/Inspector/PerformanceTab.tsx +3 -0
  71. package/src/components/Inspector/ReduxTab.tsx +28 -1
  72. package/src/components/Inspector/SettingsPanel.tsx +93 -132
  73. package/src/components/Inspector/StorageTab.tsx +56 -27
  74. package/src/components/Inspector/UpdateAvailableModal.tsx +2 -0
  75. package/src/components/LogCard.tsx +43 -31
  76. package/src/components/NetworkIcons.tsx +27 -0
  77. package/src/constants/index.ts +3 -0
  78. package/src/constants/version.ts +1 -1
  79. package/src/helpers/telemetry.ts +36 -0
  80. package/src/index.tsx +5 -0
  81. package/src/styles/index.ts +20 -5
  82. package/src/types/enums.ts +3 -0
@@ -4,7 +4,7 @@ import Svg, { Path } from 'react-native-svg';
4
4
  import { AppColors } from '../styles/AppColors';
5
5
  import { METHOD_COLORS } from '../constants';
6
6
  import { getStatusColor, getDurationColor, formatDateTime, getSize, } from '../helpers';
7
- import { CalendarIcon, ClockIcon, SizeIcon, GlobeIcon, CircleCheckIcon, CircleAlertIcon, CircleXIcon, RepeatIcon, } from './NetworkIcons';
7
+ import { CalendarIcon, ClockIcon, SizeIcon, GlobeIcon, CircleCheckIcon, CircleAlertIcon, CircleXIcon, RepeatIcon, PinIcon, } from './NetworkIcons';
8
8
  import { AppFonts } from '../styles/AppFonts';
9
9
  import styles from '../styles';
10
10
  import HighlightText from './HighlightText';
@@ -198,29 +198,6 @@ const LogCard = React.memo(function LogCard({ item, onPress, timelineMinStart, t
198
198
  GQL
199
199
  </Text>
200
200
  </View>)}
201
-
202
-
203
- {item.duration != null && !isFailed && (<View style={[
204
- styles.chip,
205
- {
206
- backgroundColor: `${durationColor}12`,
207
- borderColor: `${durationColor}2E`,
208
- paddingHorizontal: 4.5,
209
- paddingVertical: 1,
210
- borderRadius: 4,
211
- },
212
- ]}>
213
- <Text style={[
214
- styles.chipText,
215
- {
216
- fontFamily: AppFonts.interBold,
217
- fontSize: 8.5,
218
- color: durationColor,
219
- },
220
- ]}>
221
- {item.duration}ms
222
- </Text>
223
- </View>)}
224
201
  </View>
225
202
 
226
203
  <View style={[
@@ -239,6 +216,7 @@ const LogCard = React.memo(function LogCard({ item, onPress, timelineMinStart, t
239
216
  : isLoading
240
217
  ? `${AppColors.darkOrange}30`
241
218
  : `${statusColor}30`,
219
+ flexShrink: 0,
242
220
  },
243
221
  ]}>
244
222
  {renderStatusIcon()}
@@ -272,6 +250,7 @@ const LogCard = React.memo(function LogCard({ item, onPress, timelineMinStart, t
272
250
  alignItems: 'center',
273
251
  justifyContent: 'center',
274
252
  marginRight: 4,
253
+ flexShrink: 0,
275
254
  }}>
276
255
  <GlobeIcon color={AppColors.skyBlue} size={11}/>
277
256
  </View>
@@ -310,9 +289,39 @@ const LogCard = React.memo(function LogCard({ item, onPress, timelineMinStart, t
310
289
 
311
290
 
312
291
  <View style={styles.cardFooterRow}>
313
- <View style={styles.cardDateRow}>
314
- <CalendarIcon color={AppColors.grayTextWeak} size={10}/>
315
- <Text style={styles.cardDateText}>{triggeredAt}</Text>
292
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6, flex: 1, minWidth: 0, marginRight: 8 }}>
293
+ <View style={[styles.cardDateRow, { flexShrink: 0 }]}>
294
+ <CalendarIcon color={AppColors.grayTextWeak} size={10}/>
295
+ <Text style={styles.cardDateText}>{triggeredAt}</Text>
296
+ </View>
297
+
298
+ {item.caller && item.caller !== 'Unknown' && (<View style={[
299
+ styles.chip,
300
+ {
301
+ flexDirection: 'row',
302
+ alignItems: 'center',
303
+ gap: 3,
304
+ backgroundColor: `${AppColors.skyBlue}14`,
305
+ borderColor: `${AppColors.skyBlue}30`,
306
+ paddingHorizontal: 5,
307
+ paddingVertical: 1,
308
+ borderRadius: 4,
309
+ flexShrink: 1,
310
+ minWidth: 0,
311
+ },
312
+ ]}>
313
+ <PinIcon color={AppColors.skyBlue} size={8.5}/>
314
+ <Text style={[
315
+ styles.chipText,
316
+ {
317
+ color: AppColors.skyBlue,
318
+ fontSize: 8.5,
319
+ fontFamily: AppFonts.interMedium,
320
+ },
321
+ ]} numberOfLines={1} ellipsizeMode="middle">
322
+ {item.caller}
323
+ </Text>
324
+ </View>)}
316
325
  </View>
317
326
 
318
327
  <View style={styles.cardFooterRight}>
@@ -133,3 +133,4 @@ export declare const ShieldCheckIcon: ({ color, size, }: IconProps) => React.JSX
133
133
  export declare const DatabaseIcon: ({ color, size, }: IconProps) => React.JSX.Element;
134
134
  export declare const PencilIcon: ({ color, size, }: IconProps) => React.JSX.Element;
135
135
  export declare const PlusIcon: ({ color, size, }: IconProps) => React.JSX.Element;
136
+ export declare const LockIcon: ({ color, size, }: IconProps) => React.JSX.Element;
@@ -730,3 +730,7 @@ export const PencilIcon = ({ color = AppColors.grayTextWeak, size = 14, }) => (<
730
730
  export const PlusIcon = ({ color = AppColors.grayTextWeak, size = 14, }) => (<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
731
731
  <Path d="M12 5v14M5 12h14" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
732
732
  </Svg>);
733
+ export const LockIcon = ({ color = AppColors.grayTextWeak, size = 14, }) => (<Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
734
+ <Rect x="3" y="11" width="18" height="11" rx="2" ry="2" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
735
+ <Path d="M7 11V7a5 5 0 0 1 10 0v4" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
736
+ </Svg>);
@@ -18,6 +18,9 @@ export const METHOD_COLORS = {
18
18
  PUT: '#D97706',
19
19
  PATCH: '#7C3AED',
20
20
  DELETE: '#DC2626',
21
+ QUERY: '#0284C7',
22
+ OPTIONS: '#475569',
23
+ HEAD: '#0891B2',
21
24
  };
22
25
  export const DOMAIN_COLORS = AppColors.domainColors;
23
26
  export const DURATION_FAST_MS = 200;
@@ -1 +1 @@
1
- export declare const LIB_VERSION = "2.3.8";
1
+ export declare const LIB_VERSION = "2.3.10";
@@ -1 +1 @@
1
- export const LIB_VERSION = '2.3.8';
1
+ export const LIB_VERSION = '2.3.10';
@@ -79,6 +79,36 @@ async function getOrCreateClientId() {
79
79
  return `${Math.floor(100000000 + Math.random() * 900000000)}.${Math.floor(1000000000 + Math.random() * 9000000000)}`;
80
80
  }
81
81
  }
82
+ function isDeviceEmulator() {
83
+ try {
84
+ if (Platform.OS === 'android') {
85
+ const c = Platform.constants || {};
86
+ const model = String(c.Model || '').toLowerCase();
87
+ const brand = String(c.Brand || '').toLowerCase();
88
+ const fingerprint = String(c.Fingerprint || '').toLowerCase();
89
+ const hardware = String(c.Hardware || '').toLowerCase();
90
+ const manufacturer = String(c.Manufacturer || '').toLowerCase();
91
+ return (model.includes('emulator') ||
92
+ model.includes('sdk') ||
93
+ model.includes('google_sdk') ||
94
+ model.includes('vbox') ||
95
+ brand.includes('generic') ||
96
+ fingerprint.startsWith('generic') ||
97
+ hardware.includes('goldfish') ||
98
+ hardware.includes('ranchu') ||
99
+ manufacturer.includes('genymotion'));
100
+ }
101
+ if (Platform.OS === 'ios') {
102
+ const c = Platform.constants || {};
103
+ const model = String(c.Model || '').toLowerCase();
104
+ return model.includes('simulator') || Boolean(c.isSimulator);
105
+ }
106
+ }
107
+ catch {
108
+ return false;
109
+ }
110
+ return false;
111
+ }
82
112
  function getSystemMetadata(environment) {
83
113
  const rnConstants = Platform.constants;
84
114
  const rnVersion = rnConstants?.reactNativeVersion
@@ -87,6 +117,7 @@ function getSystemMetadata(environment) {
87
117
  const isHermes = Boolean(typeof global.HermesInternal === 'object' && global.HermesInternal !== null);
88
118
  const isFabric = Boolean(global?.nativeFabricUIManager !== undefined);
89
119
  const isDev = Boolean(typeof __DEV__ !== 'undefined' && __DEV__);
120
+ const isEmulator = isDeviceEmulator();
90
121
  return {
91
122
  lib_version: LIB_VERSION,
92
123
  platform: Platform.OS,
@@ -95,6 +126,9 @@ function getSystemMetadata(environment) {
95
126
  is_hermes: isHermes,
96
127
  is_fabric: isFabric,
97
128
  is_dev: isDev,
129
+ is_emulator: isEmulator,
130
+ device_type: isEmulator ? 'emulator' : 'real_device',
131
+ app_mode: isDev ? 'local_mode' : 'bundled_application',
98
132
  environment: environment || (isDev ? 'DEV' : 'PROD'),
99
133
  engagement_time_msec: 1000,
100
134
  };
package/dist/esm/index.js CHANGED
@@ -362,6 +362,9 @@ const NetworkInspector = ({ enabled = true, storage, navigationRef, appIcon, env
362
362
  return false;
363
363
  }, [latestNpmVersion]);
364
364
  useEffect(() => {
365
+ if (typeof __DEV__ === 'undefined' || !__DEV__) {
366
+ return;
367
+ }
365
368
  let cancelled = false;
366
369
  fetch('https://registry.npmjs.org/react-native-inapp-inspector/latest')
367
370
  .then(res => (res.ok ? res.json() : null))
@@ -603,6 +603,7 @@ export declare const getRawStyles: (colors: typeof AppColors) => {
603
603
  alignItems: string;
604
604
  justifyContent: string;
605
605
  marginBottom: number;
606
+ minHeight: number;
606
607
  };
607
608
  cardHeaderLeft: {
608
609
  flexDirection: string;
@@ -610,11 +611,13 @@ export declare const getRawStyles: (colors: typeof AppColors) => {
610
611
  flex: number;
611
612
  marginRight: number;
612
613
  gap: number;
614
+ minWidth: number;
613
615
  };
614
616
  cardHeaderRight: {
615
617
  flexDirection: string;
616
618
  alignItems: string;
617
619
  gap: number;
620
+ flexShrink: number;
618
621
  };
619
622
  smallCheckbox: {
620
623
  width: number;
@@ -701,6 +704,7 @@ export declare const getRawStyles: (colors: typeof AppColors) => {
701
704
  flexDirection: string;
702
705
  alignItems: string;
703
706
  flex: number;
707
+ minWidth: number;
704
708
  gap: number;
705
709
  };
706
710
  slugTag: {
@@ -713,6 +717,7 @@ export declare const getRawStyles: (colors: typeof AppColors) => {
713
717
  paddingVertical: number;
714
718
  borderRadius: number;
715
719
  marginTop: number;
720
+ flexShrink: number;
716
721
  };
717
722
  slugText: {
718
723
  fontFamily: string;
@@ -725,17 +730,20 @@ export declare const getRawStyles: (colors: typeof AppColors) => {
725
730
  flexDirection: string;
726
731
  alignItems: string;
727
732
  gap: number;
733
+ flexShrink: number;
728
734
  };
729
735
  cardFooterRow: {
730
736
  flexDirection: string;
731
737
  alignItems: string;
732
738
  justifyContent: string;
733
739
  marginTop: number;
740
+ minHeight: number;
734
741
  };
735
742
  cardDateRow: {
736
743
  flexDirection: string;
737
744
  alignItems: string;
738
745
  gap: number;
746
+ flexShrink: number;
739
747
  };
740
748
  cardDateText: {
741
749
  fontFamily: string;
@@ -747,6 +755,7 @@ export declare const getRawStyles: (colors: typeof AppColors) => {
747
755
  flexDirection: string;
748
756
  alignItems: string;
749
757
  gap: number;
758
+ flexShrink: number;
750
759
  };
751
760
  chip: {
752
761
  flexDirection: string;
@@ -848,11 +857,13 @@ export declare const getRawStyles: (colors: typeof AppColors) => {
848
857
  alignItems: string;
849
858
  justifyContent: string;
850
859
  paddingBottom: number;
860
+ gap: number;
851
861
  };
852
862
  detailInfoRight: {
853
863
  flexDirection: string;
854
864
  alignItems: string;
855
865
  gap: number;
866
+ flexShrink: number;
856
867
  };
857
868
  metaContainer: {
858
869
  backgroundColor: string;
@@ -558,18 +558,21 @@ export const getRawStyles = (colors) => ({
558
558
  alignItems: 'center',
559
559
  justifyContent: 'space-between',
560
560
  marginBottom: 4,
561
+ minHeight: 22,
561
562
  },
562
563
  cardHeaderLeft: {
563
564
  flexDirection: 'row',
564
565
  alignItems: 'center',
565
566
  flex: 1,
566
- marginRight: 8,
567
- gap: 6,
567
+ marginRight: 6,
568
+ gap: 5,
569
+ minWidth: 0,
568
570
  },
569
571
  cardHeaderRight: {
570
572
  flexDirection: 'row',
571
573
  alignItems: 'center',
572
574
  gap: 5,
575
+ flexShrink: 0,
573
576
  },
574
577
  smallCheckbox: {
575
578
  width: 15,
@@ -650,6 +653,7 @@ export const getRawStyles = (colors) => ({
650
653
  flexDirection: 'row',
651
654
  alignItems: 'flex-start',
652
655
  flex: 1,
656
+ minWidth: 0,
653
657
  gap: 6,
654
658
  },
655
659
  slugTag: {
@@ -662,6 +666,7 @@ export const getRawStyles = (colors) => ({
662
666
  paddingVertical: 1.5,
663
667
  borderRadius: 4,
664
668
  marginTop: 1,
669
+ flexShrink: 0,
665
670
  },
666
671
  slugText: {
667
672
  fontFamily: AppFonts.interMedium,
@@ -674,17 +679,20 @@ export const getRawStyles = (colors) => ({
674
679
  flexDirection: 'row',
675
680
  alignItems: 'center',
676
681
  gap: 4,
682
+ flexShrink: 0,
677
683
  },
678
684
  cardFooterRow: {
679
685
  flexDirection: 'row',
680
686
  alignItems: 'center',
681
687
  justifyContent: 'space-between',
682
- marginTop: 2,
688
+ marginTop: 4,
689
+ minHeight: 18,
683
690
  },
684
691
  cardDateRow: {
685
692
  flexDirection: 'row',
686
693
  alignItems: 'center',
687
694
  gap: 3.5,
695
+ flexShrink: 0,
688
696
  },
689
697
  cardDateText: {
690
698
  fontFamily: AppFonts.interRegular,
@@ -696,6 +704,7 @@ export const getRawStyles = (colors) => ({
696
704
  flexDirection: 'row',
697
705
  alignItems: 'center',
698
706
  gap: 5,
707
+ flexShrink: 0,
699
708
  },
700
709
  chip: {
701
710
  flexDirection: 'row',
@@ -787,9 +796,15 @@ export const getRawStyles = (colors) => ({
787
796
  flexDirection: 'row',
788
797
  alignItems: 'center',
789
798
  justifyContent: 'space-between',
790
- paddingBottom: 10,
799
+ paddingBottom: 8,
800
+ gap: 8,
801
+ },
802
+ detailInfoRight: {
803
+ flexDirection: 'row',
804
+ alignItems: 'center',
805
+ gap: 5,
806
+ flexShrink: 0,
791
807
  },
792
- detailInfoRight: { flexDirection: 'row', alignItems: 'center', gap: 6 },
793
808
  metaContainer: {
794
809
  backgroundColor: colors.primaryLight,
795
810
  borderWidth: 1,
@@ -17,6 +17,9 @@ export declare const Method: {
17
17
  readonly Put: "PUT";
18
18
  readonly Patch: "PATCH";
19
19
  readonly Delete: "DELETE";
20
+ readonly Query: "QUERY";
21
+ readonly Options: "OPTIONS";
22
+ readonly Head: "HEAD";
20
23
  };
21
24
  export type Method = (typeof Method)[keyof typeof Method];
22
25
  export declare const StatusFilter: {
@@ -16,6 +16,9 @@ export const Method = {
16
16
  Put: 'PUT',
17
17
  Patch: 'PATCH',
18
18
  Delete: 'DELETE',
19
+ Query: 'QUERY',
20
+ Options: 'OPTIONS',
21
+ Head: 'HEAD',
19
22
  };
20
23
  export const StatusFilter = {
21
24
  All: 'ALL',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-inapp-inspector",
3
- "version": "2.3.8",
3
+ "version": "2.3.10",
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",
@@ -105,7 +105,7 @@
105
105
  "prepare": "npm run build",
106
106
  "watch": "tsc -w",
107
107
  "prepack": "npm run build",
108
- "publish": "node release.cjs",
108
+ "release": "node release.cjs",
109
109
  "bundle-visualizer": "react-native-bundle-visualizer"
110
110
  },
111
111
  "keywords": [
@@ -1,4 +1,4 @@
1
- import React, {useCallback, useState, useMemo, useEffect} from 'react';
1
+ import React, {useCallback, useState, useMemo, useEffect, useRef} from 'react';
2
2
  import {
3
3
  FlatList,
4
4
  Pressable,
@@ -55,6 +55,7 @@ import {
55
55
  PinIcon,
56
56
  MoneyIcon,
57
57
  UserCheckIcon,
58
+ ChevronIcon,
58
59
  } from '../NetworkIcons';
59
60
 
60
61
  const AnalyticsHeader = React.memo(() => {
@@ -1090,6 +1091,7 @@ const AnalyticsTab = React.memo(() => {
1090
1091
  isAnalyticsFilterApplied,
1091
1092
  } = useInspector();
1092
1093
 
1094
+ const listRef = useRef<FlatList>(null);
1093
1095
  const [isFilterModalOpen, setIsFilterModalOpen] = useState(false);
1094
1096
 
1095
1097
  const keyExtractor = useCallback(
@@ -1305,49 +1307,75 @@ const AnalyticsTab = React.memo(() => {
1305
1307
  style={{flex: 1}}
1306
1308
  onLayout={() => setIsAnalyticsLayoutReady(true)}>
1307
1309
  {isAnalyticsLayoutReady ? (
1308
- <FlatList
1309
- data={filteredAnalyticsEvents}
1310
- keyExtractor={keyExtractor}
1311
- ListHeaderComponent={AnalyticsHeader}
1312
- renderItem={renderItem}
1313
- initialNumToRender={12}
1314
- maxToRenderPerBatch={8}
1315
- windowSize={5}
1316
- removeClippedSubviews={true}
1317
- renderToHardwareTextureAndroid={true}
1318
- ListEmptyComponent={
1319
- <EmptyState
1320
- isSearch={analyticsSearch.length > 0}
1321
- searchQuery={analyticsSearch}
1322
- customTitle={
1323
- analyticsSearch.length > 0
1324
- ? 'No matching analytics events'
1325
- : 'No analytics events yet'
1326
- }
1327
- customSub={
1328
- analyticsSearch.length > 0
1329
- ? `No events matched "${analyticsSearch}"`
1330
- : 'Call setupAnalyticsLogger(analytics()) at app start.'
1331
- }
1332
- onClearSearch={() => setAnalyticsSearch('')}
1333
- />
1334
- }
1335
- ListFooterComponent={
1336
- filteredAnalyticsEvents.length > 0 ? (
1337
- <EndOfListFooter
1338
- count={filteredAnalyticsEvents.length}
1339
- label="events"
1310
+ <>
1311
+ <FlatList
1312
+ ref={listRef}
1313
+ data={filteredAnalyticsEvents}
1314
+ keyExtractor={keyExtractor}
1315
+ ListHeaderComponent={AnalyticsHeader}
1316
+ renderItem={renderItem}
1317
+ initialNumToRender={12}
1318
+ maxToRenderPerBatch={8}
1319
+ windowSize={5}
1320
+ removeClippedSubviews={true}
1321
+ renderToHardwareTextureAndroid={true}
1322
+ ListEmptyComponent={
1323
+ <EmptyState
1324
+ isSearch={analyticsSearch.length > 0}
1325
+ searchQuery={analyticsSearch}
1326
+ customTitle={
1327
+ analyticsSearch.length > 0
1328
+ ? 'No matching analytics events'
1329
+ : 'No analytics events yet'
1330
+ }
1331
+ customSub={
1332
+ analyticsSearch.length > 0
1333
+ ? `No events matched "${analyticsSearch}"`
1334
+ : 'Call setupAnalyticsLogger(analytics()) at app start.'
1335
+ }
1336
+ onClearSearch={() => setAnalyticsSearch('')}
1340
1337
  />
1341
- ) : null
1342
- }
1343
- contentContainerStyle={[
1344
- styles.listContent,
1345
- filteredAnalyticsEvents.length === 0 && {
1346
- flexGrow: 1,
1347
- },
1348
- ]}
1349
- keyboardShouldPersistTaps="handled"
1350
- />
1338
+ }
1339
+ ListFooterComponent={
1340
+ filteredAnalyticsEvents.length > 0 ? (
1341
+ <EndOfListFooter
1342
+ count={filteredAnalyticsEvents.length}
1343
+ label="events"
1344
+ />
1345
+ ) : null
1346
+ }
1347
+ contentContainerStyle={[
1348
+ styles.listContent,
1349
+ filteredAnalyticsEvents.length === 0 && {
1350
+ flexGrow: 1,
1351
+ },
1352
+ ]}
1353
+ keyboardShouldPersistTaps="handled"
1354
+ />
1355
+
1356
+ <TouchableScale
1357
+ onPress={() => {
1358
+ try {
1359
+ listRef.current?.scrollToOffset({
1360
+ offset: 0,
1361
+ animated: true,
1362
+ });
1363
+ } catch {
1364
+ try {
1365
+ listRef.current?.scrollToIndex({
1366
+ index: 0,
1367
+ animated: true,
1368
+ });
1369
+ } catch {}
1370
+ }
1371
+ }}
1372
+ hitSlop={12}
1373
+ style={styles.scrollTopBtn}>
1374
+ <View style={{transform: [{rotate: '180deg'}]}}>
1375
+ <ChevronIcon color={AppColors.white} size={18} />
1376
+ </View>
1377
+ </TouchableScale>
1378
+ </>
1351
1379
  ) : null}
1352
1380
  </View>
1353
1381
  </>
@@ -19,6 +19,7 @@ import HighlightText from '../HighlightText';
19
19
  import SegmentedTabs from '../SegmentedTabs';
20
20
  import AnimatedEntrance from '../AnimatedEntrance';
21
21
  import EndOfListFooter from '../EndOfListFooter';
22
+ import FeatureUnderDevNotice from './FeatureUnderDevNotice';
22
23
  import {AppColors} from '../../styles/AppColors';
23
24
  import {AppFonts} from '../../styles/AppFonts';
24
25
  import styles from '../../styles';
@@ -1400,6 +1401,8 @@ const BundleTab = React.memo(() => {
1400
1401
  </TouchableScale>
1401
1402
  </View>
1402
1403
 
1404
+ <FeatureUnderDevNotice featureName="Metro Bundle Analyzer" />
1405
+
1403
1406
  {/* ══════════════════════════════════════════════════════════════════════ */}
1404
1407
  {/* ── 1. TAB: OVERVIEW & TREEMAP ───────────────────────────────────────── */}
1405
1408
  {/* ══════════════════════════════════════════════════════════════════════ */}
@@ -1,4 +1,4 @@
1
- import React, {useCallback, useMemo} from 'react';
1
+ import React, {useCallback, useMemo, useRef} from 'react';
2
2
  import {
3
3
  FlatList,
4
4
  Pressable,
@@ -30,6 +30,7 @@ import {
30
30
  ErrorCircleIcon,
31
31
  AnalyticsIcon,
32
32
  HeaderPauseIcon,
33
+ ChevronIcon,
33
34
  } from '../NetworkIcons';
34
35
 
35
36
  const ConsoleTab = React.memo(() => {
@@ -49,6 +50,8 @@ const ConsoleTab = React.memo(() => {
49
50
  setIsConsolePaused,
50
51
  } = useInspector();
51
52
 
53
+ const listRef = useRef<FlatList>(null);
54
+
52
55
  const renderItem = useCallback(
53
56
  ({item, index}: {item: any; index: number}) => (
54
57
  <AnimatedEntrance index={index} distance={8}>
@@ -668,6 +671,7 @@ const ConsoleTab = React.memo(() => {
668
671
  </View>
669
672
 
670
673
  <FlatList
674
+ ref={listRef}
671
675
  data={filteredConsoleLogs}
672
676
  keyExtractor={keyExtractor}
673
677
  ListHeaderComponent={listHeader}
@@ -705,6 +709,29 @@ const ConsoleTab = React.memo(() => {
705
709
  ]}
706
710
  keyboardShouldPersistTaps="handled"
707
711
  />
712
+
713
+ <TouchableScale
714
+ onPress={() => {
715
+ try {
716
+ listRef.current?.scrollToOffset({
717
+ offset: 0,
718
+ animated: true,
719
+ });
720
+ } catch {
721
+ try {
722
+ listRef.current?.scrollToIndex({
723
+ index: 0,
724
+ animated: true,
725
+ });
726
+ } catch {}
727
+ }
728
+ }}
729
+ hitSlop={12}
730
+ style={styles.scrollTopBtn}>
731
+ <View style={{transform: [{rotate: '180deg'}]}}>
732
+ <ChevronIcon color={AppColors.white} size={18} />
733
+ </View>
734
+ </TouchableScale>
708
735
  </View>
709
736
  );
710
737
  });