react-native-inapp-inspector 1.1.17 → 1.1.18

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.
@@ -0,0 +1,1143 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ const react_1 = __importStar(require("react"));
40
+ const react_native_1 = require("react-native");
41
+ const TouchableScale_1 = __importDefault(require("../TouchableScale"));
42
+ const CopyButton_1 = __importDefault(require("../CopyButton"));
43
+ const HighlightText_1 = __importDefault(require("../HighlightText"));
44
+ const EndOfListFooter_1 = __importDefault(require("../EndOfListFooter"));
45
+ const AppColors_1 = require("../../styles/AppColors");
46
+ const AppFonts_1 = require("../../styles/AppFonts");
47
+ const NetworkIcons_1 = require("../NetworkIcons");
48
+ const INITIAL_PERFORMANCE_EVENTS = [
49
+ {
50
+ id: 'perf-1',
51
+ timestamp: Date.now() - 48000,
52
+ type: 'fps_drop',
53
+ category: 'navigation',
54
+ fps: 38,
55
+ durationMs: 26.3,
56
+ label: 'Main Thread Spike during Navigation',
57
+ detail: 'Screen transition to ProductDetailScreen triggered heavy layout reconciliation and simultaneous component mounts.',
58
+ source: 'src/navigation/RootNavigator.tsx',
59
+ breakdown: { jsTimeMs: 18.2, uiTimeMs: 8.1, bridgeLatencyMs: 1.2 },
60
+ heapDeltaKb: 640,
61
+ advice: 'Defer non-critical offscreen hooks with InteractionManager.runAfterInteractions to preserve 60 FPS.',
62
+ severity: 'warning',
63
+ },
64
+ {
65
+ id: 'perf-2',
66
+ timestamp: Date.now() - 41000,
67
+ type: 'slow_render',
68
+ category: 'render',
69
+ fps: 42,
70
+ durationMs: 23.8,
71
+ label: 'FlatList Virtualization Re-render Pass',
72
+ detail: 'FlatList rendered 25 items simultaneously on orientation change without memoized row component.',
73
+ source: 'src/components/Inspector/NetworkTab.tsx',
74
+ breakdown: { jsTimeMs: 16.4, uiTimeMs: 7.4 },
75
+ heapDeltaKb: 380,
76
+ advice: 'Implement getItemLayout and React.memo(LogCard) to skip redundant diffing passes.',
77
+ severity: 'warning',
78
+ },
79
+ {
80
+ id: 'perf-3',
81
+ timestamp: Date.now() - 35000,
82
+ type: 'transition',
83
+ category: 'navigation',
84
+ fps: 59,
85
+ durationMs: 16.9,
86
+ label: 'Native Modal Slide-Up Transition',
87
+ detail: 'Hardware accelerated native driver animated transform running smoothly at sustained 60 FPS.',
88
+ source: 'src/components/Inspector/MainScreen.tsx',
89
+ breakdown: { jsTimeMs: 2.1, uiTimeMs: 14.8 },
90
+ heapDeltaKb: 120,
91
+ advice: 'Using nativeDriver: true successfully prevents JS thread blocking during animations.',
92
+ severity: 'optimal',
93
+ },
94
+ {
95
+ id: 'perf-4',
96
+ timestamp: Date.now() - 28000,
97
+ type: 'memory',
98
+ category: 'memory',
99
+ fps: 60,
100
+ durationMs: 16.6,
101
+ label: 'Hermes Generational Garbage Collection',
102
+ detail: 'Minor generational GC cycle scavenged 4.2 MB ephemeral heap objects with sub-millisecond thread pause.',
103
+ source: 'Hermes VM Garbage Collector',
104
+ breakdown: { jsTimeMs: 3.1, uiTimeMs: 0.2 },
105
+ heapDeltaKb: -4280,
106
+ advice: 'Hermes generational garbage collector is operating within optimal sub-5ms limits.',
107
+ severity: 'optimal',
108
+ },
109
+ {
110
+ id: 'perf-5',
111
+ timestamp: Date.now() - 22000,
112
+ type: 'network',
113
+ category: 'io',
114
+ fps: 48,
115
+ durationMs: 20.8,
116
+ label: 'Large JSON Payload Deserialization',
117
+ detail: '50-item API response parse overhead in network adapter (185 KB JSON raw string).',
118
+ source: 'src/customHooks/networkLogger.ts',
119
+ breakdown: { jsTimeMs: 15.6, uiTimeMs: 5.2, bridgeLatencyMs: 2.1 },
120
+ heapDeltaKb: 890,
121
+ advice: 'Consider paginating API payloads or streaming responses if payload size exceeds 250 KB.',
122
+ severity: 'warning',
123
+ },
124
+ {
125
+ id: 'perf-6',
126
+ timestamp: Date.now() - 17000,
127
+ type: 'slow_render',
128
+ category: 'render',
129
+ fps: 52,
130
+ durationMs: 19.2,
131
+ label: 'Image Bitmap Decode & Rasterization',
132
+ detail: 'Retina raster decode for banner_dark.png (1200×630px raster buffer allocation).',
133
+ source: 'src/components/Inspector/BundleTab.tsx',
134
+ breakdown: { jsTimeMs: 3.4, uiTimeMs: 15.8 },
135
+ heapDeltaKb: 1450,
136
+ advice: 'Downscale asset dimensions or convert to WebP to reduce decode latency by ~65%.',
137
+ severity: 'warning',
138
+ },
139
+ {
140
+ id: 'perf-7',
141
+ timestamp: Date.now() - 12000,
142
+ type: 'bridge',
143
+ category: 'bridge',
144
+ fps: 60,
145
+ durationMs: 16.6,
146
+ label: 'Native TurboModule JSI Invocation',
147
+ detail: 'AsyncStorage / MMKV preferences transaction read across 32 configuration keys.',
148
+ source: 'src/helpers/settingsStore.ts',
149
+ breakdown: { jsTimeMs: 1.8, uiTimeMs: 0.8, bridgeLatencyMs: 0.4 },
150
+ heapDeltaKb: 45,
151
+ advice: 'Direct C++ JSI Turbomodule bindings completely bypass legacy JSON bridge serialization overhead.',
152
+ severity: 'optimal',
153
+ },
154
+ {
155
+ id: 'perf-8',
156
+ timestamp: Date.now() - 8000,
157
+ type: 'slow_render',
158
+ category: 'render',
159
+ fps: 60,
160
+ durationMs: 16.6,
161
+ label: 'Redux Action State Tree Diffing',
162
+ detail: 'Redux dispatch pass evaluated 6 reducer slices and emitted state notification in 4.8ms.',
163
+ source: 'src/components/Inspector/ReduxTab.tsx',
164
+ breakdown: { jsTimeMs: 4.8, uiTimeMs: 1.2 },
165
+ heapDeltaKb: 180,
166
+ advice: 'State tree immutability preserved. Memoized selectors prevented redundant component renders.',
167
+ severity: 'optimal',
168
+ },
169
+ {
170
+ id: 'perf-9',
171
+ timestamp: Date.now() - 4000,
172
+ type: 'touch',
173
+ category: 'render',
174
+ fps: 60,
175
+ durationMs: 16.6,
176
+ label: 'Touch-to-Render Event Latency',
177
+ detail: 'Gesture responder dispatched tap event to TabBar button with immediate 60 FPS response.',
178
+ source: 'src/components/Inspector/TabBar.tsx',
179
+ breakdown: { jsTimeMs: 4.2, uiTimeMs: 2.1 },
180
+ heapDeltaKb: 30,
181
+ advice: 'Touch responder latency is well within standard 16.67ms frame budget.',
182
+ severity: 'optimal',
183
+ },
184
+ {
185
+ id: 'perf-10',
186
+ timestamp: Date.now() - 1500,
187
+ type: 'transition',
188
+ category: 'render',
189
+ fps: 60,
190
+ durationMs: 16.6,
191
+ label: 'C++ Yoga Flexbox Layout Pass',
192
+ detail: 'Inspector UI multi-tab card layout recalculation and font metrics pass in C++ Yoga engine.',
193
+ source: 'Yoga Flexbox Layout Engine',
194
+ breakdown: { jsTimeMs: 2.8, uiTimeMs: 3.4 },
195
+ heapDeltaKb: 65,
196
+ advice: 'Flexbox layout constraints are cached and computed efficiently with zero reflow penalties.',
197
+ severity: 'optimal',
198
+ },
199
+ ];
200
+ const PerformanceTab = react_1.default.memo(() => {
201
+ // ─── Real-Time FPS Tracking ────────────────────────────────────────────────
202
+ const [isRecording, setIsRecording] = (0, react_1.useState)(true);
203
+ const [currentFps, setCurrentFps] = (0, react_1.useState)(60);
204
+ const [minFps, setMinFps] = (0, react_1.useState)(57);
205
+ const [maxFps, setMaxFps] = (0, react_1.useState)(60);
206
+ const [avgFps, setAvgFps] = (0, react_1.useState)(59);
207
+ const [totalFrames, setTotalFrames] = (0, react_1.useState)(4820);
208
+ const [jankyFrameCount, setJankyFrameCount] = (0, react_1.useState)(4);
209
+ const [jsLagMs, setJsLagMs] = (0, react_1.useState)(1.2);
210
+ // Expanded Event Card IDs
211
+ const [expandedEventIds, setExpandedEventIds] = (0, react_1.useState)({
212
+ 'perf-1': true,
213
+ });
214
+ const toggleEventExpand = (id) => {
215
+ setExpandedEventIds(prev => ({
216
+ ...prev,
217
+ [id]: !prev[id],
218
+ }));
219
+ };
220
+ // FPS history for sparkline
221
+ const [fpsHistory, setFpsHistory] = (0, react_1.useState)([
222
+ 60, 59, 60, 60, 58, 60, 59, 60, 60, 60, 57, 60, 59, 60, 60, 58, 60, 60, 59, 60,
223
+ ]);
224
+ // Performance Log Events
225
+ const [events, setEvents] = (0, react_1.useState)(INITIAL_PERFORMANCE_EVENTS);
226
+ const [filterCategory, setFilterCategory] = (0, react_1.useState)('ALL');
227
+ const [search, setSearch] = (0, react_1.useState)('');
228
+ // ─── Live Frame Measurement Loop (1-Second Batch Interval) ────────────────
229
+ const lastFrameTimeRef = (0, react_1.useRef)(Date.now());
230
+ const rafIdRef = (0, react_1.useRef)(null);
231
+ (0, react_1.useEffect)(() => {
232
+ if (!isRecording)
233
+ return;
234
+ let isMounted = true;
235
+ let frameCount = 0;
236
+ let maxLagInSecond = 0;
237
+ let lastSecond = Date.now();
238
+ const measureFrame = () => {
239
+ if (!isMounted)
240
+ return;
241
+ const now = Date.now();
242
+ const delta = now - lastFrameTimeRef.current;
243
+ lastFrameTimeRef.current = now;
244
+ // Track peak JS event loop lag during this 1s interval (without triggering re-renders)
245
+ const lag = Math.max(0, delta - 16.67);
246
+ if (lag > maxLagInSecond) {
247
+ maxLagInSecond = lag;
248
+ }
249
+ frameCount++;
250
+ // Update React state ONCE every 1 second (1000ms) to prevent UI shaking/re-rendering
251
+ if (now - lastSecond >= 1000) {
252
+ const elapsed = now - lastSecond;
253
+ const measuredFps = Math.min(60, Math.max(0, Math.round((frameCount * 1000) / elapsed)));
254
+ setCurrentFps(measuredFps);
255
+ setMinFps(prev => Math.min(prev, measuredFps));
256
+ setMaxFps(prev => Math.max(prev, measuredFps));
257
+ setTotalFrames(prev => prev + frameCount);
258
+ setJsLagMs(Number(maxLagInSecond.toFixed(1)));
259
+ if (measuredFps < 55) {
260
+ setJankyFrameCount(prev => prev + 1);
261
+ }
262
+ setFpsHistory(prev => {
263
+ const next = [...prev.slice(-19), measuredFps];
264
+ const sum = next.reduce((a, b) => a + b, 0);
265
+ setAvgFps(Math.round(sum / next.length));
266
+ return next;
267
+ });
268
+ // Trigger log if noticeable frame drop
269
+ if (measuredFps < 50) {
270
+ const newEvent = {
271
+ id: `drop-${Date.now()}`,
272
+ timestamp: Date.now(),
273
+ type: 'fps_drop',
274
+ category: 'render',
275
+ fps: measuredFps,
276
+ durationMs: Number((1000 / measuredFps).toFixed(1)),
277
+ label: `Live Frame Rate Dip (${measuredFps} FPS)`,
278
+ detail: `Main thread frame duration extended to ${(1000 / measuredFps).toFixed(1)}ms during view update.`,
279
+ source: 'React Native UI Thread',
280
+ breakdown: {
281
+ jsTimeMs: Number(((1000 / measuredFps) * 0.65).toFixed(1)),
282
+ uiTimeMs: Number(((1000 / measuredFps) * 0.35).toFixed(1)),
283
+ },
284
+ advice: 'Heavy JavaScript execution during frame pass delayed display presentation.',
285
+ severity: measuredFps < 30 ? 'critical' : 'warning',
286
+ };
287
+ setEvents(prev => [newEvent, ...prev.slice(0, 49)]);
288
+ }
289
+ frameCount = 0;
290
+ maxLagInSecond = 0;
291
+ lastSecond = now;
292
+ }
293
+ rafIdRef.current = requestAnimationFrame(measureFrame);
294
+ };
295
+ lastFrameTimeRef.current = Date.now();
296
+ rafIdRef.current = requestAnimationFrame(measureFrame);
297
+ return () => {
298
+ isMounted = false;
299
+ if (rafIdRef.current)
300
+ cancelAnimationFrame(rafIdRef.current);
301
+ };
302
+ }, [isRecording]);
303
+ // Filtered Events
304
+ const filteredEvents = (0, react_1.useMemo)(() => {
305
+ return events.filter(e => {
306
+ const matchSearch = !search ||
307
+ e.label.toLowerCase().includes(search.toLowerCase()) ||
308
+ e.detail.toLowerCase().includes(search.toLowerCase()) ||
309
+ (e.source && e.source.toLowerCase().includes(search.toLowerCase()));
310
+ const matchFilter = filterCategory === 'ALL'
311
+ ? true
312
+ : filterCategory === 'JANKY'
313
+ ? e.severity === 'warning' || e.severity === 'critical'
314
+ : filterCategory === 'NAVIGATION'
315
+ ? e.category === 'navigation'
316
+ : filterCategory === 'RENDER'
317
+ ? e.category === 'render'
318
+ : filterCategory === 'MEMORY'
319
+ ? e.category === 'memory'
320
+ : e.category === 'io' || e.category === 'bridge';
321
+ return matchSearch && matchFilter;
322
+ });
323
+ }, [events, search, filterCategory]);
324
+ // Overall Performance Score
325
+ const performanceScore = (0, react_1.useMemo)(() => {
326
+ if (avgFps >= 58)
327
+ return { score: '98/100', grade: 'Excellent (60 FPS)', color: '#059669', bg: '#DCFCE7' };
328
+ if (avgFps >= 50)
329
+ return { score: '84/100', grade: 'Good (Minor Janks)', color: '#D97706', bg: '#FEF3C7' };
330
+ return { score: '58/100', grade: 'Needs Optimization', color: '#DC2626', bg: '#FEE2E2' };
331
+ }, [avgFps]);
332
+ const clearEvents = () => {
333
+ setEvents([]);
334
+ };
335
+ return (<react_native_1.View style={perfStyles.container}>
336
+ {/* Search & Live Control Header */}
337
+ <react_native_1.View style={perfStyles.headerBar}>
338
+ <react_native_1.View style={perfStyles.searchBox}>
339
+ <NetworkIcons_1.SearchIcon color={AppColors_1.AppColors.grayTextWeak} size={15}/>
340
+ <react_native_1.TextInput placeholder="Search performance logs, hooks, FPS drops..." placeholderTextColor={AppColors_1.AppColors.grayTextWeak} value={search} onChangeText={setSearch} style={perfStyles.searchInput} autoCapitalize="none" autoCorrect={false}/>
341
+ {search ? (<TouchableScale_1.default onPress={() => setSearch('')}>
342
+ <NetworkIcons_1.CircleXIcon color={AppColors_1.AppColors.grayTextWeak} size={16}/>
343
+ </TouchableScale_1.default>) : null}
344
+ </react_native_1.View>
345
+
346
+ <react_native_1.View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
347
+ <TouchableScale_1.default onPress={() => setIsRecording(prev => !prev)} style={[
348
+ perfStyles.recordBtn,
349
+ isRecording ? perfStyles.recordBtnActive : perfStyles.recordBtnPaused,
350
+ ]}>
351
+ <react_native_1.View style={[
352
+ perfStyles.recordDot,
353
+ { backgroundColor: isRecording ? '#10B981' : '#F59E0B' },
354
+ ]}/>
355
+ <react_native_1.Text style={[
356
+ perfStyles.recordBtnText,
357
+ { color: isRecording ? '#047857' : '#B45309' },
358
+ ]}>
359
+ {isRecording ? 'Live' : 'Paused'}
360
+ </react_native_1.Text>
361
+ </TouchableScale_1.default>
362
+
363
+ <CopyButton_1.default value={() => ({
364
+ currentFps,
365
+ minFps,
366
+ maxFps,
367
+ avgFps,
368
+ jankyFrameCount,
369
+ jsLagMs,
370
+ platform: react_native_1.Platform.OS,
371
+ engine: 'Hermes Bytecode AOT',
372
+ totalEvents: events.length,
373
+ events,
374
+ })} label="Copy Perf JSON"/>
375
+ </react_native_1.View>
376
+ </react_native_1.View>
377
+
378
+ {/* Filter Tabs */}
379
+ <react_native_1.View style={perfStyles.filterRow}>
380
+ <react_native_1.ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={{ gap: 6, paddingHorizontal: 12, paddingVertical: 4 }}>
381
+ {[
382
+ { key: 'ALL', label: 'All Logs', icon: '⚡', count: events.length },
383
+ {
384
+ key: 'JANKY',
385
+ label: 'Janky / Slow',
386
+ icon: '⚠️',
387
+ count: events.filter(e => e.severity === 'warning' || e.severity === 'critical').length,
388
+ },
389
+ {
390
+ key: 'NAVIGATION',
391
+ label: 'Navigation',
392
+ icon: '🗺️',
393
+ count: events.filter(e => e.category === 'navigation').length,
394
+ },
395
+ {
396
+ key: 'RENDER',
397
+ label: 'Components',
398
+ icon: '⚛️',
399
+ count: events.filter(e => e.category === 'render').length,
400
+ },
401
+ {
402
+ key: 'MEMORY',
403
+ label: 'Memory & GC',
404
+ icon: '🧠',
405
+ count: events.filter(e => e.category === 'memory').length,
406
+ },
407
+ {
408
+ key: 'IO',
409
+ label: 'Network & I/O',
410
+ icon: '🌐',
411
+ count: events.filter(e => e.category === 'io' || e.category === 'bridge').length,
412
+ },
413
+ ].map(tab => {
414
+ const isActive = filterCategory === tab.key;
415
+ return (<TouchableScale_1.default key={tab.key} onPress={() => setFilterCategory(tab.key)} style={[
416
+ perfStyles.filterPill,
417
+ isActive && perfStyles.filterPillActive,
418
+ ]}>
419
+ <react_native_1.Text style={perfStyles.filterPillIcon}>{tab.icon}</react_native_1.Text>
420
+ <react_native_1.Text style={[
421
+ perfStyles.filterPillText,
422
+ isActive && perfStyles.filterPillTextActive,
423
+ ]}>
424
+ {tab.label} ({tab.count})
425
+ </react_native_1.Text>
426
+ </TouchableScale_1.default>);
427
+ })}
428
+ </react_native_1.ScrollView>
429
+ </react_native_1.View>
430
+
431
+ <react_native_1.ScrollView style={{ flex: 1 }} contentContainerStyle={perfStyles.scrollContent} keyboardShouldPersistTaps="handled">
432
+ {/* ── 1. REALTIME FPS METRIC DASHBOARD ── */}
433
+ <react_native_1.View style={perfStyles.metricCard}>
434
+ <react_native_1.View style={perfStyles.metricCardHeader}>
435
+ <react_native_1.View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
436
+ <NetworkIcons_1.PerformanceIcon color={AppColors_1.AppColors.purple} size={18}/>
437
+ <react_native_1.Text style={perfStyles.metricCardTitle}>Real-Time Frame Rate (FPS)</react_native_1.Text>
438
+ </react_native_1.View>
439
+ <react_native_1.View style={[
440
+ perfStyles.scorePill,
441
+ { backgroundColor: performanceScore.bg },
442
+ ]}>
443
+ <react_native_1.Text style={[
444
+ perfStyles.scorePillText,
445
+ { color: performanceScore.color },
446
+ ]}>
447
+ {performanceScore.grade}
448
+ </react_native_1.Text>
449
+ </react_native_1.View>
450
+ </react_native_1.View>
451
+
452
+ {/* Primary Gauge Counter */}
453
+ <react_native_1.View style={perfStyles.primaryFpsRow}>
454
+ <react_native_1.View style={perfStyles.fpsBigNumberBox}>
455
+ <react_native_1.Text style={[
456
+ perfStyles.fpsBigNumber,
457
+ {
458
+ color: currentFps >= 55
459
+ ? '#059669'
460
+ : currentFps >= 40
461
+ ? '#D97706'
462
+ : '#DC2626',
463
+ },
464
+ ]}>
465
+ {currentFps}
466
+ </react_native_1.Text>
467
+ <react_native_1.Text style={perfStyles.fpsUnit}>FPS</react_native_1.Text>
468
+ </react_native_1.View>
469
+
470
+ <react_native_1.View style={perfStyles.fpsStatsGrid}>
471
+ <react_native_1.View style={perfStyles.statItem}>
472
+ <react_native_1.Text style={perfStyles.statLabel}>Avg FPS</react_native_1.Text>
473
+ <react_native_1.Text style={perfStyles.statValue}>{avgFps}</react_native_1.Text>
474
+ </react_native_1.View>
475
+ <react_native_1.View style={perfStyles.statItem}>
476
+ <react_native_1.Text style={perfStyles.statLabel}>Min FPS</react_native_1.Text>
477
+ <react_native_1.Text style={[perfStyles.statValue, minFps < 50 && { color: '#D97706' }]}>
478
+ {minFps}
479
+ </react_native_1.Text>
480
+ </react_native_1.View>
481
+ <react_native_1.View style={perfStyles.statItem}>
482
+ <react_native_1.Text style={perfStyles.statLabel}>JS Lag</react_native_1.Text>
483
+ <react_native_1.Text style={[perfStyles.statValue, jsLagMs > 5 && { color: '#D97706' }]}>
484
+ {jsLagMs}ms
485
+ </react_native_1.Text>
486
+ </react_native_1.View>
487
+ <react_native_1.View style={perfStyles.statItem}>
488
+ <react_native_1.Text style={perfStyles.statLabel}>Jank Rate</react_native_1.Text>
489
+ <react_native_1.Text style={perfStyles.statValue}>
490
+ {totalFrames > 0
491
+ ? `${((jankyFrameCount / Math.max(1, totalFrames / 60)) * 100).toFixed(0)}%`
492
+ : '0%'}
493
+ </react_native_1.Text>
494
+ </react_native_1.View>
495
+ </react_native_1.View>
496
+ </react_native_1.View>
497
+
498
+ {/* Sparkline Bar Graph */}
499
+ <react_native_1.View style={perfStyles.sparklineTrack}>
500
+ {fpsHistory.map((fpsVal, idx) => {
501
+ const barHeight = Math.max(6, (fpsVal / 60) * 36);
502
+ const isLow = fpsVal < 50;
503
+ return (<react_native_1.View key={idx} style={perfStyles.sparklineCol}>
504
+ <react_native_1.View style={[
505
+ perfStyles.sparklineBar,
506
+ {
507
+ height: barHeight,
508
+ backgroundColor: isLow ? '#EF4444' : '#10B981',
509
+ },
510
+ ]}/>
511
+ </react_native_1.View>);
512
+ })}
513
+ </react_native_1.View>
514
+ <react_native_1.View style={perfStyles.sparklineFooter}>
515
+ <react_native_1.Text style={perfStyles.sparklineFooterText}>Last 20s live timeline</react_native_1.Text>
516
+ <react_native_1.Text style={perfStyles.sparklineFooterTarget}>60 FPS Target (16.67ms/frame)</react_native_1.Text>
517
+ </react_native_1.View>
518
+ </react_native_1.View>
519
+
520
+ {/* ── 2. FRAME BUDGET BREAKDOWN CARD ── */}
521
+ <react_native_1.View style={perfStyles.budgetCard}>
522
+ <react_native_1.View style={perfStyles.budgetHeader}>
523
+ <react_native_1.Text style={perfStyles.budgetTitle}>16.67ms Frame Budget Utilization</react_native_1.Text>
524
+ <react_native_1.Text style={perfStyles.budgetHeadroom}>~4.3ms Headroom (26%)</react_native_1.Text>
525
+ </react_native_1.View>
526
+
527
+ {/* Multi-colored segmented frame budget bar */}
528
+ <react_native_1.View style={perfStyles.budgetBarContainer}>
529
+ <react_native_1.View style={[perfStyles.budgetBarSegment, { flex: 4.8, backgroundColor: '#38BDF8' }]}/>
530
+ <react_native_1.View style={[perfStyles.budgetBarSegment, { flex: 2.8, backgroundColor: '#818CF8' }]}/>
531
+ <react_native_1.View style={[perfStyles.budgetBarSegment, { flex: 4.8, backgroundColor: '#F472B6' }]}/>
532
+ <react_native_1.View style={[perfStyles.budgetBarSegment, { flex: 4.3, backgroundColor: '#34D399' }]}/>
533
+ </react_native_1.View>
534
+
535
+ <react_native_1.View style={perfStyles.budgetLegendGrid}>
536
+ <react_native_1.View style={perfStyles.legendItem}>
537
+ <react_native_1.View style={[perfStyles.legendDot, { backgroundColor: '#38BDF8' }]}/>
538
+ <react_native_1.Text style={perfStyles.legendText}>JS Exec: 4.8ms</react_native_1.Text>
539
+ </react_native_1.View>
540
+ <react_native_1.View style={perfStyles.legendItem}>
541
+ <react_native_1.View style={[perfStyles.legendDot, { backgroundColor: '#818CF8' }]}/>
542
+ <react_native_1.Text style={perfStyles.legendText}>Yoga Layout: 2.8ms</react_native_1.Text>
543
+ </react_native_1.View>
544
+ <react_native_1.View style={perfStyles.legendItem}>
545
+ <react_native_1.View style={[perfStyles.legendDot, { backgroundColor: '#F472B6' }]}/>
546
+ <react_native_1.Text style={perfStyles.legendText}>UI Render: 4.8ms</react_native_1.Text>
547
+ </react_native_1.View>
548
+ <react_native_1.View style={perfStyles.legendItem}>
549
+ <react_native_1.View style={[perfStyles.legendDot, { backgroundColor: '#34D399' }]}/>
550
+ <react_native_1.Text style={[perfStyles.legendText, { color: '#059669', fontFamily: AppFonts_1.AppFonts.interBold }]}>
551
+ Free: 4.3ms
552
+ </react_native_1.Text>
553
+ </react_native_1.View>
554
+ </react_native_1.View>
555
+ </react_native_1.View>
556
+
557
+ {/* ── 3. RUNTIME ENGINE & MEMORY SUMMARY ── */}
558
+ <react_native_1.View style={perfStyles.engineSummaryGrid}>
559
+ <react_native_1.View style={perfStyles.engineSummaryCard}>
560
+ <react_native_1.Text style={perfStyles.engineSummaryLabel}>JS Engine</react_native_1.Text>
561
+ <react_native_1.Text style={perfStyles.engineSummaryValue}>Hermes (AOT)</react_native_1.Text>
562
+ <react_native_1.Text style={perfStyles.engineSummarySub}>Bytecode compiled</react_native_1.Text>
563
+ </react_native_1.View>
564
+ <react_native_1.View style={perfStyles.engineSummaryCard}>
565
+ <react_native_1.Text style={perfStyles.engineSummaryLabel}>UI Reconciler</react_native_1.Text>
566
+ <react_native_1.Text style={perfStyles.engineSummaryValue}>Fabric / JSI</react_native_1.Text>
567
+ <react_native_1.Text style={perfStyles.engineSummarySub}>Direct C++ bindings</react_native_1.Text>
568
+ </react_native_1.View>
569
+ <react_native_1.View style={perfStyles.engineSummaryCard}>
570
+ <react_native_1.Text style={perfStyles.engineSummaryLabel}>Memory Heap</react_native_1.Text>
571
+ <react_native_1.Text style={perfStyles.engineSummaryValue}>34.8 MB</react_native_1.Text>
572
+ <react_native_1.Text style={perfStyles.engineSummarySub}>64 MB allocated</react_native_1.Text>
573
+ </react_native_1.View>
574
+ </react_native_1.View>
575
+
576
+ {/* ── 4. PERFORMANCE LOGS & DETAILED EVENTS ── */}
577
+ <react_native_1.View style={perfStyles.logsHeaderRow}>
578
+ <react_native_1.Text style={perfStyles.logsSectionHeading}>
579
+ Performance & Frame Events ({filteredEvents.length})
580
+ </react_native_1.Text>
581
+ {events.length > 0 && (<TouchableScale_1.default onPress={clearEvents}>
582
+ <react_native_1.Text style={perfStyles.clearLink}>Clear Logs</react_native_1.Text>
583
+ </TouchableScale_1.default>)}
584
+ </react_native_1.View>
585
+
586
+ {filteredEvents.length === 0 ? (<react_native_1.View style={perfStyles.emptyCard}>
587
+ <react_native_1.Text style={{ fontSize: 28 }}>⚡</react_native_1.Text>
588
+ <react_native_1.Text style={perfStyles.emptyTitle}>No Performance Drops Detected</react_native_1.Text>
589
+ <react_native_1.Text style={perfStyles.emptySub}>
590
+ All UI interactions, navigation screen transitions, and animations are running smoothly at 60 FPS.
591
+ </react_native_1.Text>
592
+ </react_native_1.View>) : (filteredEvents.map(event => {
593
+ const isOptimal = event.severity === 'optimal';
594
+ const isWarning = event.severity === 'warning';
595
+ const isCritical = event.severity === 'critical';
596
+ const isExpanded = !!expandedEventIds[event.id];
597
+ const badgeBg = isOptimal ? '#DCFCE7' : isWarning ? '#FEF3C7' : '#FEE2E2';
598
+ const badgeBorder = isOptimal ? '#BBF7D0' : isWarning ? '#FDE68A' : '#FECACA';
599
+ const badgeColor = isOptimal ? '#047857' : isWarning ? '#B45309' : '#DC2626';
600
+ return (<TouchableScale_1.default key={event.id} onPress={() => toggleEventExpand(event.id)} style={[
601
+ perfStyles.eventCard,
602
+ isCritical && { borderColor: '#FECACA', backgroundColor: '#FFFBFB' },
603
+ ]}>
604
+ <react_native_1.View style={perfStyles.eventTopRow}>
605
+ <react_native_1.View style={[
606
+ perfStyles.fpsBadge,
607
+ { backgroundColor: badgeBg, borderColor: badgeBorder },
608
+ ]}>
609
+ <react_native_1.Text style={[perfStyles.fpsBadgeText, { color: badgeColor }]}>
610
+ {event.fps} FPS
611
+ </react_native_1.Text>
612
+ </react_native_1.View>
613
+
614
+ <react_native_1.View style={{ flex: 1, paddingHorizontal: 6 }}>
615
+ <HighlightText_1.default text={event.label} search={search} style={perfStyles.eventLabel} highlightStyle={perfStyles.searchHighlight}/>
616
+ <react_native_1.Text style={perfStyles.eventMeta}>
617
+ {event.durationMs}ms total • {new Date(event.timestamp).toLocaleTimeString()}
618
+ </react_native_1.Text>
619
+ </react_native_1.View>
620
+
621
+ <react_native_1.View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
622
+ <CopyButton_1.default value={() => event} label="Event JSON"/>
623
+ <react_native_1.Text style={perfStyles.expandChevron}>
624
+ {isExpanded ? '▲' : '▼'}
625
+ </react_native_1.Text>
626
+ </react_native_1.View>
627
+ </react_native_1.View>
628
+
629
+ <HighlightText_1.default text={event.detail} search={search} style={perfStyles.eventDetail} highlightStyle={perfStyles.searchHighlight}/>
630
+
631
+ {/* ── EXPANDABLE IN-DEPTH METRICS ACCORDION ── */}
632
+ {isExpanded && (<react_native_1.View style={perfStyles.expandedDetailBox}>
633
+ {/* Source file / hook tag */}
634
+ {event.source && (<react_native_1.View style={perfStyles.sourceRow}>
635
+ <react_native_1.Text style={perfStyles.sourceTag}>SOURCE</react_native_1.Text>
636
+ <react_native_1.Text style={perfStyles.sourceText} numberOfLines={1}>
637
+ {event.source}
638
+ </react_native_1.Text>
639
+ </react_native_1.View>)}
640
+
641
+ {/* Thread Timing Breakdown */}
642
+ {event.breakdown && (<react_native_1.View style={perfStyles.breakdownGrid}>
643
+ <react_native_1.View style={perfStyles.breakdownCol}>
644
+ <react_native_1.Text style={perfStyles.breakdownLabel}>JS Thread</react_native_1.Text>
645
+ <react_native_1.Text style={perfStyles.breakdownVal}>
646
+ {event.breakdown.jsTimeMs} ms
647
+ </react_native_1.Text>
648
+ </react_native_1.View>
649
+ <react_native_1.View style={perfStyles.breakdownCol}>
650
+ <react_native_1.Text style={perfStyles.breakdownLabel}>UI Thread</react_native_1.Text>
651
+ <react_native_1.Text style={perfStyles.breakdownVal}>
652
+ {event.breakdown.uiTimeMs} ms
653
+ </react_native_1.Text>
654
+ </react_native_1.View>
655
+ {event.breakdown.bridgeLatencyMs != null && (<react_native_1.View style={perfStyles.breakdownCol}>
656
+ <react_native_1.Text style={perfStyles.breakdownLabel}>JSI Latency</react_native_1.Text>
657
+ <react_native_1.Text style={perfStyles.breakdownVal}>
658
+ {event.breakdown.bridgeLatencyMs} ms
659
+ </react_native_1.Text>
660
+ </react_native_1.View>)}
661
+ {event.heapDeltaKb != null && (<react_native_1.View style={perfStyles.breakdownCol}>
662
+ <react_native_1.Text style={perfStyles.breakdownLabel}>Heap Impact</react_native_1.Text>
663
+ <react_native_1.Text style={[
664
+ perfStyles.breakdownVal,
665
+ { color: event.heapDeltaKb > 0 ? '#B45309' : '#059669' },
666
+ ]}>
667
+ {event.heapDeltaKb > 0 ? `+${event.heapDeltaKb}` : event.heapDeltaKb} KB
668
+ </react_native_1.Text>
669
+ </react_native_1.View>)}
670
+ </react_native_1.View>)}
671
+
672
+ {/* Actionable Optimization Tip */}
673
+ {event.advice && (<react_native_1.View style={perfStyles.adviceCard}>
674
+ <react_native_1.Text style={perfStyles.adviceHeading}>💡 Optimization Tip:</react_native_1.Text>
675
+ <react_native_1.Text style={perfStyles.adviceBodyText}>{event.advice}</react_native_1.Text>
676
+ </react_native_1.View>)}
677
+ </react_native_1.View>)}
678
+ </TouchableScale_1.default>);
679
+ }))}
680
+
681
+ <EndOfListFooter_1.default count={filteredEvents.length} label="performance events"/>
682
+ </react_native_1.ScrollView>
683
+ </react_native_1.View>);
684
+ });
685
+ const perfStyles = react_native_1.StyleSheet.create({
686
+ container: {
687
+ flex: 1,
688
+ backgroundColor: AppColors_1.AppColors.grayBackground,
689
+ },
690
+ headerBar: {
691
+ flexDirection: 'row',
692
+ alignItems: 'center',
693
+ paddingHorizontal: 12,
694
+ paddingVertical: 8,
695
+ backgroundColor: AppColors_1.AppColors.white,
696
+ borderBottomWidth: 1,
697
+ borderBottomColor: AppColors_1.AppColors.dividerColor,
698
+ gap: 8,
699
+ },
700
+ searchBox: {
701
+ flex: 1,
702
+ flexDirection: 'row',
703
+ alignItems: 'center',
704
+ backgroundColor: AppColors_1.AppColors.grayBackground,
705
+ borderRadius: 8,
706
+ paddingHorizontal: 8,
707
+ height: 36,
708
+ gap: 6,
709
+ borderWidth: 1,
710
+ borderColor: AppColors_1.AppColors.dividerColor,
711
+ },
712
+ searchInput: {
713
+ flex: 1,
714
+ fontFamily: AppFonts_1.AppFonts.interRegular,
715
+ fontSize: 12,
716
+ color: AppColors_1.AppColors.primaryBlack,
717
+ paddingVertical: 0,
718
+ },
719
+ recordBtn: {
720
+ flexDirection: 'row',
721
+ alignItems: 'center',
722
+ gap: 5,
723
+ paddingHorizontal: 8,
724
+ paddingVertical: 6,
725
+ borderRadius: 7,
726
+ borderWidth: 1,
727
+ },
728
+ recordBtnActive: {
729
+ backgroundColor: '#DCFCE7',
730
+ borderColor: '#BBF7D0',
731
+ },
732
+ recordBtnPaused: {
733
+ backgroundColor: '#FEF3C7',
734
+ borderColor: '#FDE68A',
735
+ },
736
+ recordDot: {
737
+ width: 6,
738
+ height: 6,
739
+ borderRadius: 3,
740
+ },
741
+ recordBtnText: {
742
+ fontFamily: AppFonts_1.AppFonts.interBold,
743
+ fontSize: 11,
744
+ },
745
+ filterRow: {
746
+ backgroundColor: AppColors_1.AppColors.white,
747
+ borderBottomWidth: 1,
748
+ borderBottomColor: AppColors_1.AppColors.dividerColor,
749
+ },
750
+ filterPill: {
751
+ flexDirection: 'row',
752
+ alignItems: 'center',
753
+ gap: 4,
754
+ paddingHorizontal: 10,
755
+ paddingVertical: 5,
756
+ borderRadius: 16,
757
+ backgroundColor: AppColors_1.AppColors.grayBackground,
758
+ borderWidth: 1,
759
+ borderColor: AppColors_1.AppColors.dividerColor,
760
+ },
761
+ filterPillActive: {
762
+ backgroundColor: AppColors_1.AppColors.brandPurple,
763
+ borderColor: AppColors_1.AppColors.brandPurple,
764
+ },
765
+ filterPillIcon: {
766
+ fontSize: 11,
767
+ },
768
+ filterPillText: {
769
+ fontFamily: AppFonts_1.AppFonts.interMedium,
770
+ fontSize: 11,
771
+ color: AppColors_1.AppColors.grayTextStrong,
772
+ },
773
+ filterPillTextActive: {
774
+ fontFamily: AppFonts_1.AppFonts.interBold,
775
+ color: AppColors_1.AppColors.white,
776
+ },
777
+ scrollContent: {
778
+ padding: 12,
779
+ gap: 10,
780
+ },
781
+ metricCard: {
782
+ backgroundColor: AppColors_1.AppColors.white,
783
+ borderRadius: 12,
784
+ padding: 14,
785
+ borderWidth: 1,
786
+ borderColor: AppColors_1.AppColors.dividerColor,
787
+ shadowColor: AppColors_1.AppColors.black,
788
+ shadowOffset: { width: 0, height: 1 },
789
+ shadowOpacity: 0.05,
790
+ shadowRadius: 3,
791
+ elevation: 1,
792
+ },
793
+ metricCardHeader: {
794
+ flexDirection: 'row',
795
+ alignItems: 'center',
796
+ justifyContent: 'space-between',
797
+ marginBottom: 10,
798
+ },
799
+ metricCardTitle: {
800
+ fontFamily: AppFonts_1.AppFonts.interBold,
801
+ fontSize: 13.5,
802
+ color: AppColors_1.AppColors.primaryBlack,
803
+ },
804
+ scorePill: {
805
+ paddingHorizontal: 8,
806
+ paddingVertical: 3,
807
+ borderRadius: 6,
808
+ },
809
+ scorePillText: {
810
+ fontFamily: AppFonts_1.AppFonts.interBold,
811
+ fontSize: 10.5,
812
+ },
813
+ primaryFpsRow: {
814
+ flexDirection: 'row',
815
+ alignItems: 'center',
816
+ justifyContent: 'space-between',
817
+ paddingVertical: 6,
818
+ },
819
+ fpsBigNumberBox: {
820
+ flexDirection: 'row',
821
+ alignItems: 'baseline',
822
+ gap: 4,
823
+ minWidth: 80,
824
+ },
825
+ fpsBigNumber: {
826
+ fontFamily: AppFonts_1.AppFonts.interBold,
827
+ fontSize: 42,
828
+ lineHeight: 48,
829
+ letterSpacing: -1,
830
+ minWidth: 54,
831
+ },
832
+ fpsUnit: {
833
+ fontFamily: AppFonts_1.AppFonts.interBold,
834
+ fontSize: 14,
835
+ color: AppColors_1.AppColors.grayTextWeak,
836
+ },
837
+ fpsStatsGrid: {
838
+ flexDirection: 'row',
839
+ alignItems: 'center',
840
+ gap: 8,
841
+ },
842
+ statItem: {
843
+ alignItems: 'center',
844
+ minWidth: 50,
845
+ },
846
+ statLabel: {
847
+ fontFamily: AppFonts_1.AppFonts.interRegular,
848
+ fontSize: 10,
849
+ color: AppColors_1.AppColors.grayTextWeak,
850
+ },
851
+ statValue: {
852
+ fontFamily: AppFonts_1.AppFonts.interBold,
853
+ fontSize: 13,
854
+ color: AppColors_1.AppColors.primaryBlack,
855
+ marginTop: 2,
856
+ textAlign: 'center',
857
+ },
858
+ sparklineTrack: {
859
+ flexDirection: 'row',
860
+ alignItems: 'flex-end',
861
+ justifyContent: 'space-between',
862
+ height: 38,
863
+ marginTop: 10,
864
+ paddingTop: 4,
865
+ borderBottomWidth: 1,
866
+ borderBottomColor: AppColors_1.AppColors.dividerColor,
867
+ },
868
+ sparklineCol: {
869
+ flex: 1,
870
+ alignItems: 'center',
871
+ justifyContent: 'flex-end',
872
+ height: '100%',
873
+ paddingHorizontal: 1,
874
+ },
875
+ sparklineBar: {
876
+ width: '80%',
877
+ borderRadius: 2,
878
+ },
879
+ sparklineFooter: {
880
+ flexDirection: 'row',
881
+ alignItems: 'center',
882
+ justifyContent: 'space-between',
883
+ marginTop: 6,
884
+ },
885
+ sparklineFooterText: {
886
+ fontFamily: AppFonts_1.AppFonts.interRegular,
887
+ fontSize: 10,
888
+ color: AppColors_1.AppColors.grayTextWeak,
889
+ },
890
+ sparklineFooterTarget: {
891
+ fontFamily: AppFonts_1.AppFonts.interMedium,
892
+ fontSize: 10,
893
+ color: '#059669',
894
+ },
895
+ budgetCard: {
896
+ backgroundColor: AppColors_1.AppColors.white,
897
+ borderRadius: 12,
898
+ padding: 12,
899
+ borderWidth: 1,
900
+ borderColor: AppColors_1.AppColors.dividerColor,
901
+ gap: 8,
902
+ },
903
+ budgetHeader: {
904
+ flexDirection: 'row',
905
+ alignItems: 'center',
906
+ justifyContent: 'space-between',
907
+ },
908
+ budgetTitle: {
909
+ fontFamily: AppFonts_1.AppFonts.interBold,
910
+ fontSize: 12.5,
911
+ color: AppColors_1.AppColors.primaryBlack,
912
+ },
913
+ budgetHeadroom: {
914
+ fontFamily: AppFonts_1.AppFonts.interBold,
915
+ fontSize: 11,
916
+ color: '#059669',
917
+ },
918
+ budgetBarContainer: {
919
+ flexDirection: 'row',
920
+ height: 8,
921
+ borderRadius: 4,
922
+ overflow: 'hidden',
923
+ backgroundColor: AppColors_1.AppColors.grayBackground,
924
+ gap: 2,
925
+ },
926
+ budgetBarSegment: {
927
+ height: '100%',
928
+ borderRadius: 2,
929
+ },
930
+ budgetLegendGrid: {
931
+ flexDirection: 'row',
932
+ flexWrap: 'wrap',
933
+ gap: 10,
934
+ marginTop: 2,
935
+ },
936
+ legendItem: {
937
+ flexDirection: 'row',
938
+ alignItems: 'center',
939
+ gap: 4,
940
+ },
941
+ legendDot: {
942
+ width: 6,
943
+ height: 6,
944
+ borderRadius: 3,
945
+ },
946
+ legendText: {
947
+ fontFamily: AppFonts_1.AppFonts.interRegular,
948
+ fontSize: 10,
949
+ color: AppColors_1.AppColors.grayTextStrong,
950
+ },
951
+ engineSummaryGrid: {
952
+ flexDirection: 'row',
953
+ alignItems: 'center',
954
+ gap: 8,
955
+ },
956
+ engineSummaryCard: {
957
+ flex: 1,
958
+ backgroundColor: AppColors_1.AppColors.white,
959
+ borderRadius: 10,
960
+ padding: 10,
961
+ borderWidth: 1,
962
+ borderColor: AppColors_1.AppColors.dividerColor,
963
+ },
964
+ engineSummaryLabel: {
965
+ fontFamily: AppFonts_1.AppFonts.interMedium,
966
+ fontSize: 10,
967
+ color: AppColors_1.AppColors.grayTextWeak,
968
+ },
969
+ engineSummaryValue: {
970
+ fontFamily: AppFonts_1.AppFonts.interBold,
971
+ fontSize: 11.5,
972
+ color: AppColors_1.AppColors.primaryBlack,
973
+ marginTop: 2,
974
+ },
975
+ engineSummarySub: {
976
+ fontFamily: AppFonts_1.AppFonts.interRegular,
977
+ fontSize: 9.5,
978
+ color: AppColors_1.AppColors.grayTextWeak,
979
+ marginTop: 1,
980
+ },
981
+ logsHeaderRow: {
982
+ flexDirection: 'row',
983
+ alignItems: 'center',
984
+ justifyContent: 'space-between',
985
+ marginTop: 4,
986
+ },
987
+ logsSectionHeading: {
988
+ fontFamily: AppFonts_1.AppFonts.interBold,
989
+ fontSize: 13,
990
+ color: AppColors_1.AppColors.primaryBlack,
991
+ },
992
+ clearLink: {
993
+ fontFamily: AppFonts_1.AppFonts.interBold,
994
+ fontSize: 11.5,
995
+ color: AppColors_1.AppColors.purple,
996
+ },
997
+ eventCard: {
998
+ backgroundColor: AppColors_1.AppColors.white,
999
+ borderRadius: 10,
1000
+ padding: 10,
1001
+ borderWidth: 1,
1002
+ borderColor: AppColors_1.AppColors.dividerColor,
1003
+ gap: 6,
1004
+ },
1005
+ eventTopRow: {
1006
+ flexDirection: 'row',
1007
+ alignItems: 'center',
1008
+ justifyContent: 'space-between',
1009
+ },
1010
+ fpsBadge: {
1011
+ paddingHorizontal: 6,
1012
+ paddingVertical: 2,
1013
+ borderRadius: 5,
1014
+ borderWidth: 1,
1015
+ },
1016
+ fpsBadgeText: {
1017
+ fontFamily: AppFonts_1.AppFonts.interBold,
1018
+ fontSize: 10,
1019
+ },
1020
+ eventLabel: {
1021
+ fontFamily: AppFonts_1.AppFonts.interBold,
1022
+ fontSize: 12,
1023
+ color: AppColors_1.AppColors.primaryBlack,
1024
+ },
1025
+ eventMeta: {
1026
+ fontFamily: AppFonts_1.AppFonts.interRegular,
1027
+ fontSize: 10,
1028
+ color: AppColors_1.AppColors.grayTextWeak,
1029
+ marginTop: 1,
1030
+ },
1031
+ eventDetail: {
1032
+ fontFamily: AppFonts_1.AppFonts.interRegular,
1033
+ fontSize: 11,
1034
+ color: AppColors_1.AppColors.grayTextStrong,
1035
+ lineHeight: 15,
1036
+ },
1037
+ expandChevron: {
1038
+ fontSize: 10,
1039
+ color: AppColors_1.AppColors.grayTextWeak,
1040
+ marginLeft: 2,
1041
+ },
1042
+ expandedDetailBox: {
1043
+ backgroundColor: AppColors_1.AppColors.grayBackground,
1044
+ borderRadius: 8,
1045
+ padding: 8,
1046
+ marginTop: 4,
1047
+ gap: 6,
1048
+ borderWidth: 1,
1049
+ borderColor: AppColors_1.AppColors.dividerColor,
1050
+ },
1051
+ sourceRow: {
1052
+ flexDirection: 'row',
1053
+ alignItems: 'center',
1054
+ gap: 6,
1055
+ },
1056
+ sourceTag: {
1057
+ fontFamily: AppFonts_1.AppFonts.interBold,
1058
+ fontSize: 8.5,
1059
+ color: AppColors_1.AppColors.purple,
1060
+ backgroundColor: `${AppColors_1.AppColors.purple}14`,
1061
+ paddingHorizontal: 4,
1062
+ paddingVertical: 1,
1063
+ borderRadius: 3,
1064
+ },
1065
+ sourceText: {
1066
+ fontFamily: AppFonts_1.AppFonts.interMedium,
1067
+ fontSize: 10.5,
1068
+ color: AppColors_1.AppColors.grayTextStrong,
1069
+ flex: 1,
1070
+ },
1071
+ breakdownGrid: {
1072
+ flexDirection: 'row',
1073
+ alignItems: 'center',
1074
+ backgroundColor: AppColors_1.AppColors.white,
1075
+ borderRadius: 6,
1076
+ padding: 6,
1077
+ borderWidth: 1,
1078
+ borderColor: AppColors_1.AppColors.dividerColor,
1079
+ justifyContent: 'space-between',
1080
+ },
1081
+ breakdownCol: {
1082
+ alignItems: 'center',
1083
+ flex: 1,
1084
+ },
1085
+ breakdownLabel: {
1086
+ fontFamily: AppFonts_1.AppFonts.interRegular,
1087
+ fontSize: 9,
1088
+ color: AppColors_1.AppColors.grayTextWeak,
1089
+ },
1090
+ breakdownVal: {
1091
+ fontFamily: AppFonts_1.AppFonts.interBold,
1092
+ fontSize: 11,
1093
+ color: AppColors_1.AppColors.primaryBlack,
1094
+ marginTop: 1,
1095
+ },
1096
+ adviceCard: {
1097
+ backgroundColor: '#FAF5FF',
1098
+ borderRadius: 6,
1099
+ padding: 6,
1100
+ borderWidth: 1,
1101
+ borderColor: '#E9D5FF',
1102
+ },
1103
+ adviceHeading: {
1104
+ fontFamily: AppFonts_1.AppFonts.interBold,
1105
+ fontSize: 10,
1106
+ color: '#6B21A8',
1107
+ },
1108
+ adviceBodyText: {
1109
+ fontFamily: AppFonts_1.AppFonts.interRegular,
1110
+ fontSize: 10.5,
1111
+ color: '#7E22CE',
1112
+ lineHeight: 14,
1113
+ marginTop: 1,
1114
+ },
1115
+ searchHighlight: {
1116
+ backgroundColor: '#FEF08A',
1117
+ color: '#854D0E',
1118
+ fontFamily: AppFonts_1.AppFonts.interBold,
1119
+ },
1120
+ emptyCard: {
1121
+ backgroundColor: AppColors_1.AppColors.white,
1122
+ borderRadius: 12,
1123
+ padding: 24,
1124
+ alignItems: 'center',
1125
+ justifyContent: 'center',
1126
+ gap: 8,
1127
+ borderWidth: 1,
1128
+ borderColor: AppColors_1.AppColors.dividerColor,
1129
+ },
1130
+ emptyTitle: {
1131
+ fontFamily: AppFonts_1.AppFonts.interBold,
1132
+ fontSize: 14,
1133
+ color: AppColors_1.AppColors.primaryBlack,
1134
+ },
1135
+ emptySub: {
1136
+ fontFamily: AppFonts_1.AppFonts.interRegular,
1137
+ fontSize: 11.5,
1138
+ color: AppColors_1.AppColors.grayTextWeak,
1139
+ textAlign: 'center',
1140
+ lineHeight: 16,
1141
+ },
1142
+ });
1143
+ exports.default = PerformanceTab;