react-native-inapp-inspector 1.1.21 → 1.1.22

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 (37) hide show
  1. package/dist/commonjs/components/AnalyticsDetail.js +5 -3
  2. package/dist/commonjs/components/AnalyticsEventCard.d.ts +0 -1
  3. package/dist/commonjs/components/AnalyticsEventCard.js +85 -96
  4. package/dist/commonjs/components/Inspector/BundleTab.js +486 -651
  5. package/dist/commonjs/components/Inspector/PerformanceTab.js +185 -244
  6. package/dist/commonjs/components/Inspector/ReduxDetail.js +2 -2
  7. package/dist/commonjs/components/Inspector/ReduxTab.js +3 -3
  8. package/dist/commonjs/constants/version.d.ts +1 -1
  9. package/dist/commonjs/constants/version.js +1 -1
  10. package/dist/commonjs/customHooks/bundleAnalyzer.d.ts +115 -0
  11. package/dist/commonjs/customHooks/bundleAnalyzer.js +561 -0
  12. package/dist/commonjs/customHooks/performanceTracker.d.ts +84 -0
  13. package/dist/commonjs/customHooks/performanceTracker.js +541 -0
  14. package/dist/commonjs/helpers/index.d.ts +15 -0
  15. package/dist/commonjs/helpers/index.js +112 -1
  16. package/dist/commonjs/i18n/locales/en.json +225 -2
  17. package/dist/commonjs/styles/AppColors.d.ts +110 -0
  18. package/dist/commonjs/styles/AppColors.js +114 -0
  19. package/dist/esm/components/AnalyticsDetail.js +5 -3
  20. package/dist/esm/components/AnalyticsEventCard.d.ts +0 -1
  21. package/dist/esm/components/AnalyticsEventCard.js +85 -95
  22. package/dist/esm/components/Inspector/BundleTab.js +489 -654
  23. package/dist/esm/components/Inspector/PerformanceTab.js +185 -244
  24. package/dist/esm/components/Inspector/ReduxDetail.js +2 -2
  25. package/dist/esm/components/Inspector/ReduxTab.js +3 -3
  26. package/dist/esm/constants/version.d.ts +1 -1
  27. package/dist/esm/constants/version.js +1 -1
  28. package/dist/esm/customHooks/bundleAnalyzer.d.ts +115 -0
  29. package/dist/esm/customHooks/bundleAnalyzer.js +554 -0
  30. package/dist/esm/customHooks/performanceTracker.d.ts +84 -0
  31. package/dist/esm/customHooks/performanceTracker.js +537 -0
  32. package/dist/esm/helpers/index.d.ts +15 -0
  33. package/dist/esm/helpers/index.js +107 -0
  34. package/dist/esm/i18n/locales/en.json +225 -2
  35. package/dist/esm/styles/AppColors.d.ts +110 -0
  36. package/dist/esm/styles/AppColors.js +114 -0
  37. package/package.json +1 -1
@@ -6,160 +6,11 @@ import HighlightText from '../HighlightText';
6
6
  import EndOfListFooter from '../EndOfListFooter';
7
7
  import { AppColors } from '../../styles/AppColors';
8
8
  import { AppFonts } from '../../styles/AppFonts';
9
+ import { useTranslation } from '../../i18n';
10
+ import { getRuntimeDiagnostics } from '../../helpers';
9
11
  import { SearchIcon, CircleXIcon, PerformanceIcon } from '../NetworkIcons';
10
- const INITIAL_PERFORMANCE_EVENTS = [
11
- {
12
- id: 'perf-1',
13
- timestamp: Date.now() - 48000,
14
- type: 'fps_drop',
15
- category: 'navigation',
16
- fps: 38,
17
- durationMs: 26.3,
18
- label: 'Main Thread Spike during Navigation',
19
- detail: 'Screen transition to ProductDetailScreen triggered heavy layout reconciliation and simultaneous component mounts.',
20
- source: 'src/navigation/RootNavigator.tsx',
21
- breakdown: { jsTimeMs: 18.2, uiTimeMs: 8.1, bridgeLatencyMs: 1.2 },
22
- heapDeltaKb: 640,
23
- advice: 'Defer non-critical offscreen hooks with InteractionManager.runAfterInteractions to preserve 60 FPS.',
24
- severity: 'warning',
25
- },
26
- {
27
- id: 'perf-2',
28
- timestamp: Date.now() - 41000,
29
- type: 'slow_render',
30
- category: 'render',
31
- fps: 42,
32
- durationMs: 23.8,
33
- label: 'FlatList Virtualization Re-render Pass',
34
- detail: 'FlatList rendered 25 items simultaneously on orientation change without memoized row component.',
35
- source: 'src/components/Inspector/NetworkTab.tsx',
36
- breakdown: { jsTimeMs: 16.4, uiTimeMs: 7.4 },
37
- heapDeltaKb: 380,
38
- advice: 'Implement getItemLayout and React.memo(LogCard) to skip redundant diffing passes.',
39
- severity: 'warning',
40
- },
41
- {
42
- id: 'perf-3',
43
- timestamp: Date.now() - 35000,
44
- type: 'transition',
45
- category: 'navigation',
46
- fps: 59,
47
- durationMs: 16.9,
48
- label: 'Native Modal Slide-Up Transition',
49
- detail: 'Hardware accelerated native driver animated transform running smoothly at sustained 60 FPS.',
50
- source: 'src/components/Inspector/MainScreen.tsx',
51
- breakdown: { jsTimeMs: 2.1, uiTimeMs: 14.8 },
52
- heapDeltaKb: 120,
53
- advice: 'Using nativeDriver: true successfully prevents JS thread blocking during animations.',
54
- severity: 'optimal',
55
- },
56
- {
57
- id: 'perf-4',
58
- timestamp: Date.now() - 28000,
59
- type: 'memory',
60
- category: 'memory',
61
- fps: 60,
62
- durationMs: 16.6,
63
- label: 'Hermes Generational Garbage Collection',
64
- detail: 'Minor generational GC cycle scavenged 4.2 MB ephemeral heap objects with sub-millisecond thread pause.',
65
- source: 'Hermes VM Garbage Collector',
66
- breakdown: { jsTimeMs: 3.1, uiTimeMs: 0.2 },
67
- heapDeltaKb: -4280,
68
- advice: 'Hermes generational garbage collector is operating within optimal sub-5ms limits.',
69
- severity: 'optimal',
70
- },
71
- {
72
- id: 'perf-5',
73
- timestamp: Date.now() - 22000,
74
- type: 'network',
75
- category: 'io',
76
- fps: 48,
77
- durationMs: 20.8,
78
- label: 'Large JSON Payload Deserialization',
79
- detail: '50-item API response parse overhead in network adapter (185 KB JSON raw string).',
80
- source: 'src/customHooks/networkLogger.ts',
81
- breakdown: { jsTimeMs: 15.6, uiTimeMs: 5.2, bridgeLatencyMs: 2.1 },
82
- heapDeltaKb: 890,
83
- advice: 'Consider paginating API payloads or streaming responses if payload size exceeds 250 KB.',
84
- severity: 'warning',
85
- },
86
- {
87
- id: 'perf-6',
88
- timestamp: Date.now() - 17000,
89
- type: 'slow_render',
90
- category: 'render',
91
- fps: 52,
92
- durationMs: 19.2,
93
- label: 'Image Bitmap Decode & Rasterization',
94
- detail: 'Retina raster decode for banner_dark.png (1200×630px raster buffer allocation).',
95
- source: 'src/components/Inspector/BundleTab.tsx',
96
- breakdown: { jsTimeMs: 3.4, uiTimeMs: 15.8 },
97
- heapDeltaKb: 1450,
98
- advice: 'Downscale asset dimensions or convert to WebP to reduce decode latency by ~65%.',
99
- severity: 'warning',
100
- },
101
- {
102
- id: 'perf-7',
103
- timestamp: Date.now() - 12000,
104
- type: 'bridge',
105
- category: 'bridge',
106
- fps: 60,
107
- durationMs: 16.6,
108
- label: 'Native TurboModule JSI Invocation',
109
- detail: 'AsyncStorage / MMKV preferences transaction read across 32 configuration keys.',
110
- source: 'src/helpers/settingsStore.ts',
111
- breakdown: { jsTimeMs: 1.8, uiTimeMs: 0.8, bridgeLatencyMs: 0.4 },
112
- heapDeltaKb: 45,
113
- advice: 'Direct C++ JSI Turbomodule bindings completely bypass legacy JSON bridge serialization overhead.',
114
- severity: 'optimal',
115
- },
116
- {
117
- id: 'perf-8',
118
- timestamp: Date.now() - 8000,
119
- type: 'slow_render',
120
- category: 'render',
121
- fps: 60,
122
- durationMs: 16.6,
123
- label: 'Redux Action State Tree Diffing',
124
- detail: 'Redux dispatch pass evaluated 6 reducer slices and emitted state notification in 4.8ms.',
125
- source: 'src/components/Inspector/ReduxTab.tsx',
126
- breakdown: { jsTimeMs: 4.8, uiTimeMs: 1.2 },
127
- heapDeltaKb: 180,
128
- advice: 'State tree immutability preserved. Memoized selectors prevented redundant component renders.',
129
- severity: 'optimal',
130
- },
131
- {
132
- id: 'perf-9',
133
- timestamp: Date.now() - 4000,
134
- type: 'touch',
135
- category: 'render',
136
- fps: 60,
137
- durationMs: 16.6,
138
- label: 'Touch-to-Render Event Latency',
139
- detail: 'Gesture responder dispatched tap event to TabBar button with immediate 60 FPS response.',
140
- source: 'src/components/Inspector/TabBar.tsx',
141
- breakdown: { jsTimeMs: 4.2, uiTimeMs: 2.1 },
142
- heapDeltaKb: 30,
143
- advice: 'Touch responder latency is well within standard 16.67ms frame budget.',
144
- severity: 'optimal',
145
- },
146
- {
147
- id: 'perf-10',
148
- timestamp: Date.now() - 1500,
149
- type: 'transition',
150
- category: 'render',
151
- fps: 60,
152
- durationMs: 16.6,
153
- label: 'C++ Yoga Flexbox Layout Pass',
154
- detail: 'Inspector UI multi-tab card layout recalculation and font metrics pass in C++ Yoga engine.',
155
- source: 'Yoga Flexbox Layout Engine',
156
- breakdown: { jsTimeMs: 2.8, uiTimeMs: 3.4 },
157
- heapDeltaKb: 65,
158
- advice: 'Flexbox layout constraints are cached and computed efficiently with zero reflow penalties.',
159
- severity: 'optimal',
160
- },
161
- ];
162
12
  const PerformanceTab = React.memo(() => {
13
+ const { t } = useTranslation();
163
14
  // ─── Real-Time FPS Tracking ────────────────────────────────────────────────
164
15
  const [isRecording, setIsRecording] = useState(true);
165
16
  const [currentFps, setCurrentFps] = useState(60);
@@ -184,7 +35,7 @@ const PerformanceTab = React.memo(() => {
184
35
  60, 59, 60, 60, 58, 60, 59, 60, 60, 60, 57, 60, 59, 60, 60, 58, 60, 60, 59, 60,
185
36
  ]);
186
37
  // Performance Log Events
187
- const [events, setEvents] = useState(INITIAL_PERFORMANCE_EVENTS);
38
+ const [events, setEvents] = useState([]);
188
39
  const [filterCategory, setFilterCategory] = useState('ALL');
189
40
  const [search, setSearch] = useState('');
190
41
  // ─── Live Frame Measurement Loop (1-Second Batch Interval) ────────────────
@@ -229,21 +80,22 @@ const PerformanceTab = React.memo(() => {
229
80
  });
230
81
  // Trigger log if noticeable frame drop
231
82
  if (measuredFps < 50) {
83
+ const durationStr = (1000 / measuredFps).toFixed(1);
232
84
  const newEvent = {
233
85
  id: `drop-${Date.now()}`,
234
86
  timestamp: Date.now(),
235
87
  type: 'fps_drop',
236
88
  category: 'render',
237
89
  fps: measuredFps,
238
- durationMs: Number((1000 / measuredFps).toFixed(1)),
239
- label: `Live Frame Rate Dip (${measuredFps} FPS)`,
240
- detail: `Main thread frame duration extended to ${(1000 / measuredFps).toFixed(1)}ms during view update.`,
241
- source: 'React Native UI Thread',
90
+ durationMs: Number(durationStr),
91
+ label: t('performance.liveFpsDip', { fps: measuredFps }),
92
+ detail: t('performance.liveFpsDipDetail', { duration: durationStr }),
93
+ source: t('performance.uiRenderThread'),
242
94
  breakdown: {
243
95
  jsTimeMs: Number(((1000 / measuredFps) * 0.65).toFixed(1)),
244
96
  uiTimeMs: Number(((1000 / measuredFps) * 0.35).toFixed(1)),
245
97
  },
246
- advice: 'Heavy JavaScript execution during frame pass delayed display presentation.',
98
+ advice: t('performance.liveFpsDipAdvice'),
247
99
  severity: measuredFps < 30 ? 'critical' : 'warning',
248
100
  };
249
101
  setEvents(prev => [newEvent, ...prev.slice(0, 49)]);
@@ -285,12 +137,76 @@ const PerformanceTab = React.memo(() => {
285
137
  }, [events, search, filterCategory]);
286
138
  // Overall Performance Score
287
139
  const performanceScore = useMemo(() => {
288
- if (avgFps >= 58)
289
- return { score: '98/100', grade: 'Excellent (60 FPS)', color: '#059669', bg: '#DCFCE7' };
290
- if (avgFps >= 50)
291
- return { score: '84/100', grade: 'Good (Minor Janks)', color: '#D97706', bg: '#FEF3C7' };
292
- return { score: '58/100', grade: 'Needs Optimization', color: '#DC2626', bg: '#FEE2E2' };
293
- }, [avgFps]);
140
+ if (avgFps >= 58) {
141
+ return {
142
+ score: '98/100',
143
+ grade: t('performance.excellent'),
144
+ color: AppColors.emerald600,
145
+ bg: AppColors.greenBg,
146
+ };
147
+ }
148
+ if (avgFps >= 50) {
149
+ return {
150
+ score: '84/100',
151
+ grade: t('performance.good'),
152
+ color: AppColors.firebaseOrange,
153
+ bg: AppColors.amberBg,
154
+ };
155
+ }
156
+ return {
157
+ score: '58/100',
158
+ grade: t('performance.needsOptimization'),
159
+ color: AppColors.errorColor,
160
+ bg: AppColors.errorCardBg,
161
+ };
162
+ }, [avgFps, t]);
163
+ // ─── Dynamic Runtime Environment & Engine Diagnostics ──────────────────────
164
+ const runtimeDiagnostics = useMemo(() => {
165
+ const raw = getRuntimeDiagnostics();
166
+ const engineNameMap = {
167
+ hermes: t('performance.hermesAot'),
168
+ v8: t('performance.v8Jit'),
169
+ jsc: t('performance.jscEngine'),
170
+ };
171
+ const engineSubMap = {
172
+ hermes: t('performance.bytecodeCompiled'),
173
+ v8: t('performance.jitEngine'),
174
+ jsc: t('performance.webkitEngine'),
175
+ };
176
+ const archNameMap = {
177
+ fabric: t('performance.fabricJsi'),
178
+ paper: t('performance.paperBridge'),
179
+ };
180
+ const archSubMap = {
181
+ fabric: t('performance.directCppBindings'),
182
+ paper: t('performance.asyncJsonBridge'),
183
+ };
184
+ return {
185
+ jsEngineName: engineNameMap[raw.engineType],
186
+ jsEngineSub: engineSubMap[raw.engineType],
187
+ uiArchName: archNameMap[raw.archType],
188
+ uiArchSub: archSubMap[raw.archType],
189
+ usedHeapMb: raw.usedHeapMb,
190
+ totalAllocMb: raw.totalAllocMb,
191
+ };
192
+ }, [t]);
193
+ // ─── Dynamic 16.67ms Frame Budget Utilization ─────────────────────────────
194
+ const frameBudgetStats = useMemo(() => {
195
+ const frameDuration = avgFps > 0 ? 1000 / avgFps : 16.67;
196
+ const jsTime = Number(Math.max(1.0, Math.min(frameDuration * 0.45, 2.0 + jsLagMs)).toFixed(1));
197
+ const layoutTime = Number(Math.max(1.0, frameDuration * 0.2).toFixed(1));
198
+ const renderTime = Number(Math.max(1.0, frameDuration * 0.25).toFixed(1));
199
+ const totalUsed = jsTime + layoutTime + renderTime;
200
+ const freeTime = Math.max(0, Number((16.67 - totalUsed).toFixed(1)));
201
+ const headroomPercent = Math.max(0, Math.min(100, Math.round((freeTime / 16.67) * 100)));
202
+ return {
203
+ jsTime,
204
+ layoutTime,
205
+ renderTime,
206
+ freeTime,
207
+ headroomPercent,
208
+ };
209
+ }, [avgFps, jsLagMs]);
294
210
  const clearEvents = () => {
295
211
  setEvents([]);
296
212
  };
@@ -299,7 +215,7 @@ const PerformanceTab = React.memo(() => {
299
215
  <View style={perfStyles.headerBar}>
300
216
  <View style={perfStyles.searchBox}>
301
217
  <SearchIcon color={AppColors.grayTextWeak} size={15}/>
302
- <TextInput placeholder="Search performance logs, hooks, FPS drops..." placeholderTextColor={AppColors.grayTextWeak} value={search} onChangeText={setSearch} style={perfStyles.searchInput} autoCapitalize="none" autoCorrect={false}/>
218
+ <TextInput placeholder={t('performance.searchPlaceholder')} placeholderTextColor={AppColors.grayTextWeak} value={search} onChangeText={setSearch} style={perfStyles.searchInput} autoCapitalize="none" autoCorrect={false}/>
303
219
  {search ? (<TouchableScale onPress={() => setSearch('')}>
304
220
  <CircleXIcon color={AppColors.grayTextWeak} size={16}/>
305
221
  </TouchableScale>) : null}
@@ -312,13 +228,13 @@ const PerformanceTab = React.memo(() => {
312
228
  ]}>
313
229
  <View style={[
314
230
  perfStyles.recordDot,
315
- { backgroundColor: isRecording ? '#10B981' : '#F59E0B' },
231
+ { backgroundColor: isRecording ? AppColors.emerald500 : AppColors.amber500 },
316
232
  ]}/>
317
233
  <Text style={[
318
234
  perfStyles.recordBtnText,
319
- { color: isRecording ? '#047857' : '#B45309' },
235
+ { color: isRecording ? AppColors.emerald600 : AppColors.amber700 },
320
236
  ]}>
321
- {isRecording ? 'Live' : 'Paused'}
237
+ {isRecording ? t('performance.live') : t('redux.paused')}
322
238
  </Text>
323
239
  </TouchableScale>
324
240
 
@@ -330,10 +246,10 @@ const PerformanceTab = React.memo(() => {
330
246
  jankyFrameCount,
331
247
  jsLagMs,
332
248
  platform: Platform.OS,
333
- engine: 'Hermes Bytecode AOT',
249
+ engine: runtimeDiagnostics.jsEngineName,
334
250
  totalEvents: events.length,
335
251
  events,
336
- })} label="Copy Perf JSON"/>
252
+ })} label={t('common.copyJson')}/>
337
253
  </View>
338
254
  </View>
339
255
 
@@ -341,34 +257,34 @@ const PerformanceTab = React.memo(() => {
341
257
  <View style={perfStyles.filterRow}>
342
258
  <ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={{ gap: 6, paddingHorizontal: 12, paddingVertical: 4 }}>
343
259
  {[
344
- { key: 'ALL', label: 'All Logs', icon: '⚡', count: events.length },
260
+ { key: 'ALL', label: t('performance.allLogs'), icon: '⚡', count: events.length },
345
261
  {
346
262
  key: 'JANKY',
347
- label: 'Janky / Slow',
263
+ label: t('performance.jankySlow'),
348
264
  icon: '⚠️',
349
265
  count: events.filter(e => e.severity === 'warning' || e.severity === 'critical').length,
350
266
  },
351
267
  {
352
268
  key: 'NAVIGATION',
353
- label: 'Navigation',
269
+ label: t('performance.navigation'),
354
270
  icon: '🗺️',
355
271
  count: events.filter(e => e.category === 'navigation').length,
356
272
  },
357
273
  {
358
274
  key: 'RENDER',
359
- label: 'Components',
275
+ label: t('performance.components'),
360
276
  icon: '⚛️',
361
277
  count: events.filter(e => e.category === 'render').length,
362
278
  },
363
279
  {
364
280
  key: 'MEMORY',
365
- label: 'Memory & GC',
281
+ label: t('performance.memoryGc'),
366
282
  icon: '🧠',
367
283
  count: events.filter(e => e.category === 'memory').length,
368
284
  },
369
285
  {
370
286
  key: 'IO',
371
- label: 'Network & I/O',
287
+ label: t('performance.networkIo'),
372
288
  icon: '🌐',
373
289
  count: events.filter(e => e.category === 'io' || e.category === 'bridge').length,
374
290
  },
@@ -396,7 +312,7 @@ const PerformanceTab = React.memo(() => {
396
312
  <View style={perfStyles.metricCardHeader}>
397
313
  <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
398
314
  <PerformanceIcon color={AppColors.purple} size={18}/>
399
- <Text style={perfStyles.metricCardTitle}>Real-Time Frame Rate (FPS)</Text>
315
+ <Text style={perfStyles.metricCardTitle}>{t('performance.liveFps')}</Text>
400
316
  </View>
401
317
  <View style={[
402
318
  perfStyles.scorePill,
@@ -418,36 +334,36 @@ const PerformanceTab = React.memo(() => {
418
334
  perfStyles.fpsBigNumber,
419
335
  {
420
336
  color: currentFps >= 55
421
- ? '#059669'
337
+ ? AppColors.emerald600
422
338
  : currentFps >= 40
423
- ? '#D97706'
424
- : '#DC2626',
339
+ ? AppColors.firebaseOrange
340
+ : AppColors.errorColor,
425
341
  },
426
342
  ]}>
427
343
  {currentFps}
428
344
  </Text>
429
- <Text style={perfStyles.fpsUnit}>FPS</Text>
345
+ <Text style={perfStyles.fpsUnit}>{t('performance.fpsUnit')}</Text>
430
346
  </View>
431
347
 
432
348
  <View style={perfStyles.fpsStatsGrid}>
433
349
  <View style={perfStyles.statItem}>
434
- <Text style={perfStyles.statLabel}>Avg FPS</Text>
350
+ <Text style={perfStyles.statLabel}>{t('performance.avgFrameTime')}</Text>
435
351
  <Text style={perfStyles.statValue}>{avgFps}</Text>
436
352
  </View>
437
353
  <View style={perfStyles.statItem}>
438
- <Text style={perfStyles.statLabel}>Min FPS</Text>
439
- <Text style={[perfStyles.statValue, minFps < 50 && { color: '#D97706' }]}>
354
+ <Text style={perfStyles.statLabel}>{t('performance.peakTime')}</Text>
355
+ <Text style={[perfStyles.statValue, minFps < 50 && { color: AppColors.firebaseOrange }]}>
440
356
  {minFps}
441
357
  </Text>
442
358
  </View>
443
359
  <View style={perfStyles.statItem}>
444
- <Text style={perfStyles.statLabel}>JS Lag</Text>
445
- <Text style={[perfStyles.statValue, jsLagMs > 5 && { color: '#D97706' }]}>
360
+ <Text style={perfStyles.statLabel}>{t('performance.jsLag')}</Text>
361
+ <Text style={[perfStyles.statValue, jsLagMs > 5 && { color: AppColors.firebaseOrange }]}>
446
362
  {jsLagMs}ms
447
363
  </Text>
448
364
  </View>
449
365
  <View style={perfStyles.statItem}>
450
- <Text style={perfStyles.statLabel}>Jank Rate</Text>
366
+ <Text style={perfStyles.statLabel}>{t('performance.jankRate')}</Text>
451
367
  <Text style={perfStyles.statValue}>
452
368
  {totalFrames > 0
453
369
  ? `${((jankyFrameCount / Math.max(1, totalFrames / 60)) * 100).toFixed(0)}%`
@@ -467,50 +383,73 @@ const PerformanceTab = React.memo(() => {
467
383
  perfStyles.sparklineBar,
468
384
  {
469
385
  height: barHeight,
470
- backgroundColor: isLow ? '#EF4444' : '#10B981',
386
+ backgroundColor: isLow ? AppColors.errorColor : AppColors.emerald500,
471
387
  },
472
388
  ]}/>
473
389
  </View>);
474
390
  })}
475
391
  </View>
476
392
  <View style={perfStyles.sparklineFooter}>
477
- <Text style={perfStyles.sparklineFooterText}>Last 20s live timeline</Text>
478
- <Text style={perfStyles.sparklineFooterTarget}>60 FPS Target (16.67ms/frame)</Text>
393
+ <Text style={perfStyles.sparklineFooterText}>{t('performance.realtimeWindow')}</Text>
394
+ <Text style={perfStyles.sparklineFooterTarget}>{t('performance.fpsTarget')}</Text>
479
395
  </View>
480
396
  </View>
481
397
 
482
398
  {/* ── 2. FRAME BUDGET BREAKDOWN CARD ── */}
483
399
  <View style={perfStyles.budgetCard}>
484
400
  <View style={perfStyles.budgetHeader}>
485
- <Text style={perfStyles.budgetTitle}>16.67ms Frame Budget Utilization</Text>
486
- <Text style={perfStyles.budgetHeadroom}>~4.3ms Headroom (26%)</Text>
401
+ <Text style={perfStyles.budgetTitle}>{t('performance.budgetUsage')}</Text>
402
+ <Text style={perfStyles.budgetHeadroom}>
403
+ {t('performance.budgetHeadroom', {
404
+ time: frameBudgetStats.freeTime,
405
+ percent: frameBudgetStats.headroomPercent,
406
+ })}
407
+ </Text>
487
408
  </View>
488
409
 
489
410
  {/* Multi-colored segmented frame budget bar */}
490
411
  <View style={perfStyles.budgetBarContainer}>
491
- <View style={[perfStyles.budgetBarSegment, { flex: 4.8, backgroundColor: '#38BDF8' }]}/>
492
- <View style={[perfStyles.budgetBarSegment, { flex: 2.8, backgroundColor: '#818CF8' }]}/>
493
- <View style={[perfStyles.budgetBarSegment, { flex: 4.8, backgroundColor: '#F472B6' }]}/>
494
- <View style={[perfStyles.budgetBarSegment, { flex: 4.3, backgroundColor: '#34D399' }]}/>
412
+ <View style={[
413
+ perfStyles.budgetBarSegment,
414
+ { flex: Math.max(0.5, frameBudgetStats.jsTime), backgroundColor: AppColors.sky400 },
415
+ ]}/>
416
+ <View style={[
417
+ perfStyles.budgetBarSegment,
418
+ { flex: Math.max(0.5, frameBudgetStats.layoutTime), backgroundColor: AppColors.indigo500 },
419
+ ]}/>
420
+ <View style={[
421
+ perfStyles.budgetBarSegment,
422
+ { flex: Math.max(0.5, frameBudgetStats.renderTime), backgroundColor: AppColors.pink400 },
423
+ ]}/>
424
+ <View style={[
425
+ perfStyles.budgetBarSegment,
426
+ { flex: Math.max(0.5, frameBudgetStats.freeTime), backgroundColor: AppColors.emerald400 },
427
+ ]}/>
495
428
  </View>
496
429
 
497
430
  <View style={perfStyles.budgetLegendGrid}>
498
431
  <View style={perfStyles.legendItem}>
499
- <View style={[perfStyles.legendDot, { backgroundColor: '#38BDF8' }]}/>
500
- <Text style={perfStyles.legendText}>JS Exec: 4.8ms</Text>
432
+ <View style={[perfStyles.legendDot, { backgroundColor: AppColors.sky400 }]}/>
433
+ <Text style={perfStyles.legendText}>
434
+ {t('performance.jsExec', { time: frameBudgetStats.jsTime })}
435
+ </Text>
501
436
  </View>
502
437
  <View style={perfStyles.legendItem}>
503
- <View style={[perfStyles.legendDot, { backgroundColor: '#818CF8' }]}/>
504
- <Text style={perfStyles.legendText}>Yoga Layout: 2.8ms</Text>
438
+ <View style={[perfStyles.legendDot, { backgroundColor: AppColors.indigo500 }]}/>
439
+ <Text style={perfStyles.legendText}>
440
+ {t('performance.yogaLayoutTime', { time: frameBudgetStats.layoutTime })}
441
+ </Text>
505
442
  </View>
506
443
  <View style={perfStyles.legendItem}>
507
- <View style={[perfStyles.legendDot, { backgroundColor: '#F472B6' }]}/>
508
- <Text style={perfStyles.legendText}>UI Render: 4.8ms</Text>
444
+ <View style={[perfStyles.legendDot, { backgroundColor: AppColors.pink400 }]}/>
445
+ <Text style={perfStyles.legendText}>
446
+ {t('performance.uiRenderTime', { time: frameBudgetStats.renderTime })}
447
+ </Text>
509
448
  </View>
510
449
  <View style={perfStyles.legendItem}>
511
- <View style={[perfStyles.legendDot, { backgroundColor: '#34D399' }]}/>
512
- <Text style={[perfStyles.legendText, { color: '#059669', fontFamily: AppFonts.interBold }]}>
513
- Free: 4.3ms
450
+ <View style={[perfStyles.legendDot, { backgroundColor: AppColors.emerald400 }]}/>
451
+ <Text style={[perfStyles.legendText, { color: AppColors.emerald600, fontFamily: AppFonts.interBold }]}>
452
+ {t('performance.freeTime', { time: frameBudgetStats.freeTime })}
514
453
  </Text>
515
454
  </View>
516
455
  </View>
@@ -519,49 +458,51 @@ const PerformanceTab = React.memo(() => {
519
458
  {/* ── 3. RUNTIME ENGINE & MEMORY SUMMARY ── */}
520
459
  <View style={perfStyles.engineSummaryGrid}>
521
460
  <View style={perfStyles.engineSummaryCard}>
522
- <Text style={perfStyles.engineSummaryLabel}>JS Engine</Text>
523
- <Text style={perfStyles.engineSummaryValue}>Hermes (AOT)</Text>
524
- <Text style={perfStyles.engineSummarySub}>Bytecode compiled</Text>
461
+ <Text style={perfStyles.engineSummaryLabel}>{t('performance.jsEngine')}</Text>
462
+ <Text style={perfStyles.engineSummaryValue}>{runtimeDiagnostics.jsEngineName}</Text>
463
+ <Text style={perfStyles.engineSummarySub}>{runtimeDiagnostics.jsEngineSub}</Text>
525
464
  </View>
526
465
  <View style={perfStyles.engineSummaryCard}>
527
- <Text style={perfStyles.engineSummaryLabel}>UI Reconciler</Text>
528
- <Text style={perfStyles.engineSummaryValue}>Fabric / JSI</Text>
529
- <Text style={perfStyles.engineSummarySub}>Direct C++ bindings</Text>
466
+ <Text style={perfStyles.engineSummaryLabel}>{t('performance.uiRenderThread')}</Text>
467
+ <Text style={perfStyles.engineSummaryValue}>{runtimeDiagnostics.uiArchName}</Text>
468
+ <Text style={perfStyles.engineSummarySub}>{runtimeDiagnostics.uiArchSub}</Text>
530
469
  </View>
531
470
  <View style={perfStyles.engineSummaryCard}>
532
- <Text style={perfStyles.engineSummaryLabel}>Memory Heap</Text>
533
- <Text style={perfStyles.engineSummaryValue}>34.8 MB</Text>
534
- <Text style={perfStyles.engineSummarySub}>64 MB allocated</Text>
471
+ <Text style={perfStyles.engineSummaryLabel}>{t('performance.tabMemory')}</Text>
472
+ <Text style={perfStyles.engineSummaryValue}>{runtimeDiagnostics.usedHeapMb} MB</Text>
473
+ <Text style={perfStyles.engineSummarySub}>
474
+ {t('performance.heapAllocatedSub', { allocated: runtimeDiagnostics.totalAllocMb })}
475
+ </Text>
535
476
  </View>
536
477
  </View>
537
478
 
538
479
  {/* ── 4. PERFORMANCE LOGS & DETAILED EVENTS ── */}
539
480
  <View style={perfStyles.logsHeaderRow}>
540
481
  <Text style={perfStyles.logsSectionHeading}>
541
- Performance & Frame Events ({filteredEvents.length})
482
+ {t('performance.recordedEvents')} ({filteredEvents.length})
542
483
  </Text>
543
484
  {events.length > 0 && (<TouchableScale onPress={clearEvents}>
544
- <Text style={perfStyles.clearLink}>Clear Logs</Text>
485
+ <Text style={perfStyles.clearLink}>{t('common.clear')}</Text>
545
486
  </TouchableScale>)}
546
487
  </View>
547
488
 
548
489
  {filteredEvents.length === 0 ? (<View style={perfStyles.emptyCard}>
549
490
  <Text style={{ fontSize: 28 }}>⚡</Text>
550
- <Text style={perfStyles.emptyTitle}>No Performance Drops Detected</Text>
491
+ <Text style={perfStyles.emptyTitle}>{t('performance.emptyTitle')}</Text>
551
492
  <Text style={perfStyles.emptySub}>
552
- All UI interactions, navigation screen transitions, and animations are running smoothly at 60 FPS.
493
+ {t('performance.emptySubtitle')}
553
494
  </Text>
554
495
  </View>) : (filteredEvents.map(event => {
555
496
  const isOptimal = event.severity === 'optimal';
556
497
  const isWarning = event.severity === 'warning';
557
498
  const isCritical = event.severity === 'critical';
558
499
  const isExpanded = !!expandedEventIds[event.id];
559
- const badgeBg = isOptimal ? '#DCFCE7' : isWarning ? '#FEF3C7' : '#FEE2E2';
560
- const badgeBorder = isOptimal ? '#BBF7D0' : isWarning ? '#FDE68A' : '#FECACA';
561
- const badgeColor = isOptimal ? '#047857' : isWarning ? '#B45309' : '#DC2626';
500
+ const badgeBg = isOptimal ? AppColors.greenBg : isWarning ? AppColors.amberBg : AppColors.errorCardBg;
501
+ const badgeBorder = isOptimal ? AppColors.greenBorder : isWarning ? AppColors.lightOrange : AppColors.errorBorder;
502
+ const badgeColor = isOptimal ? AppColors.emerald600 : isWarning ? AppColors.amber700 : AppColors.errorColor;
562
503
  return (<TouchableScale key={event.id} onPress={() => toggleEventExpand(event.id)} style={[
563
504
  perfStyles.eventCard,
564
- isCritical && { borderColor: '#FECACA', backgroundColor: '#FFFBFB' },
505
+ isCritical && { borderColor: AppColors.errorBorder, backgroundColor: AppColors.errorCardBg },
565
506
  ]}>
566
507
  <View style={perfStyles.eventTopRow}>
567
508
  <View style={[
@@ -576,12 +517,12 @@ const PerformanceTab = React.memo(() => {
576
517
  <View style={{ flex: 1, paddingHorizontal: 6 }}>
577
518
  <HighlightText text={event.label} search={search} style={perfStyles.eventLabel} highlightStyle={perfStyles.searchHighlight}/>
578
519
  <Text style={perfStyles.eventMeta}>
579
- {event.durationMs}ms total • {new Date(event.timestamp).toLocaleTimeString()}
520
+ {t('performance.msTotal', { duration: event.durationMs, time: new Date(event.timestamp).toLocaleTimeString() })}
580
521
  </Text>
581
522
  </View>
582
523
 
583
524
  <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
584
- <CopyButton value={() => event} label="Event JSON"/>
525
+ <CopyButton value={() => event} label={t('common.copyJson')}/>
585
526
  <Text style={perfStyles.expandChevron}>
586
527
  {isExpanded ? '▲' : '▼'}
587
528
  </Text>
@@ -594,7 +535,7 @@ const PerformanceTab = React.memo(() => {
594
535
  {isExpanded && (<View style={perfStyles.expandedDetailBox}>
595
536
  {/* Source file / hook tag */}
596
537
  {event.source && (<View style={perfStyles.sourceRow}>
597
- <Text style={perfStyles.sourceTag}>SOURCE</Text>
538
+ <Text style={perfStyles.sourceTag}>{t('common.source')}</Text>
598
539
  <Text style={perfStyles.sourceText} numberOfLines={1}>
599
540
  {event.source}
600
541
  </Text>
@@ -603,28 +544,28 @@ const PerformanceTab = React.memo(() => {
603
544
  {/* Thread Timing Breakdown */}
604
545
  {event.breakdown && (<View style={perfStyles.breakdownGrid}>
605
546
  <View style={perfStyles.breakdownCol}>
606
- <Text style={perfStyles.breakdownLabel}>JS Thread</Text>
547
+ <Text style={perfStyles.breakdownLabel}>{t('performance.jsThread')}</Text>
607
548
  <Text style={perfStyles.breakdownVal}>
608
549
  {event.breakdown.jsTimeMs} ms
609
550
  </Text>
610
551
  </View>
611
552
  <View style={perfStyles.breakdownCol}>
612
- <Text style={perfStyles.breakdownLabel}>UI Thread</Text>
553
+ <Text style={perfStyles.breakdownLabel}>{t('performance.uiThread')}</Text>
613
554
  <Text style={perfStyles.breakdownVal}>
614
555
  {event.breakdown.uiTimeMs} ms
615
556
  </Text>
616
557
  </View>
617
558
  {event.breakdown.bridgeLatencyMs != null && (<View style={perfStyles.breakdownCol}>
618
- <Text style={perfStyles.breakdownLabel}>JSI Latency</Text>
559
+ <Text style={perfStyles.breakdownLabel}>{t('performance.jsiLatency')}</Text>
619
560
  <Text style={perfStyles.breakdownVal}>
620
561
  {event.breakdown.bridgeLatencyMs} ms
621
562
  </Text>
622
563
  </View>)}
623
564
  {event.heapDeltaKb != null && (<View style={perfStyles.breakdownCol}>
624
- <Text style={perfStyles.breakdownLabel}>Heap Impact</Text>
565
+ <Text style={perfStyles.breakdownLabel}>{t('performance.heapImpact')}</Text>
625
566
  <Text style={[
626
567
  perfStyles.breakdownVal,
627
- { color: event.heapDeltaKb > 0 ? '#B45309' : '#059669' },
568
+ { color: event.heapDeltaKb > 0 ? AppColors.amber700 : AppColors.emerald600 },
628
569
  ]}>
629
570
  {event.heapDeltaKb > 0 ? `+${event.heapDeltaKb}` : event.heapDeltaKb} KB
630
571
  </Text>
@@ -633,14 +574,14 @@ const PerformanceTab = React.memo(() => {
633
574
 
634
575
  {/* Actionable Optimization Tip */}
635
576
  {event.advice && (<View style={perfStyles.adviceCard}>
636
- <Text style={perfStyles.adviceHeading}>💡 Optimization Tip:</Text>
577
+ <Text style={perfStyles.adviceHeading}>💡 {t('performance.optimizationTip')}</Text>
637
578
  <Text style={perfStyles.adviceBodyText}>{event.advice}</Text>
638
579
  </View>)}
639
580
  </View>)}
640
581
  </TouchableScale>);
641
582
  }))}
642
583
 
643
- <EndOfListFooter count={filteredEvents.length} label="performance events"/>
584
+ <EndOfListFooter count={filteredEvents.length} label={t('performance.recordedEvents')}/>
644
585
  </ScrollView>
645
586
  </View>);
646
587
  });
@@ -688,12 +629,12 @@ const perfStyles = StyleSheet.create({
688
629
  borderWidth: 1,
689
630
  },
690
631
  recordBtnActive: {
691
- backgroundColor: '#DCFCE7',
692
- borderColor: '#BBF7D0',
632
+ backgroundColor: AppColors.greenBg,
633
+ borderColor: AppColors.greenBorder,
693
634
  },
694
635
  recordBtnPaused: {
695
- backgroundColor: '#FEF3C7',
696
- borderColor: '#FDE68A',
636
+ backgroundColor: AppColors.amberBg,
637
+ borderColor: AppColors.lightOrange,
697
638
  },
698
639
  recordDot: {
699
640
  width: 6,
@@ -852,7 +793,7 @@ const perfStyles = StyleSheet.create({
852
793
  sparklineFooterTarget: {
853
794
  fontFamily: AppFonts.interMedium,
854
795
  fontSize: 10,
855
- color: '#059669',
796
+ color: AppColors.emerald600,
856
797
  },
857
798
  budgetCard: {
858
799
  backgroundColor: AppColors.white,
@@ -875,7 +816,7 @@ const perfStyles = StyleSheet.create({
875
816
  budgetHeadroom: {
876
817
  fontFamily: AppFonts.interBold,
877
818
  fontSize: 11,
878
- color: '#059669',
819
+ color: AppColors.emerald600,
879
820
  },
880
821
  budgetBarContainer: {
881
822
  flexDirection: 'row',
@@ -1056,27 +997,27 @@ const perfStyles = StyleSheet.create({
1056
997
  marginTop: 1,
1057
998
  },
1058
999
  adviceCard: {
1059
- backgroundColor: '#FAF5FF',
1000
+ backgroundColor: AppColors.purpleTintBg,
1060
1001
  borderRadius: 6,
1061
1002
  padding: 6,
1062
1003
  borderWidth: 1,
1063
- borderColor: '#E9D5FF',
1004
+ borderColor: AppColors.purpleBorder,
1064
1005
  },
1065
1006
  adviceHeading: {
1066
1007
  fontFamily: AppFonts.interBold,
1067
1008
  fontSize: 10,
1068
- color: '#6B21A8',
1009
+ color: AppColors.purpleShade700,
1069
1010
  },
1070
1011
  adviceBodyText: {
1071
1012
  fontFamily: AppFonts.interRegular,
1072
1013
  fontSize: 10.5,
1073
- color: '#7E22CE',
1014
+ color: AppColors.purple,
1074
1015
  lineHeight: 14,
1075
1016
  marginTop: 1,
1076
1017
  },
1077
1018
  searchHighlight: {
1078
- backgroundColor: '#FEF08A',
1079
- color: '#854D0E',
1019
+ backgroundColor: AppColors.yellowHighlight,
1020
+ color: AppColors.amber800,
1080
1021
  fontFamily: AppFonts.interBold,
1081
1022
  },
1082
1023
  emptyCard: {