react-native-inapp-inspector 2.0.5 → 2.0.7

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 (85) hide show
  1. package/android/src/main/java/com/inappinspector/NetworkInspectorModule.kt +11 -0
  2. package/dist/commonjs/components/Inspector/AuditTab.d.ts +3 -0
  3. package/dist/commonjs/components/Inspector/AuditTab.js +741 -0
  4. package/dist/commonjs/components/Inspector/DeviceTab.d.ts +3 -0
  5. package/dist/commonjs/components/Inspector/DeviceTab.js +717 -0
  6. package/dist/commonjs/components/Inspector/FirstTimeWalkthroughModal.d.ts +7 -0
  7. package/dist/commonjs/components/Inspector/FirstTimeWalkthroughModal.js +600 -0
  8. package/dist/commonjs/components/Inspector/InspectorHeader.js +28 -25
  9. package/dist/commonjs/components/Inspector/MainScreen.js +19 -2
  10. package/dist/commonjs/components/Inspector/NetworkDetail.js +15 -0
  11. package/dist/commonjs/components/Inspector/PerformanceHud.d.ts +7 -0
  12. package/dist/commonjs/components/Inspector/PerformanceHud.js +210 -0
  13. package/dist/commonjs/components/Inspector/PerformanceTab.js +30 -18
  14. package/dist/commonjs/components/Inspector/RequestReplayModal.d.ts +9 -0
  15. package/dist/commonjs/components/Inspector/RequestReplayModal.js +792 -0
  16. package/dist/commonjs/components/Inspector/RequestReplayView.d.ts +8 -0
  17. package/dist/commonjs/components/Inspector/RequestReplayView.js +871 -0
  18. package/dist/commonjs/components/Inspector/SettingsPanel.js +448 -49
  19. package/dist/commonjs/components/Inspector/StorageTab.d.ts +3 -0
  20. package/dist/commonjs/components/Inspector/StorageTab.js +610 -0
  21. package/dist/commonjs/components/Inspector/TabBar.js +23 -2
  22. package/dist/commonjs/components/NetworkIcons.d.ts +12 -0
  23. package/dist/commonjs/components/NetworkIcons.js +72 -3
  24. package/dist/commonjs/constants/version.d.ts +1 -1
  25. package/dist/commonjs/constants/version.js +1 -1
  26. package/dist/commonjs/customHooks/storageInspector.d.ts +13 -0
  27. package/dist/commonjs/customHooks/storageInspector.js +179 -0
  28. package/dist/commonjs/helpers/deviceInfo.d.ts +4 -0
  29. package/dist/commonjs/helpers/deviceInfo.js +94 -0
  30. package/dist/commonjs/helpers/index.d.ts +2 -0
  31. package/dist/commonjs/helpers/index.js +12 -1
  32. package/dist/commonjs/helpers/packageAuditor.d.ts +5 -0
  33. package/dist/commonjs/helpers/packageAuditor.js +182 -0
  34. package/dist/commonjs/helpers/settingsStore.d.ts +1 -1
  35. package/dist/commonjs/helpers/settingsStore.js +4 -2
  36. package/dist/commonjs/i18n/locales/en.json +3 -2
  37. package/dist/commonjs/index.d.ts +1 -0
  38. package/dist/commonjs/index.js +127 -9
  39. package/dist/commonjs/native/NativeInspector.d.ts +1 -0
  40. package/dist/commonjs/types/enums.d.ts +7 -0
  41. package/dist/commonjs/types/enums.js +7 -0
  42. package/dist/commonjs/types/interfaces.d.ts +77 -0
  43. package/dist/esm/components/Inspector/AuditTab.d.ts +3 -0
  44. package/dist/esm/components/Inspector/AuditTab.js +702 -0
  45. package/dist/esm/components/Inspector/DeviceTab.d.ts +3 -0
  46. package/dist/esm/components/Inspector/DeviceTab.js +678 -0
  47. package/dist/esm/components/Inspector/FirstTimeWalkthroughModal.d.ts +7 -0
  48. package/dist/esm/components/Inspector/FirstTimeWalkthroughModal.js +560 -0
  49. package/dist/esm/components/Inspector/InspectorHeader.js +28 -25
  50. package/dist/esm/components/Inspector/MainScreen.js +19 -2
  51. package/dist/esm/components/Inspector/NetworkDetail.js +16 -1
  52. package/dist/esm/components/Inspector/PerformanceHud.d.ts +7 -0
  53. package/dist/esm/components/Inspector/PerformanceHud.js +170 -0
  54. package/dist/esm/components/Inspector/PerformanceTab.js +31 -19
  55. package/dist/esm/components/Inspector/RequestReplayModal.d.ts +9 -0
  56. package/dist/esm/components/Inspector/RequestReplayModal.js +752 -0
  57. package/dist/esm/components/Inspector/RequestReplayView.d.ts +8 -0
  58. package/dist/esm/components/Inspector/RequestReplayView.js +831 -0
  59. package/dist/esm/components/Inspector/SettingsPanel.js +449 -50
  60. package/dist/esm/components/Inspector/StorageTab.d.ts +3 -0
  61. package/dist/esm/components/Inspector/StorageTab.js +571 -0
  62. package/dist/esm/components/Inspector/TabBar.js +24 -3
  63. package/dist/esm/components/NetworkIcons.d.ts +12 -0
  64. package/dist/esm/components/NetworkIcons.js +57 -0
  65. package/dist/esm/constants/version.d.ts +1 -1
  66. package/dist/esm/constants/version.js +1 -1
  67. package/dist/esm/customHooks/storageInspector.d.ts +13 -0
  68. package/dist/esm/customHooks/storageInspector.js +170 -0
  69. package/dist/esm/helpers/deviceInfo.d.ts +4 -0
  70. package/dist/esm/helpers/deviceInfo.js +89 -0
  71. package/dist/esm/helpers/index.d.ts +2 -0
  72. package/dist/esm/helpers/index.js +10 -0
  73. package/dist/esm/helpers/packageAuditor.d.ts +5 -0
  74. package/dist/esm/helpers/packageAuditor.js +173 -0
  75. package/dist/esm/helpers/settingsStore.d.ts +1 -1
  76. package/dist/esm/helpers/settingsStore.js +4 -2
  77. package/dist/esm/i18n/locales/en.json +3 -2
  78. package/dist/esm/index.d.ts +1 -0
  79. package/dist/esm/index.js +123 -8
  80. package/dist/esm/native/NativeInspector.d.ts +1 -0
  81. package/dist/esm/types/enums.d.ts +7 -0
  82. package/dist/esm/types/enums.js +7 -0
  83. package/dist/esm/types/interfaces.d.ts +77 -0
  84. package/ios/NetworkInspectorModule.mm +12 -5
  85. package/package.json +4 -1
@@ -17,15 +17,20 @@ import BundleTab from './BundleTab';
17
17
  import PerformanceTab from './PerformanceTab';
18
18
  import CrashTab from './CrashTab';
19
19
  import CrashDetail from './CrashDetail';
20
+ import StorageTab from './StorageTab';
21
+ import DeviceTab from './DeviceTab';
22
+ import AuditTab from './AuditTab';
20
23
  import SettingsPanel from './SettingsPanel';
21
24
  import TelemetryConsentModal from './TelemetryConsentModal';
25
+ import FirstTimeWalkthroughModal from './FirstTimeWalkthroughModal';
22
26
  import NpmUpdateToast from './NpmUpdateToast';
23
27
  import Toast from '../Toast';
24
28
  import styles from '../../styles';
25
29
  import { AppColors } from '../../styles/AppColors';
26
30
  import NavigationTracker from './NavigationTracker';
31
+ import PerformanceHud from './PerformanceHud';
27
32
  const MainScreen = () => {
28
- const { visible, modalAnimationType, closeModal, modalHeightPercent, selected, selectedEvent, selectedLog, selectedReduxSlice, selectedReduxAction, selectedCrash, settingsPage, activeTab, isReady, isEnabled, useNativeFab, hasNavigationContext, setNavState, } = useInspector();
33
+ const { visible, setVisible, switchActiveTab, modalAnimationType, closeModal, modalHeightPercent, isWalkthroughVisible, setIsWalkthroughVisible, selected, selectedEvent, selectedLog, selectedReduxSlice, selectedReduxAction, selectedCrash, settingsPage, activeTab, isReady, isEnabled, isPerformanceHudEnabled, useNativeFab, hasNavigationContext, setNavState, } = useInspector();
29
34
  const isDetailActive = (activeTab === 'apis' && selected != null) ||
30
35
  (activeTab === 'analytics' && selectedEvent != null) ||
31
36
  (activeTab === 'logs' && selectedLog != null) ||
@@ -66,10 +71,16 @@ const MainScreen = () => {
66
71
  }).start();
67
72
  }, [activeTab]);
68
73
  return (<>
69
- {(Platform.OS === 'ios' || Platform.OS === 'android') &&
74
+ <View pointerEvents="box-none" style={StyleSheet.absoluteFillObject}>
75
+ {(Platform.OS === 'ios' || Platform.OS === 'android') &&
70
76
  isEnabled &&
71
77
  !visible &&
72
78
  !useNativeFab && <FabLauncher />}
79
+ {isEnabled && !visible && isPerformanceHudEnabled && (<PerformanceHud visible={true} onPress={() => {
80
+ setVisible(true);
81
+ switchActiveTab('performance');
82
+ }}/>)}
83
+ </View>
73
84
  <Modal visible={visible} animationType={modalAnimationType} transparent statusBarTranslucent={true}>
74
85
  {visible && (<ErrorBoundary onClose={closeModal}>
75
86
  <View style={styles.modalBackdrop}>
@@ -113,6 +124,9 @@ const MainScreen = () => {
113
124
  {activeTab === 'logs' && <ConsoleTab />}
114
125
  {activeTab === 'analytics' && <AnalyticsTab />}
115
126
  {activeTab === 'redux' && <ReduxTab />}
127
+ {activeTab === 'storage' && <StorageTab />}
128
+ {activeTab === 'device' && <DeviceTab />}
129
+ {activeTab === 'audit' && <AuditTab />}
116
130
  {activeTab === 'bundle' && <BundleTab />}
117
131
  {activeTab === 'performance' && <PerformanceTab />}
118
132
  {activeTab === 'crash' && <CrashTab />}
@@ -172,6 +186,9 @@ const MainScreen = () => {
172
186
 
173
187
  {/* NPM Version Update Toast with timeout progress bar */}
174
188
  <NpmUpdateToast />
189
+
190
+ {/* First-Time Walkthrough & Quick Guide Modal */}
191
+ <FirstTimeWalkthroughModal visible={isWalkthroughVisible} onClose={() => setIsWalkthroughVisible(false)}/>
175
192
  </View>
176
193
  </View>
177
194
  </ErrorBoundary>)}
@@ -16,9 +16,11 @@ import { AppColors } from '../../styles/AppColors';
16
16
  import { AppFonts } from '../../styles/AppFonts';
17
17
  import { METHOD_COLORS } from '../../constants';
18
18
  import { getStatusColor, getSize, formatDateTime, getFetchCommand, getCurlCommand, } from '../../helpers';
19
- import { FailIcon, StatusIcon, HeadersIcon, RequestIcon, ResponseIcon, DownloadIcon, ClearIcon, SizeIcon, ExternalLinkIcon, ClockIcon, } from '../NetworkIcons';
19
+ import { FailIcon, StatusIcon, HeadersIcon, RequestIcon, ResponseIcon, DownloadIcon, ClearIcon, SizeIcon, ExternalLinkIcon, ClockIcon, ReplayIcon, } from '../NetworkIcons';
20
+ import RequestReplayView from './RequestReplayView';
20
21
  const NetworkDetail = React.memo(() => {
21
22
  const { t } = useTranslation();
23
+ const [isReplayScreenActive, setIsReplayScreenActive] = React.useState(false);
22
24
  const { selected, detailDisplayUrl, apiDetailActiveTab, setApiDetailActiveTab, detailSearch, setDetailSearch, reqExpanded, setReqExpanded, resExpanded, setResExpanded, showReqDiff, setShowReqDiff, showResDiff, setShowResDiff, prevRequestData, prevResponseData, logRouteMapRef, } = useInspector();
23
25
  const handleOpenUrl = () => {
24
26
  Alert.alert(t('common.openInBrowser') || 'Open in Browser', `${t('common.openInBrowserPrompt') || 'Are you sure you want to open this URL in your external browser?'}\n\n${detailDisplayUrl}`, [
@@ -42,6 +44,9 @@ const NetworkDetail = React.memo(() => {
42
44
  };
43
45
  if (!selected)
44
46
  return null;
47
+ if (isReplayScreenActive) {
48
+ return (<RequestReplayView request={selected} onBack={() => setIsReplayScreenActive(false)}/>);
49
+ }
45
50
  return (<View style={{ flex: 1 }}>
46
51
  {/* Non-scrollable details header */}
47
52
  <View style={{ paddingHorizontal: 6, paddingTop: 4 }}>
@@ -247,6 +252,16 @@ const NetworkDetail = React.memo(() => {
247
252
  </View>
248
253
 
249
254
  <View style={styles.detailInfoRight}>
255
+ <TouchableScale style={[
256
+ styles.iconSquareBtn,
257
+ {
258
+ backgroundColor: `${AppColors.purple}15`,
259
+ borderColor: `${AppColors.purple}30`,
260
+ borderWidth: 1,
261
+ },
262
+ ]} onPress={() => setIsReplayScreenActive(true)} accessible={true} accessibilityRole="button" accessibilityLabel="Edit & Resend Request" hitSlop={12}>
263
+ <ReplayIcon color={AppColors.purple} size={14}/>
264
+ </TouchableScale>
250
265
  <TouchableScale style={[styles.iconSquareBtn, { backgroundColor: `${AppColors.sky600}15` }]} onPress={handleOpenUrl} hitSlop={12}>
251
266
  <ExternalLinkIcon color={AppColors.sky600} size={14}/>
252
267
  </TouchableScale>
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ interface PerformanceHudProps {
3
+ visible: boolean;
4
+ onPress: () => void;
5
+ }
6
+ export declare const PerformanceHud: React.FC<PerformanceHudProps>;
7
+ export default PerformanceHud;
@@ -0,0 +1,170 @@
1
+ import React, { useEffect, useRef, useState } from 'react';
2
+ import { Animated, PanResponder, Platform, StyleSheet, Text, View, } from 'react-native';
3
+ import TouchableScale from '../TouchableScale';
4
+ import { AppColors } from '../../styles/AppColors';
5
+ import { AppFonts } from '../../styles/AppFonts';
6
+ import { getNativeDeviceMetrics } from '../../native/NativeInspector';
7
+ export const PerformanceHud = ({ visible, onPress, }) => {
8
+ const [fps, setFps] = useState(60);
9
+ const [ramMb, setRamMb] = useState(45);
10
+ // Pan / Drag positioning
11
+ const pan = useRef(new Animated.ValueXY({ x: 16, y: Platform.OS === 'ios' ? 56 : 36 })).current;
12
+ const panResponder = useRef(PanResponder.create({
13
+ onStartShouldSetPanResponder: () => false,
14
+ onMoveShouldSetPanResponder: (_, gestureState) => {
15
+ return Math.abs(gestureState.dx) > 4 || Math.abs(gestureState.dy) > 4;
16
+ },
17
+ onPanResponderGrant: () => {
18
+ pan.setOffset({
19
+ // @ts-ignore
20
+ x: pan.x._value,
21
+ // @ts-ignore
22
+ y: pan.y._value,
23
+ });
24
+ pan.setValue({ x: 0, y: 0 });
25
+ },
26
+ onPanResponderMove: Animated.event([null, { dx: pan.x, dy: pan.y }], {
27
+ useNativeDriver: false,
28
+ }),
29
+ onPanResponderRelease: () => {
30
+ pan.flattenOffset();
31
+ },
32
+ })).current;
33
+ // Real-time FPS Calculation via requestAnimationFrame
34
+ useEffect(() => {
35
+ if (!visible)
36
+ return;
37
+ let frameCount = 0;
38
+ let lastTime = performance.now();
39
+ let animId;
40
+ const loop = (now) => {
41
+ frameCount++;
42
+ const elapsed = now - lastTime;
43
+ if (elapsed >= 500) {
44
+ const currentFps = Math.min(60, Math.round((frameCount * 1000) / elapsed));
45
+ setFps(currentFps);
46
+ frameCount = 0;
47
+ lastTime = now;
48
+ }
49
+ animId = requestAnimationFrame(loop);
50
+ };
51
+ animId = requestAnimationFrame(loop);
52
+ return () => {
53
+ cancelAnimationFrame(animId);
54
+ };
55
+ }, [visible]);
56
+ // Memory Metrics Polling
57
+ useEffect(() => {
58
+ if (!visible)
59
+ return;
60
+ const interval = setInterval(async () => {
61
+ try {
62
+ const metrics = await getNativeDeviceMetrics();
63
+ if (metrics && metrics.residentMemory != null) {
64
+ const mb = metrics.residentMemory / (1024 * 1024);
65
+ setRamMb(Math.round(mb));
66
+ }
67
+ else if (metrics && metrics.usedRAM != null) {
68
+ const mb = metrics.usedRAM / (1024 * 1024);
69
+ setRamMb(Math.round(mb));
70
+ }
71
+ else if (typeof performance !== 'undefined' &&
72
+ // @ts-ignore
73
+ performance.memory?.usedJSHeapSize) {
74
+ // @ts-ignore
75
+ const mb = performance.memory.usedJSHeapSize / (1024 * 1024);
76
+ setRamMb(Math.round(mb));
77
+ }
78
+ }
79
+ catch {
80
+ // Safe fallback
81
+ }
82
+ }, 1500);
83
+ return () => clearInterval(interval);
84
+ }, [visible]);
85
+ if (!visible)
86
+ return null;
87
+ const fpsColor = fps >= 55
88
+ ? AppColors.greenColor
89
+ : fps >= 35
90
+ ? AppColors.warningIconGold
91
+ : AppColors.errorColor;
92
+ return (<Animated.View {...panResponder.panHandlers} style={[
93
+ styles.hudContainer,
94
+ {
95
+ transform: pan.getTranslateTransform(),
96
+ },
97
+ ]}>
98
+ <TouchableScale onPress={onPress} style={styles.hudInner}>
99
+ {/* FPS Indicator */}
100
+ <View style={styles.metricItem}>
101
+ <View style={[styles.statusDot, { backgroundColor: fpsColor }]}/>
102
+ <Text style={[styles.metricValue, { color: fpsColor }]}>{fps}</Text>
103
+ <Text style={styles.metricUnit}>FPS</Text>
104
+ </View>
105
+
106
+ <View style={styles.divider}/>
107
+
108
+ {/* RAM / Heap Indicator */}
109
+ <View style={styles.metricItem}>
110
+ <Text style={styles.metricIcon}>⚡</Text>
111
+ <Text style={styles.metricValue}>{ramMb}</Text>
112
+ <Text style={styles.metricUnit}>MB</Text>
113
+ </View>
114
+ </TouchableScale>
115
+ </Animated.View>);
116
+ };
117
+ const styles = StyleSheet.create({
118
+ hudContainer: {
119
+ position: 'absolute',
120
+ top: 0,
121
+ left: 0,
122
+ zIndex: 999998,
123
+ elevation: 999998,
124
+ },
125
+ hudInner: {
126
+ flexDirection: 'row',
127
+ alignItems: 'center',
128
+ backgroundColor: 'rgba(15, 23, 42, 0.88)',
129
+ borderRadius: 20,
130
+ borderWidth: 1,
131
+ borderColor: 'rgba(255, 255, 255, 0.18)',
132
+ paddingHorizontal: 10,
133
+ paddingVertical: 5,
134
+ gap: 8,
135
+ shadowColor: AppColors.black,
136
+ shadowOpacity: 0.35,
137
+ shadowRadius: 6,
138
+ shadowOffset: { width: 0, height: 2 },
139
+ },
140
+ metricItem: {
141
+ flexDirection: 'row',
142
+ alignItems: 'center',
143
+ gap: 4,
144
+ },
145
+ statusDot: {
146
+ width: 6,
147
+ height: 6,
148
+ borderRadius: 3,
149
+ },
150
+ metricIcon: {
151
+ fontSize: 10,
152
+ },
153
+ metricValue: {
154
+ fontFamily: AppFonts.interBold,
155
+ fontSize: 11.5,
156
+ color: AppColors.white,
157
+ },
158
+ metricUnit: {
159
+ fontFamily: AppFonts.interRegular,
160
+ fontSize: 9,
161
+ color: 'rgba(255, 255, 255, 0.65)',
162
+ letterSpacing: 0.2,
163
+ },
164
+ divider: {
165
+ width: 1,
166
+ height: 12,
167
+ backgroundColor: 'rgba(255, 255, 255, 0.2)',
168
+ },
169
+ });
170
+ export default PerformanceHud;
@@ -9,7 +9,7 @@ import { AppColors } from '../../styles/AppColors';
9
9
  import { AppFonts } from '../../styles/AppFonts';
10
10
  import { useTranslation } from '../../i18n';
11
11
  import { getRuntimeDiagnostics } from '../../helpers';
12
- import { SearchIcon, CircleXIcon, PerformanceIcon, BoltIcon, LightbulbIcon, } from '../NetworkIcons';
12
+ import { SearchIcon, CircleXIcon, PerformanceIcon, BoltIcon, LightbulbIcon, WarningTriangleIcon, MapIcon, AtomIcon, BrainIcon, GlobeIcon, SparklesIcon, } from '../NetworkIcons';
13
13
  import { getPerformanceEvents, subscribePerformanceEvents, logPerformanceEvent, clearPerformanceEvents, } from '../../customHooks/performanceTracker';
14
14
  import { startNativeFpsMonitoring, stopNativeFpsMonitoring, getNativeFpsMetrics, getNativeSystemMetrics, isNativeModuleAvailable, } from '../../native/NativeInspector';
15
15
  const PerformanceTab = React.memo(() => {
@@ -373,24 +373,33 @@ const PerformanceTab = React.memo(() => {
373
373
  <View style={perfStyles.categoryScrollWrapper}>
374
374
  <ScrollView horizontal showsHorizontalScrollIndicator={false} style={perfStyles.categoryScrollView} contentContainerStyle={perfStyles.categoryScroll}>
375
375
  {[
376
- { key: 'ALL', label: t('performance.catAll') },
377
- { key: 'JANKY', label: t('performance.catJanky') },
378
- { key: 'NAVIGATION', label: t('performance.catNavigation') },
379
- { key: 'RENDER', label: t('performance.catRender') },
380
- { key: 'MEMORY', label: t('performance.catMemory') },
381
- { key: 'IO', label: t('performance.catIo') },
376
+ { key: 'ALL', label: t('performance.catAll'), icon: 'all' },
377
+ { key: 'JANKY', label: t('performance.catJanky'), icon: 'janky' },
378
+ { key: 'NAVIGATION', label: t('performance.catNavigation'), icon: 'nav' },
379
+ { key: 'RENDER', label: t('performance.catRender'), icon: 'render' },
380
+ { key: 'MEMORY', label: t('performance.catMemory'), icon: 'memory' },
381
+ { key: 'IO', label: t('performance.catIo'), icon: 'io' },
382
382
  ].map(cat => {
383
383
  const isSelected = filterCategory === cat.key;
384
+ const iconColor = isSelected ? AppColors.white : AppColors.grayText;
384
385
  return (<TouchableScale key={cat.key} onPress={() => setFilterCategory(cat.key)} style={[
385
386
  perfStyles.categoryChip,
386
387
  isSelected && perfStyles.categoryChipActive,
387
388
  ]}>
388
- <Text style={[
389
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 5 }}>
390
+ {cat.icon === 'all' && <SparklesIcon size={12} color={iconColor}/>}
391
+ {cat.icon === 'janky' && <WarningTriangleIcon size={12} color={isSelected ? AppColors.white : AppColors.errorColor}/>}
392
+ {cat.icon === 'nav' && <MapIcon size={12} color={iconColor}/>}
393
+ {cat.icon === 'render' && <AtomIcon size={12} color={iconColor}/>}
394
+ {cat.icon === 'memory' && <BrainIcon size={12} color={iconColor}/>}
395
+ {cat.icon === 'io' && <GlobeIcon size={12} color={iconColor}/>}
396
+ <Text style={[
389
397
  perfStyles.categoryChipText,
390
398
  isSelected && perfStyles.categoryChipTextActive,
391
399
  ]}>
392
- {cat.label}
393
- </Text>
400
+ {cat.label}
401
+ </Text>
402
+ </View>
394
403
  </TouchableScale>);
395
404
  })}
396
405
  </ScrollView>
@@ -401,13 +410,13 @@ const PerformanceTab = React.memo(() => {
401
410
  {/* ── 1. REAL-TIME FPS SCORECARD & SMOOTH AREA GRAPH ── */}
402
411
  <View style={perfStyles.heroCard}>
403
412
  <View style={perfStyles.heroHeaderRow}>
404
- <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
405
- <PerformanceIcon color={AppColors.purple} size={17}/>
406
- <View>
407
- <Text style={perfStyles.heroTitle}>
413
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 8, flex: 1 }}>
414
+ <PerformanceIcon color={AppColors.purple} size={18}/>
415
+ <View style={{ flex: 1 }}>
416
+ <Text style={perfStyles.heroTitle} numberOfLines={1}>
408
417
  {t('performance.fpsAreaChartTitle')}
409
418
  </Text>
410
- <Text style={perfStyles.heroSubTitle}>
419
+ <Text style={perfStyles.heroSubTitle} numberOfLines={1}>
411
420
  {t('performance.fpsAreaChartSub')}
412
421
  </Text>
413
422
  </View>
@@ -1122,18 +1131,21 @@ const perfStyles = StyleSheet.create({
1122
1131
  color: AppColors.grayTextWeak,
1123
1132
  },
1124
1133
  fpsStatsGrid: {
1134
+ flex: 1,
1125
1135
  flexDirection: 'row',
1126
1136
  alignItems: 'center',
1127
- gap: 8,
1137
+ justifyContent: 'space-around',
1138
+ marginLeft: 8,
1128
1139
  },
1129
1140
  statItem: {
1130
1141
  alignItems: 'center',
1131
- minWidth: 50,
1142
+ flex: 1,
1132
1143
  },
1133
1144
  statLabel: {
1134
- fontFamily: AppFonts.interRegular,
1135
- fontSize: 10,
1145
+ fontFamily: AppFonts.interMedium,
1146
+ fontSize: 9.5,
1136
1147
  color: AppColors.grayTextWeak,
1148
+ textAlign: 'center',
1137
1149
  },
1138
1150
  statValue: {
1139
1151
  fontFamily: AppFonts.interBold,
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ import { NetworkLog } from '../../types';
3
+ interface RequestReplayModalProps {
4
+ visible: boolean;
5
+ onClose: () => void;
6
+ request: NetworkLog;
7
+ }
8
+ export declare const RequestReplayModal: React.FC<RequestReplayModalProps>;
9
+ export default RequestReplayModal;