react-native-inapp-inspector 1.1.27 → 1.1.28

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 (65) hide show
  1. package/android/src/main/java/com/inappinspector/NetworkInspectorModule.java +34 -0
  2. package/dist/commonjs/components/ErrorBoundary.d.ts +1 -1
  3. package/dist/commonjs/components/ErrorBoundary.js +530 -100
  4. package/dist/commonjs/components/Inspector/AnalyticsTab.js +176 -126
  5. package/dist/commonjs/components/Inspector/BundleTab.d.ts +1 -0
  6. package/dist/commonjs/components/Inspector/BundleTab.js +145 -21
  7. package/dist/commonjs/components/Inspector/CrashTab.js +0 -8
  8. package/dist/commonjs/components/Inspector/NetworkDetail.js +41 -0
  9. package/dist/commonjs/components/Inspector/PerformanceTab.d.ts +5 -0
  10. package/dist/commonjs/components/Inspector/PerformanceTab.js +588 -155
  11. package/dist/commonjs/components/Inspector/SettingsPanel.js +57 -22
  12. package/dist/commonjs/components/Inspector/TabBar.js +13 -1
  13. package/dist/commonjs/components/LogCard.js +75 -34
  14. package/dist/commonjs/components/NetworkIcons.d.ts +112 -102
  15. package/dist/commonjs/constants/version.d.ts +1 -1
  16. package/dist/commonjs/constants/version.js +1 -1
  17. package/dist/commonjs/customHooks/analyticsLogger.d.ts +1 -11
  18. package/dist/commonjs/customHooks/analyticsLogger.js +8 -1
  19. package/dist/commonjs/customHooks/bundleAnalyzer.js +121 -124
  20. package/dist/commonjs/customHooks/networkLogger.d.ts +1 -0
  21. package/dist/commonjs/customHooks/networkLogger.js +16 -0
  22. package/dist/commonjs/customHooks/performanceTracker.d.ts +74 -0
  23. package/dist/commonjs/customHooks/performanceTracker.js +546 -392
  24. package/dist/commonjs/customHooks/reduxLogger.d.ts +1 -0
  25. package/dist/commonjs/customHooks/reduxLogger.js +3 -1
  26. package/dist/commonjs/helpers/index.js +27 -3
  27. package/dist/commonjs/i18n/locales/en.json +225 -4
  28. package/dist/commonjs/index.d.ts +1 -1
  29. package/dist/commonjs/index.js +36 -2
  30. package/dist/commonjs/styles/index.d.ts +3 -1
  31. package/dist/commonjs/styles/index.js +8 -6
  32. package/dist/commonjs/types/interfaces.d.ts +3 -0
  33. package/dist/esm/components/ErrorBoundary.d.ts +1 -1
  34. package/dist/esm/components/ErrorBoundary.js +498 -101
  35. package/dist/esm/components/Inspector/AnalyticsTab.js +177 -127
  36. package/dist/esm/components/Inspector/BundleTab.d.ts +1 -0
  37. package/dist/esm/components/Inspector/BundleTab.js +145 -23
  38. package/dist/esm/components/Inspector/CrashTab.js +0 -8
  39. package/dist/esm/components/Inspector/NetworkDetail.js +41 -0
  40. package/dist/esm/components/Inspector/PerformanceTab.d.ts +5 -0
  41. package/dist/esm/components/Inspector/PerformanceTab.js +591 -158
  42. package/dist/esm/components/Inspector/SettingsPanel.js +57 -22
  43. package/dist/esm/components/Inspector/TabBar.js +13 -1
  44. package/dist/esm/components/LogCard.js +75 -34
  45. package/dist/esm/components/NetworkIcons.d.ts +112 -102
  46. package/dist/esm/constants/version.d.ts +1 -1
  47. package/dist/esm/constants/version.js +1 -1
  48. package/dist/esm/customHooks/analyticsLogger.d.ts +1 -11
  49. package/dist/esm/customHooks/analyticsLogger.js +6 -0
  50. package/dist/esm/customHooks/bundleAnalyzer.js +121 -124
  51. package/dist/esm/customHooks/networkLogger.d.ts +1 -0
  52. package/dist/esm/customHooks/networkLogger.js +16 -0
  53. package/dist/esm/customHooks/performanceTracker.d.ts +74 -0
  54. package/dist/esm/customHooks/performanceTracker.js +531 -391
  55. package/dist/esm/customHooks/reduxLogger.d.ts +1 -0
  56. package/dist/esm/customHooks/reduxLogger.js +1 -0
  57. package/dist/esm/helpers/index.js +27 -3
  58. package/dist/esm/i18n/locales/en.json +225 -4
  59. package/dist/esm/index.d.ts +1 -1
  60. package/dist/esm/index.js +24 -3
  61. package/dist/esm/styles/index.d.ts +3 -1
  62. package/dist/esm/styles/index.js +8 -6
  63. package/dist/esm/types/interfaces.d.ts +3 -0
  64. package/ios/NetworkInspectorModule.m +10 -0
  65. package/package.json +1 -1
@@ -36,6 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.downloadBundleFile = void 0;
39
40
  const react_1 = __importStar(require("react"));
40
41
  const react_native_1 = require("react-native");
41
42
  const i18n_1 = require("../../i18n");
@@ -44,6 +45,7 @@ const CopyButton_1 = __importDefault(require("../CopyButton"));
44
45
  const HighlightText_1 = __importDefault(require("../HighlightText"));
45
46
  const AppColors_1 = require("../../styles/AppColors");
46
47
  const AppFonts_1 = require("../../styles/AppFonts");
48
+ const helpers_1 = require("../../helpers");
47
49
  const NetworkIcons_1 = require("../NetworkIcons");
48
50
  const react_native_svg_1 = __importStar(require("react-native-svg"));
49
51
  const bundleAnalyzer_1 = require("../../customHooks/bundleAnalyzer");
@@ -342,7 +344,73 @@ function buildBundleFileTree(files) {
342
344
  }
343
345
  return convertMapToNodes(rootMap);
344
346
  }
345
- const BundleTreeNodeView = ({ node, level, search, totalBundleKb, expandedFolders, toggleFolder, isLastChild, }) => {
347
+ const downloadBundleFile = async (file, scriptURL) => {
348
+ let content = '';
349
+ // 1. Attempt to fetch real source code from Metro dev server if available
350
+ try {
351
+ let devServerOrigin = 'http://localhost:8081';
352
+ if (scriptURL && scriptURL.startsWith('http')) {
353
+ const match = scriptURL.match(/^(https?:\/\/[^/]+)/);
354
+ if (match) {
355
+ devServerOrigin = match[1];
356
+ }
357
+ }
358
+ const cleanPath = file.path.replace(/^\/+/, '');
359
+ const url = `${devServerOrigin}/${cleanPath}`;
360
+ const res = await fetch(url);
361
+ if (res.ok) {
362
+ const text = await res.text();
363
+ if (text && text.length > 0) {
364
+ content = text;
365
+ }
366
+ }
367
+ }
368
+ catch { }
369
+ // 2. If dev server direct fetch is unavailable, provide structured module descriptor with full path
370
+ if (!content) {
371
+ content = `// ─────────────────────────────────────────────────────────────
372
+ // File: ${file.name}
373
+ // Path: ${file.path}
374
+ // Category: ${file.category.toUpperCase()}
375
+ // Size: ${file.sizeKb} KB
376
+ // Status: ${file.status === 'optimal' ? 'Active in Dependency Graph' : 'Not consumed / Static'}
377
+ // Meta: ${file.meta}
378
+ // ─────────────────────────────────────────────────────────────
379
+
380
+ export default {
381
+ id: ${JSON.stringify(file.id)},
382
+ name: ${JSON.stringify(file.name)},
383
+ path: ${JSON.stringify(file.path)},
384
+ category: ${JSON.stringify(file.category)},
385
+ sizeKb: ${file.sizeKb},
386
+ meta: ${JSON.stringify(file.meta)},
387
+ status: ${JSON.stringify(file.status || 'optimal')},
388
+ };
389
+ `;
390
+ }
391
+ // 3. Trigger native Share / Download sheet
392
+ try {
393
+ await react_native_1.Share.share(react_native_1.Platform.OS === 'ios'
394
+ ? {
395
+ title: file.name,
396
+ message: content,
397
+ }
398
+ : {
399
+ title: `Download ${file.name}`,
400
+ message: content,
401
+ }, {
402
+ dialogTitle: `Download / Save ${file.name}`,
403
+ });
404
+ }
405
+ catch { }
406
+ // 4. Also copy directly to clipboard for quick paste in VS Code or external editor
407
+ try {
408
+ (0, helpers_1.copyToClipboard)(content, file.name);
409
+ }
410
+ catch { }
411
+ };
412
+ exports.downloadBundleFile = downloadBundleFile;
413
+ const BundleTreeNodeView = ({ node, level, search, totalBundleKb, expandedFolders, toggleFolder, isLastChild, onDownload, }) => {
346
414
  const isExpanded = search ? true : !!expandedFolders[node.fullPath];
347
415
  const isCollapsed = !isExpanded;
348
416
  const indentLeft = level * 16 + 10;
@@ -380,7 +448,7 @@ const BundleTreeNodeView = ({ node, level, search, totalBundleKb, expandedFolder
380
448
  </TouchableScale_1.default>
381
449
 
382
450
  {!isCollapsed && (<react_native_1.View style={bundleStyles.treeChildrenContainer}>
383
- {node.children.map((child, cIdx) => (<BundleTreeNodeView key={child.id} node={child} level={level + 1} search={search} totalBundleKb={totalBundleKb} expandedFolders={expandedFolders} toggleFolder={toggleFolder} isLastChild={cIdx === node.children.length - 1}/>))}
451
+ {node.children.map((child, cIdx) => (<BundleTreeNodeView key={child.id} node={child} level={level + 1} search={search} totalBundleKb={totalBundleKb} expandedFolders={expandedFolders} toggleFolder={toggleFolder} isLastChild={cIdx === node.children.length - 1} onDownload={onDownload}/>))}
384
452
  </react_native_1.View>)}
385
453
  </react_native_1.View>);
386
454
  }
@@ -421,7 +489,13 @@ const BundleTreeNodeView = ({ node, level, search, totalBundleKb, expandedFolder
421
489
  <react_native_1.Text style={bundleStyles.treeFilePctText}>{pctOfTotal}%</react_native_1.Text>
422
490
  </react_native_1.View>
423
491
 
424
- <CopyButton_1.default value={() => file} label={(0, i18n_1.t)('bundle.fileDetailsJson')}/>
492
+ <react_native_1.View style={{ flexDirection: 'row', alignItems: 'center', gap: 5 }}>
493
+ {onDownload && (<TouchableScale_1.default onPress={() => onDownload(file)} style={bundleStyles.treeActionBtn} hitSlop={8}>
494
+ <NetworkIcons_1.DownloadIcon size={13} color={AppColors_1.AppColors.sky500}/>
495
+ </TouchableScale_1.default>)}
496
+
497
+ <CopyButton_1.default value={() => file} label={(0, i18n_1.t)('bundle.fileDetailsJson')}/>
498
+ </react_native_1.View>
425
499
  </react_native_1.View>
426
500
  </react_native_1.View>);
427
501
  };
@@ -598,12 +672,28 @@ const BundleTab = react_1.default.memo(() => {
598
672
  const filteredTotalKb = (0, react_1.useMemo)(() => {
599
673
  return filteredFiles.reduce((acc, f) => acc + f.sizeKb, 0);
600
674
  }, [filteredFiles]);
601
- // Tree View State & Node Generation (Collapsed by default)
675
+ // Tree View State & Node Generation (Top folders expanded by default)
602
676
  const [filesViewMode, setFilesViewMode] = (0, react_1.useState)('tree');
603
677
  const [expandedFolders, setExpandedFolders] = (0, react_1.useState)({});
604
678
  const fileTreeNodes = (0, react_1.useMemo)(() => {
605
679
  return buildBundleFileTree(filteredFiles);
606
680
  }, [filteredFiles]);
681
+ (0, react_1.useEffect)(() => {
682
+ if (fileTreeNodes.length > 0 && Object.keys(expandedFolders).length === 0) {
683
+ const initial = {};
684
+ fileTreeNodes.forEach(node => {
685
+ if (node.isFolder) {
686
+ initial[node.fullPath] = true;
687
+ node.children.forEach(child => {
688
+ if (child.isFolder) {
689
+ initial[child.fullPath] = true;
690
+ }
691
+ });
692
+ }
693
+ });
694
+ setExpandedFolders(initial);
695
+ }
696
+ }, [fileTreeNodes]);
607
697
  const toggleFolder = (0, react_1.useCallback)((path) => {
608
698
  setExpandedFolders(prev => ({
609
699
  ...prev,
@@ -1356,7 +1446,7 @@ const BundleTab = react_1.default.memo(() => {
1356
1446
 
1357
1447
  {/* ── MODE 1: DIRECTORY TREE VIEW ── */}
1358
1448
  {filesViewMode === 'tree' ? (<react_native_1.View style={bundleStyles.treeRootCard}>
1359
- {fileTreeNodes.map((node, nIdx) => (<BundleTreeNodeView key={node.id} node={node} level={0} search={search} totalBundleKb={summary.totalKb} expandedFolders={expandedFolders} toggleFolder={toggleFolder} isLastChild={nIdx === fileTreeNodes.length - 1}/>))}
1449
+ {fileTreeNodes.map((node, nIdx) => (<BundleTreeNodeView key={node.id} node={node} level={0} search={search} totalBundleKb={summary.totalKb} expandedFolders={expandedFolders} toggleFolder={toggleFolder} isLastChild={nIdx === fileTreeNodes.length - 1} onDownload={f => (0, exports.downloadBundleFile)(f, analysis?.scriptURL)}/>))}
1360
1450
  </react_native_1.View>) : (
1361
1451
  /* ── MODE 2: FLAT LIST VIEW ── */
1362
1452
  filteredFiles.map((file, index) => {
@@ -1392,6 +1482,9 @@ const BundleTab = react_1.default.memo(() => {
1392
1482
  </react_native_1.Text>
1393
1483
  <react_native_1.Text style={bundleStyles.filePercent}>{pctOfTotal}%</react_native_1.Text>
1394
1484
  </react_native_1.View>
1485
+ <TouchableScale_1.default onPress={() => (0, exports.downloadBundleFile)(file, analysis?.scriptURL)} style={bundleStyles.downloadFileBtn} hitSlop={8}>
1486
+ <NetworkIcons_1.DownloadIcon size={14} color={AppColors_1.AppColors.sky500}/>
1487
+ </TouchableScale_1.default>
1395
1488
  <CopyButton_1.default value={() => file} label={t('bundle.fileDetailsJson')}/>
1396
1489
  </react_native_1.View>
1397
1490
  </react_native_1.View>
@@ -1410,21 +1503,25 @@ const BundleTab = react_1.default.memo(() => {
1410
1503
  <react_native_1.Text style={bundleStyles.fileMetaText}>{file.meta}</react_native_1.Text>
1411
1504
  {file.advice && (<react_native_1.View style={[
1412
1505
  bundleStyles.adviceBadge,
1413
- file.status === 'warning' && bundleStyles.adviceWarning,
1414
- file.status === 'optimal' && bundleStyles.adviceOptimal,
1506
+ file.status === 'warning' &&
1507
+ bundleStyles.adviceWarning,
1508
+ file.status === 'optimal' &&
1509
+ bundleStyles.adviceOptimal,
1415
1510
  isUnused && bundleStyles.adviceUnused,
1416
1511
  ]}>
1417
- <react_native_1.View style={{ flexDirection: 'row', alignItems: 'flex-start', gap: 5 }}>
1418
1512
  {isUnused ? (<NetworkIcons_1.TrashIcon color={AppColors_1.AppColors.red600} size={12}/>) : file.status === 'warning' ? (<NetworkIcons_1.LightbulbIcon color={AppColors_1.AppColors.amber800Warm} size={12}/>) : (<NetworkIcons_1.SparkleIcon color={AppColors_1.AppColors.emerald700} size={12}/>)}
1419
1513
  <react_native_1.Text style={[
1420
1514
  bundleStyles.adviceText,
1421
- file.status === 'warning' && { color: AppColors_1.AppColors.amber800Warm },
1422
- file.status === 'optimal' && { color: AppColors_1.AppColors.emerald700 },
1515
+ file.status === 'warning' && {
1516
+ color: AppColors_1.AppColors.amber800Warm,
1517
+ },
1518
+ file.status === 'optimal' && {
1519
+ color: AppColors_1.AppColors.emerald700,
1520
+ },
1423
1521
  isUnused && { color: AppColors_1.AppColors.red600 },
1424
1522
  ]}>
1425
1523
  {file.advice}
1426
1524
  </react_native_1.Text>
1427
- </react_native_1.View>
1428
1525
  </react_native_1.View>)}
1429
1526
  </react_native_1.View>
1430
1527
  </react_native_1.View>);
@@ -1655,6 +1752,9 @@ const BundleTab = react_1.default.memo(() => {
1655
1752
  {t('bundle.fileSizeKb', { size: file.sizeKb })}
1656
1753
  </react_native_1.Text>
1657
1754
  </react_native_1.View>
1755
+ <TouchableScale_1.default onPress={() => (0, exports.downloadBundleFile)(file, analysis?.scriptURL)} style={bundleStyles.downloadFileBtn} hitSlop={8}>
1756
+ <NetworkIcons_1.DownloadIcon size={14} color={AppColors_1.AppColors.sky500}/>
1757
+ </TouchableScale_1.default>
1658
1758
  <CopyButton_1.default value={() => file} label={t('bundle.mediaItemJson')}/>
1659
1759
  </react_native_1.View>
1660
1760
  </react_native_1.View>
@@ -1666,16 +1766,18 @@ const BundleTab = react_1.default.memo(() => {
1666
1766
  file.status === 'warning' && bundleStyles.adviceWarning,
1667
1767
  file.status === 'optimal' && bundleStyles.adviceOptimal,
1668
1768
  ]}>
1669
- <react_native_1.View style={{ flexDirection: 'row', alignItems: 'flex-start', gap: 5 }}>
1670
- {file.status === 'warning' ? (<NetworkIcons_1.LightbulbIcon color={AppColors_1.AppColors.amber800Warm} size={12}/>) : (<NetworkIcons_1.SparkleIcon color={AppColors_1.AppColors.emerald700} size={12}/>)}
1671
- <react_native_1.Text style={[
1769
+ {file.status === 'warning' ? (<NetworkIcons_1.LightbulbIcon color={AppColors_1.AppColors.amber800Warm} size={12}/>) : (<NetworkIcons_1.SparkleIcon color={AppColors_1.AppColors.emerald700} size={12}/>)}
1770
+ <react_native_1.Text style={[
1672
1771
  bundleStyles.adviceText,
1673
- file.status === 'warning' && { color: AppColors_1.AppColors.amber800Warm },
1674
- file.status === 'optimal' && { color: AppColors_1.AppColors.emerald700 },
1772
+ file.status === 'warning' && {
1773
+ color: AppColors_1.AppColors.amber800Warm,
1774
+ },
1775
+ file.status === 'optimal' && {
1776
+ color: AppColors_1.AppColors.emerald700,
1777
+ },
1675
1778
  ]}>
1676
- {file.advice}
1677
- </react_native_1.Text>
1678
- </react_native_1.View>
1779
+ {file.advice}
1780
+ </react_native_1.Text>
1679
1781
  </react_native_1.View>)}
1680
1782
  </react_native_1.View>
1681
1783
  </react_native_1.View>))}
@@ -2446,6 +2548,24 @@ const bundleStyles = react_native_1.StyleSheet.create({
2446
2548
  fileSizeBox: {
2447
2549
  alignItems: 'flex-end',
2448
2550
  },
2551
+ downloadFileBtn: {
2552
+ padding: 6,
2553
+ borderRadius: 6,
2554
+ backgroundColor: `${AppColors_1.AppColors.sky500}14`,
2555
+ alignItems: 'center',
2556
+ justifyContent: 'center',
2557
+ borderWidth: 1,
2558
+ borderColor: `${AppColors_1.AppColors.sky500}28`,
2559
+ },
2560
+ treeActionBtn: {
2561
+ padding: 4,
2562
+ borderRadius: 4,
2563
+ backgroundColor: `${AppColors_1.AppColors.sky500}14`,
2564
+ alignItems: 'center',
2565
+ justifyContent: 'center',
2566
+ borderWidth: 1,
2567
+ borderColor: `${AppColors_1.AppColors.sky500}28`,
2568
+ },
2449
2569
  fileSizeKb: {
2450
2570
  fontFamily: AppFonts_1.AppFonts.interBold,
2451
2571
  fontSize: 12,
@@ -2617,10 +2737,13 @@ const bundleStyles = react_native_1.StyleSheet.create({
2617
2737
  color: AppColors_1.AppColors.amber800Warm,
2618
2738
  },
2619
2739
  adviceBadge: {
2740
+ flexDirection: 'row',
2741
+ alignItems: 'center',
2620
2742
  paddingHorizontal: 8,
2621
- paddingVertical: 4,
2743
+ paddingVertical: 5,
2622
2744
  borderRadius: 6,
2623
- marginTop: 2,
2745
+ marginTop: 4,
2746
+ gap: 6,
2624
2747
  },
2625
2748
  adviceWarning: {
2626
2749
  backgroundColor: AppColors_1.AppColors.amber100,
@@ -2636,6 +2759,7 @@ const bundleStyles = react_native_1.StyleSheet.create({
2636
2759
  fontFamily: AppFonts_1.AppFonts.interMedium,
2637
2760
  fontSize: 10.5,
2638
2761
  lineHeight: 14,
2762
+ flex: 1,
2639
2763
  },
2640
2764
  impactBadge: {
2641
2765
  paddingHorizontal: 6,
@@ -72,14 +72,6 @@ const CrashTab = react_1.default.memo(() => {
72
72
  const [crashFilters, setCrashFilters] = (0, react_1.useState)(CrashFilterModal_1.DEFAULT_CRASH_FILTERS);
73
73
  const [isFilterModalOpen, setIsFilterModalOpen] = (0, react_1.useState)(false);
74
74
  const [detailSubTab, setDetailSubTab] = (0, react_1.useState)('stack');
75
- // Auto-open the latest crash detail on first visit if crashes exist
76
- const hasAutoOpened = react_1.default.useRef(false);
77
- react_1.default.useEffect(() => {
78
- if (!hasAutoOpened.current && !selectedCrash && crashRecords.length > 0) {
79
- setSelectedCrash(crashRecords[0]);
80
- hasAutoOpened.current = true;
81
- }
82
- }, [crashRecords, selectedCrash, setSelectedCrash]);
83
75
  // Calculate statistics
84
76
  const stats = (0, react_1.useMemo)(() => {
85
77
  const total = crashRecords.length;
@@ -102,6 +102,47 @@ const NetworkDetail = react_1.default.memo(() => {
102
102
  </react_native_1.Text>
103
103
  </react_native_1.View>
104
104
 
105
+ {/* Client Chip (AXIOS / FETCH / XHR) */}
106
+ {selected.client && (<react_native_1.View style={[
107
+ styles_1.default.chip,
108
+ {
109
+ backgroundColor: selected.client === 'axios'
110
+ ? `${AppColors_1.AppColors.purple}14`
111
+ : selected.client === 'apollo' || selected.client === 'graphql'
112
+ ? `${AppColors_1.AppColors.pink500}14`
113
+ : selected.client === 'xhr'
114
+ ? `${AppColors_1.AppColors.amber500}14`
115
+ : `${AppColors_1.AppColors.sky500}14`,
116
+ borderColor: selected.client === 'axios'
117
+ ? `${AppColors_1.AppColors.purple}30`
118
+ : selected.client === 'apollo' || selected.client === 'graphql'
119
+ ? `${AppColors_1.AppColors.pink500}30`
120
+ : selected.client === 'xhr'
121
+ ? `${AppColors_1.AppColors.amber500}30`
122
+ : `${AppColors_1.AppColors.sky500}30`,
123
+ paddingHorizontal: 6,
124
+ paddingVertical: 3,
125
+ borderRadius: 6,
126
+ },
127
+ ]}>
128
+ <react_native_1.Text style={[
129
+ styles_1.default.chipText,
130
+ {
131
+ fontFamily: AppFonts_1.AppFonts.interBold,
132
+ fontSize: 9.5,
133
+ color: selected.client === 'axios'
134
+ ? AppColors_1.AppColors.purple
135
+ : selected.client === 'apollo' || selected.client === 'graphql'
136
+ ? AppColors_1.AppColors.pink500
137
+ : selected.client === 'xhr'
138
+ ? AppColors_1.AppColors.amber700
139
+ : AppColors_1.AppColors.sky600,
140
+ },
141
+ ]}>
142
+ {selected.client.toUpperCase()}
143
+ </react_native_1.Text>
144
+ </react_native_1.View>)}
145
+
105
146
  {/* Status Pill */}
106
147
  {selected.status != null && (<react_native_1.View style={[
107
148
  styles_1.default.chip,
@@ -6,6 +6,11 @@ export interface PerformanceEvent {
6
6
  category: 'render' | 'navigation' | 'memory' | 'io' | 'bridge';
7
7
  fps: number;
8
8
  durationMs: number;
9
+ droppedFrames?: number;
10
+ frameTimeMs?: number;
11
+ frameBudgetPct?: number;
12
+ bottleneckThread?: 'JS Thread' | 'UI Thread' | 'Bridge' | 'Balanced';
13
+ screenName?: string;
9
14
  label: string;
10
15
  detail: string;
11
16
  source?: string;