react-native-inapp-inspector 2.0.0 → 2.0.2

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 (95) hide show
  1. package/README.md +10 -0
  2. package/android/.gradle/8.9/checksums/checksums.lock +0 -0
  3. package/android/.gradle/8.9/checksums/md5-checksums.bin +0 -0
  4. package/android/.gradle/8.9/checksums/sha1-checksums.bin +0 -0
  5. package/android/.gradle/8.9/dependencies-accessors/gc.properties +0 -0
  6. package/android/.gradle/8.9/executionHistory/executionHistory.lock +0 -0
  7. package/android/.gradle/8.9/fileChanges/last-build.bin +0 -0
  8. package/android/.gradle/8.9/fileHashes/fileHashes.lock +0 -0
  9. package/android/.gradle/8.9/gc.properties +0 -0
  10. package/android/.gradle/9.2.0/checksums/checksums.lock +0 -0
  11. package/android/.gradle/9.2.0/fileChanges/last-build.bin +0 -0
  12. package/android/.gradle/9.2.0/fileHashes/fileHashes.bin +0 -0
  13. package/android/.gradle/9.2.0/fileHashes/fileHashes.lock +0 -0
  14. package/android/.gradle/9.2.0/gc.properties +0 -0
  15. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  16. package/android/.gradle/buildOutputCleanup/cache.properties +2 -0
  17. package/android/.gradle/vcs-1/gc.properties +0 -0
  18. package/android/build/reports/problems/problems-report.html +659 -0
  19. package/android/src/main/java/com/inappinspector/NetworkInspectorModule.kt +33 -24
  20. package/dist/commonjs/components/ConsoleLogCard.js +5 -5
  21. package/dist/commonjs/components/EndOfListFooter.js +1 -1
  22. package/dist/commonjs/components/Inspector/AnalyticsTab.js +19 -71
  23. package/dist/commonjs/components/Inspector/BundleTab.js +670 -188
  24. package/dist/commonjs/components/Inspector/ConsoleTab.js +109 -98
  25. package/dist/commonjs/components/Inspector/CrashTab.js +2 -1
  26. package/dist/commonjs/components/Inspector/InspectorHeader.js +102 -19
  27. package/dist/commonjs/components/Inspector/MainScreen.js +151 -17
  28. package/dist/commonjs/components/Inspector/NetworkTab.js +104 -20
  29. package/dist/commonjs/components/Inspector/SettingsPanel.js +1111 -1385
  30. package/dist/commonjs/components/Inspector/TabBar.js +3 -4
  31. package/dist/commonjs/components/Inspector/TelemetryConsentModal.d.ts +3 -0
  32. package/dist/commonjs/components/Inspector/TelemetryConsentModal.js +212 -0
  33. package/dist/commonjs/components/JsonViewer.js +9 -4
  34. package/dist/commonjs/components/LogCard.js +31 -1
  35. package/dist/commonjs/components/LogSyntaxHighlighter.d.ts +16 -0
  36. package/dist/commonjs/components/LogSyntaxHighlighter.js +130 -0
  37. package/dist/commonjs/components/NetworkIcons.d.ts +2 -0
  38. package/dist/commonjs/components/NetworkIcons.js +10 -1
  39. package/dist/commonjs/components/Slider.d.ts +13 -0
  40. package/dist/commonjs/components/Slider.js +261 -0
  41. package/dist/commonjs/constants/version.d.ts +1 -1
  42. package/dist/commonjs/constants/version.js +1 -1
  43. package/dist/commonjs/customHooks/analyticsLogger.js +1 -1
  44. package/dist/commonjs/customHooks/crashHandler.js +1 -1
  45. package/dist/commonjs/helpers/index.d.ts +2 -0
  46. package/dist/commonjs/helpers/index.js +16 -0
  47. package/dist/commonjs/helpers/searchQueryParser.d.ts +6 -0
  48. package/dist/commonjs/helpers/searchQueryParser.js +236 -0
  49. package/dist/commonjs/helpers/settingsStore.js +13 -13
  50. package/dist/commonjs/helpers/telemetry.d.ts +99 -0
  51. package/dist/commonjs/helpers/telemetry.js +398 -0
  52. package/dist/commonjs/index.d.ts +2 -0
  53. package/dist/commonjs/index.js +30 -21
  54. package/dist/commonjs/native/NativeInspector.d.ts +5 -0
  55. package/dist/commonjs/native/NativeInspector.js +3 -2
  56. package/dist/commonjs/styles/index.js +35 -35
  57. package/dist/esm/components/ConsoleLogCard.js +5 -5
  58. package/dist/esm/components/EndOfListFooter.js +2 -2
  59. package/dist/esm/components/Inspector/AnalyticsTab.js +21 -73
  60. package/dist/esm/components/Inspector/BundleTab.js +670 -188
  61. package/dist/esm/components/Inspector/ConsoleTab.js +110 -99
  62. package/dist/esm/components/Inspector/CrashTab.js +2 -1
  63. package/dist/esm/components/Inspector/InspectorHeader.js +103 -20
  64. package/dist/esm/components/Inspector/MainScreen.js +119 -18
  65. package/dist/esm/components/Inspector/NetworkTab.js +105 -21
  66. package/dist/esm/components/Inspector/SettingsPanel.js +1114 -1388
  67. package/dist/esm/components/Inspector/TabBar.js +4 -5
  68. package/dist/esm/components/Inspector/TelemetryConsentModal.d.ts +3 -0
  69. package/dist/esm/components/Inspector/TelemetryConsentModal.js +175 -0
  70. package/dist/esm/components/JsonViewer.js +9 -4
  71. package/dist/esm/components/LogCard.js +32 -2
  72. package/dist/esm/components/LogSyntaxHighlighter.d.ts +16 -0
  73. package/dist/esm/components/LogSyntaxHighlighter.js +123 -0
  74. package/dist/esm/components/NetworkIcons.d.ts +2 -0
  75. package/dist/esm/components/NetworkIcons.js +7 -0
  76. package/dist/esm/components/Slider.d.ts +13 -0
  77. package/dist/esm/components/Slider.js +222 -0
  78. package/dist/esm/constants/version.d.ts +1 -1
  79. package/dist/esm/constants/version.js +1 -1
  80. package/dist/esm/customHooks/analyticsLogger.js +1 -1
  81. package/dist/esm/customHooks/crashHandler.js +1 -1
  82. package/dist/esm/helpers/index.d.ts +2 -0
  83. package/dist/esm/helpers/index.js +2 -0
  84. package/dist/esm/helpers/searchQueryParser.d.ts +6 -0
  85. package/dist/esm/helpers/searchQueryParser.js +233 -0
  86. package/dist/esm/helpers/settingsStore.js +13 -13
  87. package/dist/esm/helpers/telemetry.d.ts +99 -0
  88. package/dist/esm/helpers/telemetry.js +380 -0
  89. package/dist/esm/index.d.ts +2 -0
  90. package/dist/esm/index.js +20 -21
  91. package/dist/esm/native/NativeInspector.d.ts +5 -0
  92. package/dist/esm/native/NativeInspector.js +3 -2
  93. package/dist/esm/styles/index.js +35 -35
  94. package/ios/{NetworkInspectorModule.m → NetworkInspectorModule.mm} +193 -97
  95. package/package.json +2 -2
@@ -74,7 +74,11 @@ const PackageLogoRenderer = ({ name, size = 28 }) => {
74
74
  if (lowerName.includes('screens') || lowerName.includes('software-mansion')) {
75
75
  return (<View style={[
76
76
  bundleStyles.pkgLogoWrap,
77
- { width: size, height: size, backgroundColor: AppColors.reanimatedNavy },
77
+ {
78
+ width: size,
79
+ height: size,
80
+ backgroundColor: AppColors.reanimatedNavy,
81
+ },
78
82
  ]}>
79
83
  <Svg width={size - 8} height={size - 8} viewBox="0 0 100 100" fill="none">
80
84
  <Path d="M50 15 L88 35 L50 55 L12 35 Z" fill={AppColors.sky400}/>
@@ -126,7 +130,12 @@ const PackageLogoRenderer = ({ name, size = 28 }) => {
126
130
  </View>);
127
131
  };
128
132
  const BundleCategoryIcon = ({ type, size = 16, color = AppColors.purple }) => {
129
- const stroke = { stroke: color, strokeWidth: 1.7, strokeLinecap: 'round', strokeLinejoin: 'round' };
133
+ const stroke = {
134
+ stroke: color,
135
+ strokeWidth: 1.7,
136
+ strokeLinecap: 'round',
137
+ strokeLinejoin: 'round',
138
+ };
130
139
  const renderGlyph = () => {
131
140
  switch (type) {
132
141
  case 'folder':
@@ -337,7 +346,9 @@ export const downloadBundleFile = async (file, scriptURL) => {
337
346
  // Path: ${file.path}
338
347
  // Category: ${file.category.toUpperCase()}
339
348
  // Size: ${file.sizeKb} KB
340
- // Status: ${file.status === 'optimal' ? 'Active in Dependency Graph' : 'Not consumed / Static'}
349
+ // Status: ${file.status === 'optimal'
350
+ ? 'Active in Dependency Graph'
351
+ : 'Not consumed / Static'}
341
352
  // Meta: ${file.meta}
342
353
  // ─────────────────────────────────────────────────────────────
343
354
 
@@ -379,10 +390,9 @@ const BundleTreeNodeView = ({ node, level, search, totalBundleKb, expandedFolder
379
390
  const indentLeft = level * 16 + 10;
380
391
  if (node.isFolder) {
381
392
  return (<View style={bundleStyles.treeFolderBlock}>
382
- <TouchableScale accessible={true} accessibilityRole="button" accessibilityLabel={`Folder ${node.name}, ${node.fileCount} ${node.fileCount === 1 ? 'file' : 'files'}, ${node.sizeKb >= 1024 ? `${(node.sizeKb / 1024).toFixed(2)} MB` : `${node.sizeKb} KB`}`} accessibilityState={{ expanded: isExpanded }} accessibilityHint="Double tap to expand or collapse folder" onPress={() => toggleFolder(node.fullPath)} style={[
383
- bundleStyles.treeFolderRow,
384
- { paddingLeft: indentLeft },
385
- ]}>
393
+ <TouchableScale accessible={true} accessibilityRole="button" accessibilityLabel={`Folder ${node.name}, ${node.fileCount} ${node.fileCount === 1 ? 'file' : 'files'}, ${node.sizeKb >= 1024
394
+ ? `${(node.sizeKb / 1024).toFixed(2)} MB`
395
+ : `${node.sizeKb} KB`}`} accessibilityState={{ expanded: isExpanded }} accessibilityHint="Double tap to expand or collapse folder" onPress={() => toggleFolder(node.fullPath)} style={[bundleStyles.treeFolderRow, { paddingLeft: indentLeft }]}>
386
396
  <View style={bundleStyles.treeRowLeft}>
387
397
  <Text style={bundleStyles.treeChevron}>
388
398
  {isCollapsed ? '▸' : '▾'}
@@ -391,7 +401,8 @@ const BundleTreeNodeView = ({ node, level, search, totalBundleKb, expandedFolder
391
401
  <HighlightText text={node.name + '/'} search={search} style={bundleStyles.treeFolderName} highlightStyle={bundleStyles.searchHighlight}/>
392
402
  <View style={bundleStyles.treeCountBadge}>
393
403
  <Text style={bundleStyles.treeCountBadgeText}>
394
- {node.fileCount} {node.fileCount === 1 ? t('bundle.file') : t('bundle.files')}
404
+ {node.fileCount}{' '}
405
+ {node.fileCount === 1 ? t('bundle.file') : t('bundle.files')}
395
406
  </Text>
396
407
  </View>
397
408
  </View>
@@ -399,7 +410,9 @@ const BundleTreeNodeView = ({ node, level, search, totalBundleKb, expandedFolder
399
410
  <View style={bundleStyles.treeRowRight}>
400
411
  <Text style={bundleStyles.treeFolderSize}>
401
412
  {node.sizeKb >= 1024
402
- ? t('bundle.fileSizeMb', { size: (node.sizeKb / 1024).toFixed(2) })
413
+ ? t('bundle.fileSizeMb', {
414
+ size: (node.sizeKb / 1024).toFixed(2),
415
+ })
403
416
  : t('bundle.fileSizeKb', { size: node.sizeKb })}
404
417
  </Text>
405
418
  <CopyButton value={() => ({
@@ -438,13 +451,18 @@ const BundleTreeNodeView = ({ node, level, search, totalBundleKb, expandedFolder
438
451
  <HighlightText text={file.name} search={search} style={bundleStyles.treeFileName} highlightStyle={bundleStyles.searchHighlight}/>
439
452
 
440
453
  {isUnused && (<View style={bundleStyles.treeUnusedPill}>
441
- <Text style={bundleStyles.treeUnusedPillText}>{t('bundle.notConsumed')}</Text>
454
+ <Text style={bundleStyles.treeUnusedPillText}>
455
+ {t('bundle.notConsumed')}
456
+ </Text>
442
457
  </View>)}
443
458
  </View>
444
459
 
445
460
  <View style={bundleStyles.treeRowRight}>
446
461
  <View style={bundleStyles.treeFileSizeGroup}>
447
- <Text style={[bundleStyles.treeFileSizeText, isUnused && { color: AppColors.red600 }]}>
462
+ <Text style={[
463
+ bundleStyles.treeFileSizeText,
464
+ isUnused && { color: AppColors.red600 },
465
+ ]}>
448
466
  {file.sizeKb >= 1024
449
467
  ? t('bundle.fileSizeMb', { size: (file.sizeKb / 1024).toFixed(2) })
450
468
  : t('bundle.fileSizeKb', { size: file.sizeKb })}
@@ -467,17 +485,41 @@ const CATEGORY_TABS = [
467
485
  { key: 'UNUSED', labelKey: 'bundle.catUnused', iconType: 'ban' },
468
486
  { key: 'CONSUMED', labelKey: 'bundle.catConsumed', iconType: 'bolt' },
469
487
  { key: 'image', labelKey: 'bundle.catImages', iconType: 'image' },
470
- { key: 'typescript', labelKey: 'bundle.catTypescript', iconType: 'typescript' },
471
- { key: 'javascript', labelKey: 'bundle.catJavascript', iconType: 'javascript' },
488
+ {
489
+ key: 'typescript',
490
+ labelKey: 'bundle.catTypescript',
491
+ iconType: 'typescript',
492
+ },
493
+ {
494
+ key: 'javascript',
495
+ labelKey: 'bundle.catJavascript',
496
+ iconType: 'javascript',
497
+ },
472
498
  { key: 'font', labelKey: 'bundle.catFonts', iconType: 'font' },
473
499
  { key: 'json', labelKey: 'bundle.catJson', iconType: 'json' },
474
500
  ];
475
501
  const CATEGORY_COLORS = {
476
- image: { label: 'Images & Media', color: AppColors.pink500, bg: AppColors.pink100 },
477
- typescript: { label: 'TypeScript', color: AppColors.sky500, bg: AppColors.sky100 },
478
- javascript: { label: 'JavaScript', color: AppColors.indigo500, bg: AppColors.indigo50 },
502
+ image: {
503
+ label: 'Images & Media',
504
+ color: AppColors.pink500,
505
+ bg: AppColors.pink100,
506
+ },
507
+ typescript: {
508
+ label: 'TypeScript',
509
+ color: AppColors.sky500,
510
+ bg: AppColors.sky100,
511
+ },
512
+ javascript: {
513
+ label: 'JavaScript',
514
+ color: AppColors.indigo500,
515
+ bg: AppColors.indigo50,
516
+ },
479
517
  font: { label: 'Fonts', color: AppColors.purple500, bg: AppColors.purple100 },
480
- json: { label: 'JSON / Data', color: AppColors.emerald500, bg: AppColors.emerald100 },
518
+ json: {
519
+ label: 'JSON / Data',
520
+ color: AppColors.emerald500,
521
+ bg: AppColors.emerald100,
522
+ },
481
523
  };
482
524
  const BundleTab = React.memo(() => {
483
525
  const { t } = useTranslation();
@@ -524,8 +566,13 @@ const BundleTab = React.memo(() => {
524
566
  const bundlePackages = useMemo(() => (analysis ? analysis.packages : []), [analysis]);
525
567
  // Comprehensive bundle summary (full Metro bundle calculation e.g. 6.0 MB)
526
568
  const summary = useMemo(() => {
527
- const totalKb = analysis?.totalDevKb || (analysis?.totalDevBytes ? Math.round(analysis.totalDevBytes / 1024) : 6144);
528
- const totalMb = analysis?.totalDevMb ? analysis.totalDevMb.toFixed(2) : (totalKb / 1024).toFixed(2);
569
+ const totalKb = analysis?.totalDevKb ||
570
+ (analysis?.totalDevBytes
571
+ ? Math.round(analysis.totalDevBytes / 1024)
572
+ : 6144);
573
+ const totalMb = analysis?.totalDevMb
574
+ ? analysis.totalDevMb.toFixed(2)
575
+ : (totalKb / 1024).toFixed(2);
529
576
  const nodeModulesKb = analysis?.splitUp?.nodeModules?.kb || Math.round(totalKb * 0.52);
530
577
  const nodeModulesPct = analysis?.splitUp?.nodeModules?.pct || 52.0;
531
578
  const metroKb = analysis?.splitUp?.metroDevOverhead?.kb || Math.round(totalKb * 0.21);
@@ -576,7 +623,9 @@ const BundleTab = React.memo(() => {
576
623
  iconType: 'image',
577
624
  title: 'Images & Static Media',
578
625
  targetTab: 'media',
579
- count: bundleFiles.filter(f => f.category === 'image' || f.category === 'font' || f.category === 'json').length || 3,
626
+ count: bundleFiles.filter(f => f.category === 'image' ||
627
+ f.category === 'font' ||
628
+ f.category === 'json').length || 3,
580
629
  sizeKb: assetsMediaKb,
581
630
  sizeMb: (assetsMediaKb / 1024).toFixed(2),
582
631
  pct: assetsMediaPct,
@@ -760,7 +809,7 @@ const BundleTab = React.memo(() => {
760
809
  </TouchableScale>);
761
810
  })}
762
811
  </ScrollView>
763
- <TouchableScale accessible={true} accessibilityRole="button" accessibilityLabel={isAnalyzing ? "Analyzing Metro bundle" : "Reload bundle analysis"} accessibilityHint="Re-probes Metro dev server and refreshes bundle statistics" onPress={refreshAnalysis} style={bundleStyles.reloadButton} hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}>
812
+ <TouchableScale accessible={true} accessibilityRole="button" accessibilityLabel={isAnalyzing ? 'Analyzing Metro bundle' : 'Reload bundle analysis'} accessibilityHint="Re-probes Metro dev server and refreshes bundle statistics" onPress={refreshAnalysis} style={bundleStyles.reloadButton} hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}>
764
813
  {isAnalyzing ? (<ActivityIndicator size="small" color={AppColors.brandPurple}/>) : (<RefreshCcwIcon color={AppColors.brandPurple} size={14}/>)}
765
814
  </TouchableScale>
766
815
  </View>
@@ -769,7 +818,6 @@ const BundleTab = React.memo(() => {
769
818
  {/* ── 1. TAB: OVERVIEW & TREEMAP ───────────────────────────────────────── */}
770
819
  {/* ══════════════════════════════════════════════════════════════════════ */}
771
820
  {activeSubTab === 'overview' && (<ScrollView style={{ flex: 1 }} contentContainerStyle={bundleStyles.contentContainer} keyboardShouldPersistTaps="handled">
772
-
773
821
  {/* Hero Overview Card */}
774
822
  <View style={bundleStyles.heroCard}>
775
823
  <View style={bundleStyles.heroTopRow}>
@@ -777,7 +825,9 @@ const BundleTab = React.memo(() => {
777
825
  <PackageIcon color={AppColors.brandPurple} size={24}/>
778
826
  </View>
779
827
  <View style={{ flex: 1 }}>
780
- <Text style={bundleStyles.heroTitle}>{t('bundle.heroTitle')}</Text>
828
+ <Text style={bundleStyles.heroTitle}>
829
+ {t('bundle.heroTitle')}
830
+ </Text>
781
831
  {analysis?.isLive && analysis.scriptURL ? (<View style={{ marginTop: 4, gap: 4 }}>
782
832
  <Text style={bundleStyles.heroSubtitle}>
783
833
  {t('bundle.heroSubtitle')}
@@ -796,7 +846,11 @@ const BundleTab = React.memo(() => {
796
846
  justifyContent: 'space-between',
797
847
  marginBottom: 3,
798
848
  }}>
799
- <View style={{ flexDirection: 'row', alignItems: 'center', gap: 4 }}>
849
+ <View style={{
850
+ flexDirection: 'row',
851
+ alignItems: 'center',
852
+ gap: 4,
853
+ }}>
800
854
  <ExternalLinkIcon color={AppColors.skyBlue} size={11}/>
801
855
  <Text style={{
802
856
  fontFamily: AppFonts.interBold,
@@ -836,7 +890,9 @@ const BundleTab = React.memo(() => {
836
890
  {/* 4-Stat Metric Grid */}
837
891
  <View style={bundleStyles.metricsGrid}>
838
892
  <View style={bundleStyles.metricBox}>
839
- <Text style={bundleStyles.metricLabel}>{t('bundle.devBundleSize')}</Text>
893
+ <Text style={bundleStyles.metricLabel}>
894
+ {t('bundle.devBundleSize')}
895
+ </Text>
840
896
  <Text style={bundleStyles.metricValue}>
841
897
  {t('bundle.mbValue', {
842
898
  size: analysis ? analysis.totalDevMb : summary.totalMb,
@@ -853,8 +909,13 @@ const BundleTab = React.memo(() => {
853
909
  </View>
854
910
 
855
911
  <View style={bundleStyles.metricBox}>
856
- <Text style={bundleStyles.metricLabel}>{t('bundle.imagesMedia')}</Text>
857
- <Text style={[bundleStyles.metricValue, { color: AppColors.pink500 }]}>
912
+ <Text style={bundleStyles.metricLabel}>
913
+ {t('bundle.imagesMedia')}
914
+ </Text>
915
+ <Text style={[
916
+ bundleStyles.metricValue,
917
+ { color: AppColors.pink500 },
918
+ ]}>
858
919
  {t('bundle.fileSizeKb', { size: summary.images.kb })}
859
920
  </Text>
860
921
  <Text style={bundleStyles.metricHint}>
@@ -863,9 +924,13 @@ const BundleTab = React.memo(() => {
863
924
  </View>
864
925
 
865
926
  <View style={bundleStyles.metricBox}>
866
- <Text style={bundleStyles.metricLabel}>{t('bundle.tsJsSource')}</Text>
927
+ <Text style={bundleStyles.metricLabel}>
928
+ {t('bundle.tsJsSource')}
929
+ </Text>
867
930
  <Text style={[bundleStyles.metricValue, { color: AppColors.sky500 }]}>
868
- {t('bundle.fileSizeKb', { size: summary.ts.kb + summary.js.kb })}
931
+ {t('bundle.fileSizeKb', {
932
+ size: summary.ts.kb + summary.js.kb,
933
+ })}
869
934
  </Text>
870
935
  <Text style={bundleStyles.metricHint}>
871
936
  {t('bundle.pctOfTrackedAssets', {
@@ -875,11 +940,22 @@ const BundleTab = React.memo(() => {
875
940
  </View>
876
941
 
877
942
  <View style={bundleStyles.metricBox}>
878
- <Text style={bundleStyles.metricLabel}>{t('bundle.jsEngine')}</Text>
879
- <View style={{ flexDirection: 'row', alignItems: 'center', gap: 5, marginTop: 2 }}>
943
+ <Text style={bundleStyles.metricLabel}>
944
+ {t('bundle.jsEngine')}
945
+ </Text>
946
+ <View style={{
947
+ flexDirection: 'row',
948
+ alignItems: 'center',
949
+ gap: 5,
950
+ marginTop: 2,
951
+ }}>
880
952
  <View style={[
881
953
  bundleStyles.statusIndicator,
882
- { backgroundColor: isHermes ? AppColors.greenColor : AppColors.darkOrange },
954
+ {
955
+ backgroundColor: isHermes
956
+ ? AppColors.greenColor
957
+ : AppColors.darkOrange,
958
+ },
883
959
  ]}/>
884
960
  <Text style={bundleStyles.metricValue}>
885
961
  {isHermes ? 'Hermes' : 'JSC'}
@@ -894,7 +970,12 @@ const BundleTab = React.memo(() => {
894
970
 
895
971
  {/* Live analysis unavailable warning */}
896
972
  {analysis && !analysis.isLive && (<View style={bundleStyles.liveWarningCard}>
897
- <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6, flex: 1 }}>
973
+ <View style={{
974
+ flexDirection: 'row',
975
+ alignItems: 'center',
976
+ gap: 6,
977
+ flex: 1,
978
+ }}>
898
979
  <CircleAlertIcon color={AppColors.amber700} size={16}/>
899
980
  <View style={{ flex: 1 }}>
900
981
  <Text style={bundleStyles.liveWarningTitle}>
@@ -907,13 +988,13 @@ const BundleTab = React.memo(() => {
907
988
  </View>
908
989
  </View>)}
909
990
 
910
-
911
-
912
991
  {/* Visual Type Distribution Ratio Bar */}
913
992
  <View style={bundleStyles.sectionCard}>
914
993
  <View style={bundleStyles.sectionHeaderRow}>
915
- <View>
916
- <Text style={bundleStyles.sectionTitle}>{t('bundle.treemapTitle')}</Text>
994
+ <View style={{ flex: 1, paddingRight: 4 }}>
995
+ <Text style={bundleStyles.sectionTitle}>
996
+ {t('bundle.treemapTitle')}
997
+ </Text>
917
998
  <Text style={bundleStyles.sectionSub}>
918
999
  {t('bundle.treemapSub', { mb: summary.totalMb })}
919
1000
  </Text>
@@ -921,19 +1002,44 @@ const BundleTab = React.memo(() => {
921
1002
  <CopyButton value={() => ({
922
1003
  totalMb: summary.totalMb,
923
1004
  totalKb: summary.totalKb,
924
- nodeModules: { kb: summary.nodeModulesKb, pct: summary.nodeModulesPct },
1005
+ nodeModules: {
1006
+ kb: summary.nodeModulesKb,
1007
+ pct: summary.nodeModulesPct,
1008
+ },
925
1009
  metroRuntime: { kb: summary.metroKb, pct: summary.metroPct },
926
- appSource: { kb: summary.appSourceKb, pct: summary.appSourcePct },
927
- assetsMedia: { kb: summary.assetsMediaKb, pct: summary.assetsMediaPct },
1010
+ appSource: {
1011
+ kb: summary.appSourceKb,
1012
+ pct: summary.appSourcePct,
1013
+ },
1014
+ assetsMedia: {
1015
+ kb: summary.assetsMediaKb,
1016
+ pct: summary.assetsMediaPct,
1017
+ },
928
1018
  })} label={t('bundle.treemapJson')}/>
929
1019
  </View>
930
1020
 
931
1021
  {/* Stacked Colored Bar for Full 6.0 MB Bundle */}
932
1022
  <View style={bundleStyles.treemapBar}>
933
- <View style={{ flex: summary.nodeModulesPct, backgroundColor: AppColors.indigo500, height: 16 }}/>
934
- <View style={{ flex: summary.metroPct, backgroundColor: AppColors.amber500, height: 16 }}/>
935
- <View style={{ flex: summary.appSourcePct, backgroundColor: AppColors.sky500, height: 16 }}/>
936
- <View style={{ flex: summary.assetsMediaPct, backgroundColor: AppColors.pink500, height: 16 }}/>
1023
+ <View style={{
1024
+ flex: summary.nodeModulesPct,
1025
+ backgroundColor: AppColors.indigo500,
1026
+ height: 16,
1027
+ }}/>
1028
+ <View style={{
1029
+ flex: summary.metroPct,
1030
+ backgroundColor: AppColors.amber500,
1031
+ height: 16,
1032
+ }}/>
1033
+ <View style={{
1034
+ flex: summary.appSourcePct,
1035
+ backgroundColor: AppColors.sky500,
1036
+ height: 16,
1037
+ }}/>
1038
+ <View style={{
1039
+ flex: summary.assetsMediaPct,
1040
+ backgroundColor: AppColors.pink500,
1041
+ height: 16,
1042
+ }}/>
937
1043
  </View>
938
1044
 
939
1045
  {/* Legend Grid */}
@@ -945,7 +1051,8 @@ const BundleTab = React.memo(() => {
945
1051
  <Text style={bundleStyles.legendVal}>
946
1052
  {summary.nodeModulesKb >= 1024
947
1053
  ? `${(summary.nodeModulesKb / 1024).toFixed(1)} MB`
948
- : `${summary.nodeModulesKb} KB`} ({summary.nodeModulesPct}%)
1054
+ : `${summary.nodeModulesKb} KB`}{' '}
1055
+ ({summary.nodeModulesPct}%)
949
1056
  </Text>
950
1057
  </Text>
951
1058
  </View>
@@ -956,7 +1063,8 @@ const BundleTab = React.memo(() => {
956
1063
  <Text style={bundleStyles.legendVal}>
957
1064
  {summary.metroKb >= 1024
958
1065
  ? `${(summary.metroKb / 1024).toFixed(1)} MB`
959
- : `${summary.metroKb} KB`} ({summary.metroPct}%)
1066
+ : `${summary.metroKb} KB`}{' '}
1067
+ ({summary.metroPct}%)
960
1068
  </Text>
961
1069
  </Text>
962
1070
  </View>
@@ -967,7 +1075,8 @@ const BundleTab = React.memo(() => {
967
1075
  <Text style={bundleStyles.legendVal}>
968
1076
  {summary.appSourceKb >= 1024
969
1077
  ? `${(summary.appSourceKb / 1024).toFixed(1)} MB`
970
- : `${summary.appSourceKb} KB`} ({summary.appSourcePct}%)
1078
+ : `${summary.appSourceKb} KB`}{' '}
1079
+ ({summary.appSourcePct}%)
971
1080
  </Text>
972
1081
  </Text>
973
1082
  </View>
@@ -978,7 +1087,8 @@ const BundleTab = React.memo(() => {
978
1087
  <Text style={bundleStyles.legendVal}>
979
1088
  {summary.assetsMediaKb >= 1024
980
1089
  ? `${(summary.assetsMediaKb / 1024).toFixed(1)} MB`
981
- : `${summary.assetsMediaKb} KB`} ({summary.assetsMediaPct}%)
1090
+ : `${summary.assetsMediaKb} KB`}{' '}
1091
+ ({summary.assetsMediaPct}%)
982
1092
  </Text>
983
1093
  </Text>
984
1094
  </View>
@@ -988,10 +1098,12 @@ const BundleTab = React.memo(() => {
988
1098
  {/* Detailed Category Footprint Cards */}
989
1099
  <View style={bundleStyles.sectionCard}>
990
1100
  <View style={bundleStyles.sectionHeaderRow}>
991
- <Text style={bundleStyles.sectionTitle}>{t('bundle.categoryBreakdown')}</Text>
1101
+ <Text style={bundleStyles.sectionTitle}>
1102
+ {t('bundle.categoryBreakdown')}
1103
+ </Text>
992
1104
  <CopyButton value={() => summary} label={t('bundle.categoriesJson')}/>
993
1105
  </View>
994
-
1106
+
995
1107
  {summary.categories.map((cat, cIdx) => (<Pressable key={cIdx} accessible={true} accessibilityRole="button" accessibilityLabel={`${cat.title}, ${cat.sizeKb >= 1024 ? `${cat.sizeMb} MB` : `${cat.sizeKb} KB`}, ${cat.pct}% of bundle`} accessibilityHint={`Double tap to view ${cat.title} details`} style={({ pressed }) => [
996
1108
  bundleStyles.catRowCard,
997
1109
  pressed && { opacity: 0.85, transform: [{ scale: 0.99 }] },
@@ -999,25 +1111,42 @@ const BundleTab = React.memo(() => {
999
1111
  <View style={bundleStyles.catRowTop}>
1000
1112
  <View style={[
1001
1113
  bundleStyles.catIconBox,
1002
- { backgroundColor: `${cat.color}15`, borderColor: `${cat.color}30` },
1114
+ {
1115
+ backgroundColor: `${cat.color}15`,
1116
+ borderColor: `${cat.color}30`,
1117
+ },
1003
1118
  ]}>
1004
1119
  <BundleCategoryIcon type={cat.iconType} size={18} color={cat.color}/>
1005
1120
  </View>
1006
1121
 
1007
1122
  <View style={bundleStyles.catRowTitleWrap}>
1008
- <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', gap: 6 }}>
1123
+ <View style={{
1124
+ flexDirection: 'row',
1125
+ alignItems: 'center',
1126
+ justifyContent: 'space-between',
1127
+ gap: 6,
1128
+ }}>
1009
1129
  <Text style={bundleStyles.catRowTitle} numberOfLines={1}>
1010
1130
  {cat.title}
1011
1131
  </Text>
1012
- <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
1132
+ <View style={{
1133
+ flexDirection: 'row',
1134
+ alignItems: 'center',
1135
+ gap: 6,
1136
+ }}>
1013
1137
  <Text style={bundleStyles.catRowSize}>
1014
- {cat.sizeKb >= 1024 ? `${cat.sizeMb} MB` : `${cat.sizeKb} KB`}
1138
+ {cat.sizeKb >= 1024
1139
+ ? `${cat.sizeMb} MB`
1140
+ : `${cat.sizeKb} KB`}
1015
1141
  </Text>
1016
1142
  <View style={[
1017
1143
  bundleStyles.catPctPill,
1018
1144
  { backgroundColor: `${cat.color}18` },
1019
1145
  ]}>
1020
- <Text style={[bundleStyles.catPctText, { color: cat.color }]}>
1146
+ <Text style={[
1147
+ bundleStyles.catPctText,
1148
+ { color: cat.color },
1149
+ ]}>
1021
1150
  {cat.pct}%
1022
1151
  </Text>
1023
1152
  </View>
@@ -1068,20 +1197,43 @@ const BundleTab = React.memo(() => {
1068
1197
  {/* 4-Pill Chip Breakdown */}
1069
1198
  <View style={bundleStyles.totalFormulaGrid}>
1070
1199
  <View style={bundleStyles.totalChip}>
1071
- <View style={[bundleStyles.totalDot, { backgroundColor: AppColors.indigo500 }]}/>
1072
- <Text style={bundleStyles.totalChipText}>NPM: {(summary.nodeModulesKb / 1024).toFixed(1)}M ({summary.nodeModulesPct}%)</Text>
1200
+ <View style={[
1201
+ bundleStyles.totalDot,
1202
+ { backgroundColor: AppColors.indigo500 },
1203
+ ]}/>
1204
+ <Text style={bundleStyles.totalChipText}>
1205
+ NPM: {(summary.nodeModulesKb / 1024).toFixed(1)}M (
1206
+ {summary.nodeModulesPct}%)
1207
+ </Text>
1073
1208
  </View>
1074
1209
  <View style={bundleStyles.totalChip}>
1075
- <View style={[bundleStyles.totalDot, { backgroundColor: AppColors.amber500 }]}/>
1076
- <Text style={bundleStyles.totalChipText}>Metro: {(summary.metroKb / 1024).toFixed(1)}M ({summary.metroPct}%)</Text>
1210
+ <View style={[
1211
+ bundleStyles.totalDot,
1212
+ { backgroundColor: AppColors.amber500 },
1213
+ ]}/>
1214
+ <Text style={bundleStyles.totalChipText}>
1215
+ Metro: {(summary.metroKb / 1024).toFixed(1)}M (
1216
+ {summary.metroPct}%)
1217
+ </Text>
1077
1218
  </View>
1078
1219
  <View style={bundleStyles.totalChip}>
1079
- <View style={[bundleStyles.totalDot, { backgroundColor: AppColors.sky500 }]}/>
1080
- <Text style={bundleStyles.totalChipText}>Source: {(summary.appSourceKb / 1024).toFixed(1)}M ({summary.appSourcePct}%)</Text>
1220
+ <View style={[
1221
+ bundleStyles.totalDot,
1222
+ { backgroundColor: AppColors.sky500 },
1223
+ ]}/>
1224
+ <Text style={bundleStyles.totalChipText}>
1225
+ Source: {(summary.appSourceKb / 1024).toFixed(1)}M (
1226
+ {summary.appSourcePct}%)
1227
+ </Text>
1081
1228
  </View>
1082
1229
  <View style={bundleStyles.totalChip}>
1083
- <View style={[bundleStyles.totalDot, { backgroundColor: AppColors.pink500 }]}/>
1084
- <Text style={bundleStyles.totalChipText}>Media: {summary.assetsMediaKb}K ({summary.assetsMediaPct}%)</Text>
1230
+ <View style={[
1231
+ bundleStyles.totalDot,
1232
+ { backgroundColor: AppColors.pink500 },
1233
+ ]}/>
1234
+ <Text style={bundleStyles.totalChipText}>
1235
+ Media: {summary.assetsMediaKb}K ({summary.assetsMediaPct}%)
1236
+ </Text>
1085
1237
  </View>
1086
1238
  </View>
1087
1239
  </View>
@@ -1090,10 +1242,13 @@ const BundleTab = React.memo(() => {
1090
1242
  {/* Detailed Subsystem Architecture Split-up */}
1091
1243
  <View style={bundleStyles.sectionCard}>
1092
1244
  <View style={bundleStyles.sectionHeaderRow}>
1093
- <View>
1094
- <Text style={bundleStyles.sectionTitle}>Bundle Subsystem Breakdown</Text>
1245
+ <View style={{ flex: 1, paddingRight: 4 }}>
1246
+ <Text style={bundleStyles.sectionTitle}>
1247
+ Bundle Subsystem Breakdown
1248
+ </Text>
1095
1249
  <Text style={bundleStyles.sectionSub}>
1096
- Granular analysis of packages, application source, engine runtime & assets
1250
+ Granular analysis of packages, application source, engine
1251
+ runtime & assets
1097
1252
  </Text>
1098
1253
  </View>
1099
1254
  <CopyButton value={() => ({
@@ -1102,8 +1257,11 @@ const BundleTab = React.memo(() => {
1102
1257
  hermesEngine: isHermes ? 'Hermes (Bytecode)' : 'JSC (Source)',
1103
1258
  jsDevKb: summary.totalKb,
1104
1259
  jsHermesReleaseKb: Math.round(summary.totalKb * 0.38),
1105
- appSourceFiles: bundleFiles.filter(f => f.category === 'typescript' || f.category === 'javascript').length,
1106
- mediaAssets: bundleFiles.filter(f => f.category === 'image' || f.category === 'font' || f.category === 'json').length,
1260
+ appSourceFiles: bundleFiles.filter(f => f.category === 'typescript' ||
1261
+ f.category === 'javascript').length,
1262
+ mediaAssets: bundleFiles.filter(f => f.category === 'image' ||
1263
+ f.category === 'font' ||
1264
+ f.category === 'json').length,
1107
1265
  })} label="Subsystem Split JSON"/>
1108
1266
  </View>
1109
1267
 
@@ -1112,28 +1270,51 @@ const BundleTab = React.memo(() => {
1112
1270
  <View style={bundleStyles.subsystemHeader}>
1113
1271
  <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
1114
1272
  <BundleCategoryIcon type="deps" size={16} color={AppColors.indigo500}/>
1115
- <Text style={bundleStyles.subsystemTitle}>NPM & Third-Party Dependencies</Text>
1273
+ <Text style={bundleStyles.subsystemTitle}>
1274
+ NPM & Third-Party Dependencies
1275
+ </Text>
1116
1276
  </View>
1117
- <View style={[bundleStyles.subsystemBadge, { backgroundColor: `${AppColors.indigo500}18` }]}>
1118
- <Text style={[bundleStyles.subsystemBadgeText, { color: AppColors.indigo500 }]}>
1119
- {(summary.nodeModulesKb / 1024).toFixed(1)} MB • {summary.nodeModulesPct}%
1277
+ <View style={[
1278
+ bundleStyles.subsystemBadge,
1279
+ { backgroundColor: `${AppColors.indigo500}18` },
1280
+ ]}>
1281
+ <Text style={[
1282
+ bundleStyles.subsystemBadgeText,
1283
+ { color: AppColors.indigo500 },
1284
+ ]}>
1285
+ {(summary.nodeModulesKb / 1024).toFixed(1)} MB •{' '}
1286
+ {summary.nodeModulesPct}%
1120
1287
  </Text>
1121
1288
  </View>
1122
1289
  </View>
1123
1290
  <View style={bundleStyles.subsystemGrid}>
1124
1291
  <View style={bundleStyles.subsystemItem}>
1125
1292
  <Text style={bundleStyles.subsystemItemText}>
1126
- Direct: <Text style={bundleStyles.subsystemItemVal}>{bundlePackages.filter(p => p.type === 'direct').length || 12} pkgs</Text>
1293
+ Direct:{' '}
1294
+ <Text style={bundleStyles.subsystemItemVal}>
1295
+ {bundlePackages.filter(p => p.type === 'direct').length ||
1296
+ 12}{' '}
1297
+ pkgs
1298
+ </Text>
1127
1299
  </Text>
1128
1300
  </View>
1129
1301
  <View style={bundleStyles.subsystemItem}>
1130
1302
  <Text style={bundleStyles.subsystemItemText}>
1131
- Transitive: <Text style={bundleStyles.subsystemItemVal}>{bundlePackages.filter(p => p.type !== 'direct').length || 41} pkgs</Text>
1303
+ Transitive:{' '}
1304
+ <Text style={bundleStyles.subsystemItemVal}>
1305
+ {bundlePackages.filter(p => p.type !== 'direct').length ||
1306
+ 41}{' '}
1307
+ pkgs
1308
+ </Text>
1132
1309
  </Text>
1133
1310
  </View>
1134
1311
  <View style={bundleStyles.subsystemItem}>
1135
1312
  <Text style={bundleStyles.subsystemItemText}>
1136
- Top Package: <Text style={bundleStyles.subsystemItemVal}>{bundlePackages[0]?.name || 'react-native'} ({bundlePackages[0]?.sizeKb || 1240} KB)</Text>
1313
+ Top Package:{' '}
1314
+ <Text style={bundleStyles.subsystemItemVal}>
1315
+ {bundlePackages[0]?.name || 'react-native'} (
1316
+ {bundlePackages[0]?.sizeKb || 1240} KB)
1317
+ </Text>
1137
1318
  </Text>
1138
1319
  </View>
1139
1320
  </View>
@@ -1144,28 +1325,50 @@ const BundleTab = React.memo(() => {
1144
1325
  <View style={bundleStyles.subsystemHeader}>
1145
1326
  <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
1146
1327
  <BundleCategoryIcon type="source" size={16} color={AppColors.sky500}/>
1147
- <Text style={bundleStyles.subsystemTitle}>Application Source Code</Text>
1328
+ <Text style={bundleStyles.subsystemTitle}>
1329
+ Application Source Code
1330
+ </Text>
1148
1331
  </View>
1149
- <View style={[bundleStyles.subsystemBadge, { backgroundColor: `${AppColors.sky500}18` }]}>
1150
- <Text style={[bundleStyles.subsystemBadgeText, { color: AppColors.sky500 }]}>
1151
- {(summary.appSourceKb / 1024).toFixed(1)} MB • {summary.appSourcePct}%
1332
+ <View style={[
1333
+ bundleStyles.subsystemBadge,
1334
+ { backgroundColor: `${AppColors.sky500}18` },
1335
+ ]}>
1336
+ <Text style={[
1337
+ bundleStyles.subsystemBadgeText,
1338
+ { color: AppColors.sky500 },
1339
+ ]}>
1340
+ {(summary.appSourceKb / 1024).toFixed(1)} MB •{' '}
1341
+ {summary.appSourcePct}%
1152
1342
  </Text>
1153
1343
  </View>
1154
1344
  </View>
1155
1345
  <View style={bundleStyles.subsystemGrid}>
1156
1346
  <View style={bundleStyles.subsystemItem}>
1157
1347
  <Text style={bundleStyles.subsystemItemText}>
1158
- TypeScript/TSX: <Text style={bundleStyles.subsystemItemVal}>{bundleFiles.filter(f => f.category === 'typescript').length || 14} files</Text>
1348
+ TypeScript/TSX:{' '}
1349
+ <Text style={bundleStyles.subsystemItemVal}>
1350
+ {bundleFiles.filter(f => f.category === 'typescript')
1351
+ .length || 14}{' '}
1352
+ files
1353
+ </Text>
1159
1354
  </Text>
1160
1355
  </View>
1161
1356
  <View style={bundleStyles.subsystemItem}>
1162
1357
  <Text style={bundleStyles.subsystemItemText}>
1163
- JavaScript/JSX: <Text style={bundleStyles.subsystemItemVal}>{bundleFiles.filter(f => f.category === 'javascript').length || 3} files</Text>
1358
+ JavaScript/JSX:{' '}
1359
+ <Text style={bundleStyles.subsystemItemVal}>
1360
+ {bundleFiles.filter(f => f.category === 'javascript')
1361
+ .length || 3}{' '}
1362
+ files
1363
+ </Text>
1164
1364
  </Text>
1165
1365
  </View>
1166
1366
  <View style={bundleStyles.subsystemItem}>
1167
1367
  <Text style={bundleStyles.subsystemItemText}>
1168
- Tracked Modules: <Text style={bundleStyles.subsystemItemVal}>{bundleFiles.length || 18} files</Text>
1368
+ Tracked Modules:{' '}
1369
+ <Text style={bundleStyles.subsystemItemVal}>
1370
+ {bundleFiles.length || 18} files
1371
+ </Text>
1169
1372
  </Text>
1170
1373
  </View>
1171
1374
  </View>
@@ -1176,10 +1379,18 @@ const BundleTab = React.memo(() => {
1176
1379
  <View style={bundleStyles.subsystemHeader}>
1177
1380
  <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
1178
1381
  <BundleCategoryIcon type="bolt" size={16} color={AppColors.emerald600}/>
1179
- <Text style={bundleStyles.subsystemTitle}>Hermes Bytecode Engine</Text>
1382
+ <Text style={bundleStyles.subsystemTitle}>
1383
+ Hermes Bytecode Engine
1384
+ </Text>
1180
1385
  </View>
1181
- <View style={[bundleStyles.subsystemBadge, { backgroundColor: `${AppColors.emerald500}18` }]}>
1182
- <Text style={[bundleStyles.subsystemBadgeText, { color: AppColors.emerald700 }]}>
1386
+ <View style={[
1387
+ bundleStyles.subsystemBadge,
1388
+ { backgroundColor: `${AppColors.emerald500}18` },
1389
+ ]}>
1390
+ <Text style={[
1391
+ bundleStyles.subsystemBadgeText,
1392
+ { color: AppColors.emerald700 },
1393
+ ]}>
1183
1394
  ~62% AOT Compression
1184
1395
  </Text>
1185
1396
  </View>
@@ -1187,17 +1398,26 @@ const BundleTab = React.memo(() => {
1187
1398
  <View style={bundleStyles.subsystemGrid}>
1188
1399
  <View style={bundleStyles.subsystemItem}>
1189
1400
  <Text style={bundleStyles.subsystemItemText}>
1190
- Dev JS Size: <Text style={bundleStyles.subsystemItemVal}>{summary.totalMb} MB</Text>
1401
+ Dev JS Size:{' '}
1402
+ <Text style={bundleStyles.subsystemItemVal}>
1403
+ {summary.totalMb} MB
1404
+ </Text>
1191
1405
  </Text>
1192
1406
  </View>
1193
1407
  <View style={bundleStyles.subsystemItem}>
1194
1408
  <Text style={bundleStyles.subsystemItemText}>
1195
- Hermes Bytecode: <Text style={bundleStyles.subsystemItemVal}>{((summary.totalKb * 0.38) / 1024).toFixed(2)} MB</Text>
1409
+ Hermes Bytecode:{' '}
1410
+ <Text style={bundleStyles.subsystemItemVal}>
1411
+ {((summary.totalKb * 0.38) / 1024).toFixed(2)} MB
1412
+ </Text>
1196
1413
  </Text>
1197
1414
  </View>
1198
1415
  <View style={bundleStyles.subsystemItem}>
1199
1416
  <Text style={bundleStyles.subsystemItemText}>
1200
- Execution: <Text style={bundleStyles.subsystemItemVal}>{isHermes ? 'Hermes JSI Direct' : 'JSC Standard'}</Text>
1417
+ Execution:{' '}
1418
+ <Text style={bundleStyles.subsystemItemVal}>
1419
+ {isHermes ? 'Hermes JSI Direct' : 'JSC Standard'}
1420
+ </Text>
1201
1421
  </Text>
1202
1422
  </View>
1203
1423
  </View>
@@ -1208,10 +1428,18 @@ const BundleTab = React.memo(() => {
1208
1428
  <View style={bundleStyles.subsystemHeader}>
1209
1429
  <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
1210
1430
  <BundleCategoryIcon type="media" size={16} color={AppColors.pink500}/>
1211
- <Text style={bundleStyles.subsystemTitle}>Images, Fonts & Static Assets</Text>
1431
+ <Text style={bundleStyles.subsystemTitle}>
1432
+ Images, Fonts & Static Assets
1433
+ </Text>
1212
1434
  </View>
1213
- <View style={[bundleStyles.subsystemBadge, { backgroundColor: `${AppColors.pink500}18` }]}>
1214
- <Text style={[bundleStyles.subsystemBadgeText, { color: AppColors.pink500 }]}>
1435
+ <View style={[
1436
+ bundleStyles.subsystemBadge,
1437
+ { backgroundColor: `${AppColors.pink500}18` },
1438
+ ]}>
1439
+ <Text style={[
1440
+ bundleStyles.subsystemBadgeText,
1441
+ { color: AppColors.pink500 },
1442
+ ]}>
1215
1443
  {summary.assetsMediaKb} KB • {summary.assetsMediaPct}%
1216
1444
  </Text>
1217
1445
  </View>
@@ -1219,17 +1447,32 @@ const BundleTab = React.memo(() => {
1219
1447
  <View style={bundleStyles.subsystemGrid}>
1220
1448
  <View style={bundleStyles.subsystemItem}>
1221
1449
  <Text style={bundleStyles.subsystemItemText}>
1222
- Images & Icons: <Text style={bundleStyles.subsystemItemVal}>{bundleFiles.filter(f => f.category === 'image').length || 2} assets</Text>
1450
+ Images & Icons:{' '}
1451
+ <Text style={bundleStyles.subsystemItemVal}>
1452
+ {bundleFiles.filter(f => f.category === 'image').length ||
1453
+ 2}{' '}
1454
+ assets
1455
+ </Text>
1223
1456
  </Text>
1224
1457
  </View>
1225
1458
  <View style={bundleStyles.subsystemItem}>
1226
1459
  <Text style={bundleStyles.subsystemItemText}>
1227
- JSON Data: <Text style={bundleStyles.subsystemItemVal}>{bundleFiles.filter(f => f.category === 'json').length || 1} files</Text>
1460
+ JSON Data:{' '}
1461
+ <Text style={bundleStyles.subsystemItemVal}>
1462
+ {bundleFiles.filter(f => f.category === 'json').length ||
1463
+ 1}{' '}
1464
+ files
1465
+ </Text>
1228
1466
  </Text>
1229
1467
  </View>
1230
1468
  <View style={bundleStyles.subsystemItem}>
1231
1469
  <Text style={bundleStyles.subsystemItemText}>
1232
- Custom Fonts: <Text style={bundleStyles.subsystemItemVal}>{bundleFiles.filter(f => f.category === 'font').length || 0} fonts</Text>
1470
+ Custom Fonts:{' '}
1471
+ <Text style={bundleStyles.subsystemItemVal}>
1472
+ {bundleFiles.filter(f => f.category === 'font').length ||
1473
+ 0}{' '}
1474
+ fonts
1475
+ </Text>
1233
1476
  </Text>
1234
1477
  </View>
1235
1478
  </View>
@@ -1241,13 +1484,24 @@ const BundleTab = React.memo(() => {
1241
1484
  {/* ── 2. TAB: PRODUCTION BUILDS & PLATFORMS ───────────────────────────── */}
1242
1485
  {/* ══════════════════════════════════════════════════════════════════════ */}
1243
1486
  {activeSubTab === 'production' && (<ScrollView style={{ flex: 1 }} contentContainerStyle={bundleStyles.contentContainer} keyboardShouldPersistTaps="handled">
1244
-
1245
1487
  {/* Platform Segmented Switcher (Horizontally Scrollable) */}
1246
1488
  <ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={bundleStyles.prodPlatformScroll}>
1247
1489
  {[
1248
- { key: 'ios', label: 'iOS App (.ipa)', badge: `${analysis?.production.ios.totalDownloadMb || 111.9} MB` },
1249
- { key: 'androidAab', label: 'Android AAB (.aab)', badge: `${analysis?.production.androidAab.totalDownloadMb || 38.5} MB` },
1250
- { key: 'androidApk', label: 'Universal APK (.apk)', badge: `${analysis?.production.androidApk.totalInstallMb || 364.0} MB` },
1490
+ {
1491
+ key: 'ios',
1492
+ label: 'iOS App (.ipa)',
1493
+ badge: `${analysis?.production.ios.totalDownloadMb || 111.9} MB`,
1494
+ },
1495
+ {
1496
+ key: 'androidAab',
1497
+ label: 'Android AAB (.aab)',
1498
+ badge: `${analysis?.production.androidAab.totalDownloadMb || 38.5} MB`,
1499
+ },
1500
+ {
1501
+ key: 'androidApk',
1502
+ label: 'Universal APK (.apk)',
1503
+ badge: `${analysis?.production.androidApk.totalInstallMb || 364.0} MB`,
1504
+ },
1251
1505
  ].map(tab => {
1252
1506
  const isSelected = prodPlatform === tab.key;
1253
1507
  return (<TouchableScale key={tab.key} accessible={true} accessibilityRole="tab" accessibilityLabel={`${tab.label}, ${tab.badge}`} accessibilityState={{ selected: isSelected }} accessibilityHint={`Switches production estimate to ${tab.label}`} onPress={() => setProdPlatform(tab.key)} style={[
@@ -1304,31 +1558,51 @@ const BundleTab = React.memo(() => {
1304
1558
  <View style={bundleStyles.metricsGrid}>
1305
1559
  <View style={bundleStyles.metricBox}>
1306
1560
  <Text style={bundleStyles.metricLabel}>INSTALL SIZE</Text>
1307
- <Text style={[bundleStyles.metricValue, { color: AppColors.purple }]}>
1561
+ <Text style={[
1562
+ bundleStyles.metricValue,
1563
+ { color: AppColors.purple },
1564
+ ]}>
1308
1565
  {analysis.production[prodPlatform].totalInstallMb} MB
1309
1566
  </Text>
1310
- <Text style={bundleStyles.metricHint}>On-device uncompressed footprint</Text>
1567
+ <Text style={bundleStyles.metricHint}>
1568
+ On-device uncompressed footprint
1569
+ </Text>
1311
1570
  </View>
1312
1571
 
1313
1572
  <View style={bundleStyles.metricBox}>
1314
1573
  <Text style={bundleStyles.metricLabel}>DOWNLOAD SIZE</Text>
1315
- <Text style={[bundleStyles.metricValue, { color: AppColors.emerald600 }]}>
1574
+ <Text style={[
1575
+ bundleStyles.metricValue,
1576
+ { color: AppColors.emerald600 },
1577
+ ]}>
1316
1578
  ~{analysis.production[prodPlatform].totalDownloadMb} MB
1317
1579
  </Text>
1318
- <Text style={bundleStyles.metricHint}>Store network transfer payload</Text>
1580
+ <Text style={bundleStyles.metricHint}>
1581
+ Store network transfer payload
1582
+ </Text>
1319
1583
  </View>
1320
1584
 
1321
1585
  <View style={bundleStyles.metricBox}>
1322
1586
  <Text style={bundleStyles.metricLabel}>COMPRESSION</Text>
1323
- <Text style={[bundleStyles.metricValue, { color: AppColors.sky500 }]}>
1587
+ <Text style={[
1588
+ bundleStyles.metricValue,
1589
+ { color: AppColors.sky500 },
1590
+ ]}>
1324
1591
  {analysis.production[prodPlatform].compressionRatioPct}%
1325
1592
  </Text>
1326
- <Text style={bundleStyles.metricHint}>Bytecode & asset ratio</Text>
1593
+ <Text style={bundleStyles.metricHint}>
1594
+ Bytecode & asset ratio
1595
+ </Text>
1327
1596
  </View>
1328
1597
 
1329
1598
  <View style={bundleStyles.metricBox}>
1330
- <Text style={bundleStyles.metricLabel}>FORMAT ARCHITECTURE</Text>
1331
- <Text style={[bundleStyles.metricValue, { color: AppColors.amber700 }]}>
1599
+ <Text style={bundleStyles.metricLabel}>
1600
+ FORMAT ARCHITECTURE
1601
+ </Text>
1602
+ <Text style={[
1603
+ bundleStyles.metricValue,
1604
+ { color: AppColors.amber700 },
1605
+ ]}>
1332
1606
  {prodPlatform === 'ios'
1333
1607
  ? 'Mach-O 64-bit'
1334
1608
  : prodPlatform === 'androidAab'
@@ -1349,10 +1623,13 @@ const BundleTab = React.memo(() => {
1349
1623
  {/* Component Breakdown Cards */}
1350
1624
  {analysis && (<View style={bundleStyles.sectionCard}>
1351
1625
  <View style={bundleStyles.sectionHeaderRow}>
1352
- <View>
1353
- <Text style={bundleStyles.sectionTitle}>Binary Component Architecture</Text>
1626
+ <View style={{ flex: 1, paddingRight: 4 }}>
1627
+ <Text style={bundleStyles.sectionTitle}>
1628
+ Binary Component Architecture
1629
+ </Text>
1354
1630
  <Text style={bundleStyles.sectionSub}>
1355
- Compiled native libraries, runtime bytecodes, assets, and signature blocks
1631
+ Compiled native libraries, runtime bytecodes, assets, and
1632
+ signature blocks
1356
1633
  </Text>
1357
1634
  </View>
1358
1635
  <CopyButton value={() => analysis.production[prodPlatform].components} label="Components JSON"/>
@@ -1370,26 +1647,29 @@ const BundleTab = React.memo(() => {
1370
1647
  {/* Production Binary Legend */}
1371
1648
  <View style={[bundleStyles.legendGrid, { marginBottom: 12 }]}>
1372
1649
  {analysis.production[prodPlatform].components.map((comp, cIdx) => (<View key={cIdx} style={bundleStyles.legendItem}>
1373
- <View style={{
1650
+ <View style={{
1374
1651
  width: 8,
1375
1652
  height: 8,
1376
1653
  borderRadius: 4,
1377
1654
  backgroundColor: comp.color,
1378
1655
  }}/>
1379
- <Text style={bundleStyles.legendText} numberOfLines={1}>
1380
- {comp.name}{' '}
1381
- <Text style={bundleStyles.legendVal}>
1382
- {comp.sizeMb} MB ({comp.pct}%)
1656
+ <Text style={bundleStyles.legendText} numberOfLines={1}>
1657
+ {comp.name}{' '}
1658
+ <Text style={bundleStyles.legendVal}>
1659
+ {comp.sizeMb} MB ({comp.pct}%)
1660
+ </Text>
1383
1661
  </Text>
1384
- </Text>
1385
- </View>))}
1662
+ </View>))}
1386
1663
  </View>
1387
1664
 
1388
1665
  {analysis.production[prodPlatform].components.map((comp, idx) => (<View key={idx} style={bundleStyles.catRowCard}>
1389
1666
  <View style={bundleStyles.catRowTop}>
1390
1667
  <View style={[
1391
1668
  bundleStyles.catIconBox,
1392
- { backgroundColor: `${comp.color}15`, borderColor: `${comp.color}30` },
1669
+ {
1670
+ backgroundColor: `${comp.color}15`,
1671
+ borderColor: `${comp.color}30`,
1672
+ },
1393
1673
  ]}>
1394
1674
  <BundleCategoryIcon type={comp.category === 'native'
1395
1675
  ? 'source'
@@ -1403,17 +1683,31 @@ const BundleTab = React.memo(() => {
1403
1683
  </View>
1404
1684
 
1405
1685
  <View style={bundleStyles.catRowTitleWrap}>
1406
- <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', gap: 6 }}>
1686
+ <View style={{
1687
+ flexDirection: 'row',
1688
+ alignItems: 'center',
1689
+ justifyContent: 'space-between',
1690
+ gap: 6,
1691
+ }}>
1407
1692
  <Text style={bundleStyles.catRowTitle} numberOfLines={1}>
1408
1693
  {comp.name}
1409
1694
  </Text>
1410
- <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
1411
- <Text style={bundleStyles.catRowSize}>{comp.sizeMb} MB</Text>
1695
+ <View style={{
1696
+ flexDirection: 'row',
1697
+ alignItems: 'center',
1698
+ gap: 6,
1699
+ }}>
1700
+ <Text style={bundleStyles.catRowSize}>
1701
+ {comp.sizeMb} MB
1702
+ </Text>
1412
1703
  <View style={[
1413
1704
  bundleStyles.catPctPill,
1414
1705
  { backgroundColor: `${comp.color}18` },
1415
1706
  ]}>
1416
- <Text style={[bundleStyles.catPctText, { color: comp.color }]}>
1707
+ <Text style={[
1708
+ bundleStyles.catPctText,
1709
+ { color: comp.color },
1710
+ ]}>
1417
1711
  {comp.pct}%
1418
1712
  </Text>
1419
1713
  </View>
@@ -1433,9 +1727,16 @@ const BundleTab = React.memo(() => {
1433
1727
  ]}/>
1434
1728
  </View>
1435
1729
 
1436
- {comp.advice ? (<View style={[bundleStyles.adviceBadge, bundleStyles.adviceOptimal, { marginTop: 6 }]}>
1730
+ {comp.advice ? (<View style={[
1731
+ bundleStyles.adviceBadge,
1732
+ bundleStyles.adviceOptimal,
1733
+ { marginTop: 6 },
1734
+ ]}>
1437
1735
  <LightbulbIcon color={AppColors.emerald700} size={13}/>
1438
- <Text style={[bundleStyles.adviceText, { color: AppColors.emerald700, flex: 1, marginLeft: 4 }]}>
1736
+ <Text style={[
1737
+ bundleStyles.adviceText,
1738
+ { color: AppColors.emerald700, flex: 1, marginLeft: 4 },
1739
+ ]}>
1439
1740
  {comp.advice}
1440
1741
  </Text>
1441
1742
  </View>) : null}
@@ -1470,21 +1771,33 @@ const BundleTab = React.memo(() => {
1470
1771
  {/* Platform Metric Grid Chips */}
1471
1772
  <View style={bundleStyles.totalFormulaGrid}>
1472
1773
  <View style={bundleStyles.totalChip}>
1473
- <View style={[bundleStyles.totalDot, { backgroundColor: AppColors.purple }]}/>
1774
+ <View style={[
1775
+ bundleStyles.totalDot,
1776
+ { backgroundColor: AppColors.purple },
1777
+ ]}/>
1474
1778
  <Text style={bundleStyles.totalChipText}>
1475
- Install: {analysis.production[prodPlatform].totalInstallMb} MB
1779
+ Install:{' '}
1780
+ {analysis.production[prodPlatform].totalInstallMb} MB
1476
1781
  </Text>
1477
1782
  </View>
1478
1783
  <View style={bundleStyles.totalChip}>
1479
- <View style={[bundleStyles.totalDot, { backgroundColor: AppColors.sky500 }]}/>
1784
+ <View style={[
1785
+ bundleStyles.totalDot,
1786
+ { backgroundColor: AppColors.sky500 },
1787
+ ]}/>
1480
1788
  <Text style={bundleStyles.totalChipText}>
1481
- Download: ~{analysis.production[prodPlatform].totalDownloadMb} MB
1789
+ Download: ~
1790
+ {analysis.production[prodPlatform].totalDownloadMb} MB
1482
1791
  </Text>
1483
1792
  </View>
1484
1793
  <View style={bundleStyles.totalChip}>
1485
- <View style={[bundleStyles.totalDot, { backgroundColor: AppColors.emerald500 }]}/>
1794
+ <View style={[
1795
+ bundleStyles.totalDot,
1796
+ { backgroundColor: AppColors.emerald500 },
1797
+ ]}/>
1486
1798
  <Text style={bundleStyles.totalChipText}>
1487
- Compression: {analysis.production[prodPlatform].compressionRatioPct}%
1799
+ Compression:{' '}
1800
+ {analysis.production[prodPlatform].compressionRatioPct}%
1488
1801
  </Text>
1489
1802
  </View>
1490
1803
  </View>
@@ -1521,8 +1834,16 @@ const BundleTab = React.memo(() => {
1521
1834
  return (<TouchableScale key={tab.key} accessible={true} accessibilityRole="button" accessibilityLabel={`Filter by ${t(tab.labelKey)}, ${count} items`} accessibilityState={{ selected: isActive }} accessibilityHint={`Filters files list to ${t(tab.labelKey)}`} onPress={() => setActiveCategory(tab.key)} style={[
1522
1835
  bundleStyles.catPill,
1523
1836
  isActive && bundleStyles.catPillActive,
1524
- tab.key === 'UNUSED' && isActive && { backgroundColor: AppColors.red500, borderColor: AppColors.red600 },
1525
- tab.key === 'CONSUMED' && isActive && { backgroundColor: AppColors.emerald600, borderColor: AppColors.emerald700 },
1837
+ tab.key === 'UNUSED' &&
1838
+ isActive && {
1839
+ backgroundColor: AppColors.red500,
1840
+ borderColor: AppColors.red600,
1841
+ },
1842
+ tab.key === 'CONSUMED' &&
1843
+ isActive && {
1844
+ backgroundColor: AppColors.emerald600,
1845
+ borderColor: AppColors.emerald700,
1846
+ },
1526
1847
  ]}>
1527
1848
  <BundleCategoryIcon type={tab.iconType} size={12} color={isActive ? AppColors.white : AppColors.grayTextStrong}/>
1528
1849
  <Text style={[
@@ -1538,7 +1859,6 @@ const BundleTab = React.memo(() => {
1538
1859
 
1539
1860
  {/* Files List / Tree Content */}
1540
1861
  <ScrollView style={{ flex: 1 }} contentContainerStyle={bundleStyles.contentContainer} keyboardShouldPersistTaps="handled">
1541
-
1542
1862
  {/* View Mode Toggle Header Bar */}
1543
1863
  <View style={bundleStyles.treeHeaderRow}>
1544
1864
  <View style={bundleStyles.viewModeToggleGroup}>
@@ -1548,7 +1868,8 @@ const BundleTab = React.memo(() => {
1548
1868
  ]}>
1549
1869
  <Text style={[
1550
1870
  bundleStyles.viewModeBtnText,
1551
- filesViewMode === 'tree' && bundleStyles.viewModeBtnTextActive,
1871
+ filesViewMode === 'tree' &&
1872
+ bundleStyles.viewModeBtnTextActive,
1552
1873
  ]}>
1553
1874
  {t('bundle.treeView')}
1554
1875
  </Text>
@@ -1559,7 +1880,8 @@ const BundleTab = React.memo(() => {
1559
1880
  ]}>
1560
1881
  <Text style={[
1561
1882
  bundleStyles.viewModeBtnText,
1562
- filesViewMode === 'list' && bundleStyles.viewModeBtnTextActive,
1883
+ filesViewMode === 'list' &&
1884
+ bundleStyles.viewModeBtnTextActive,
1563
1885
  ]}>
1564
1886
  {t('bundle.flatList')}
1565
1887
  </Text>
@@ -1568,11 +1890,17 @@ const BundleTab = React.memo(() => {
1568
1890
 
1569
1891
  {filesViewMode === 'tree' ? (<View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
1570
1892
  <TouchableScale accessible={true} accessibilityRole="button" accessibilityLabel="Expand all folders" onPress={expandAllFolders}>
1571
- <Text style={bundleStyles.treeActionLink}>{t('bundle.expand')}</Text>
1893
+ <Text style={bundleStyles.treeActionLink}>
1894
+ {t('bundle.expand')}
1895
+ </Text>
1572
1896
  </TouchableScale>
1573
- <Text style={{ color: AppColors.grayTextWeak, fontSize: 11 }}>•</Text>
1897
+ <Text style={{ color: AppColors.grayTextWeak, fontSize: 11 }}>
1898
+
1899
+ </Text>
1574
1900
  <TouchableScale accessible={true} accessibilityRole="button" accessibilityLabel="Collapse all folders" onPress={collapseAllFolders}>
1575
- <Text style={bundleStyles.treeActionLink}>{t('bundle.collapse')}</Text>
1901
+ <Text style={bundleStyles.treeActionLink}>
1902
+ {t('bundle.collapse')}
1903
+ </Text>
1576
1904
  </TouchableScale>
1577
1905
  </View>) : (<Text style={bundleStyles.listHeaderCount}>
1578
1906
  {t('bundle.showingFilesOf', {
@@ -1589,9 +1917,13 @@ const BundleTab = React.memo(() => {
1589
1917
  /* ── MODE 2: FLAT LIST VIEW ── */
1590
1918
  filteredFiles.map((file, index) => {
1591
1919
  const categoryMeta = CATEGORY_COLORS[file.category] || CATEGORY_COLORS.typescript;
1592
- const pctOfTotal = ((file.sizeKb / summary.totalKb) * 100).toFixed(1);
1920
+ const pctOfTotal = ((file.sizeKb / summary.totalKb) *
1921
+ 100).toFixed(1);
1593
1922
  const isUnused = file.isConsumed === false;
1594
- return (<View key={file.id} style={[bundleStyles.fileCard, isUnused && bundleStyles.treeFileCardUnused]}>
1923
+ return (<View key={file.id} style={[
1924
+ bundleStyles.fileCard,
1925
+ isUnused && bundleStyles.treeFileCardUnused,
1926
+ ]}>
1595
1927
  <View style={bundleStyles.fileCardTop}>
1596
1928
  <View style={bundleStyles.sNoBadge}>
1597
1929
  <Text style={bundleStyles.sNoText}>#{index + 1}</Text>
@@ -1600,25 +1932,45 @@ const BundleTab = React.memo(() => {
1600
1932
  <MediaPreviewThumbnail file={file}/>
1601
1933
 
1602
1934
  <View style={{ flex: 1, paddingHorizontal: 4 }}>
1603
- <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6, flexWrap: 'wrap' }}>
1935
+ <View style={{
1936
+ flexDirection: 'row',
1937
+ alignItems: 'center',
1938
+ gap: 6,
1939
+ flexWrap: 'wrap',
1940
+ }}>
1604
1941
  <HighlightText text={file.name} search={search} style={bundleStyles.fileName} highlightStyle={bundleStyles.searchHighlight}/>
1605
1942
  {isUnused ? (<View style={bundleStyles.unusedBadge}>
1606
- <Text style={bundleStyles.unusedBadgeText}>{t('bundle.notConsumed')}</Text>
1943
+ <Text style={bundleStyles.unusedBadgeText}>
1944
+ {t('bundle.notConsumed')}
1945
+ </Text>
1607
1946
  </View>) : (<View style={bundleStyles.consumedBadge}>
1608
- <Text style={bundleStyles.consumedBadgeText}>{t('bundle.consumed')}</Text>
1947
+ <Text style={bundleStyles.consumedBadgeText}>
1948
+ {t('bundle.consumed')}
1949
+ </Text>
1609
1950
  </View>)}
1610
1951
  </View>
1611
1952
  <HighlightText text={file.path} search={search} style={bundleStyles.filePath} highlightStyle={bundleStyles.searchHighlight} numberOfLines={1}/>
1612
1953
  </View>
1613
1954
 
1614
- <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
1955
+ <View style={{
1956
+ flexDirection: 'row',
1957
+ alignItems: 'center',
1958
+ gap: 6,
1959
+ }}>
1615
1960
  <View style={bundleStyles.fileSizeBox}>
1616
- <Text style={[bundleStyles.fileSizeKb, isUnused && { color: AppColors.amber800Warm }]}>
1961
+ <Text style={[
1962
+ bundleStyles.fileSizeKb,
1963
+ isUnused && { color: AppColors.amber800Warm },
1964
+ ]}>
1617
1965
  {file.sizeKb >= 1024
1618
- ? t('bundle.fileSizeMb', { size: (file.sizeKb / 1024).toFixed(2) })
1966
+ ? t('bundle.fileSizeMb', {
1967
+ size: (file.sizeKb / 1024).toFixed(2),
1968
+ })
1619
1969
  : t('bundle.fileSizeKb', { size: file.sizeKb })}
1620
1970
  </Text>
1621
- <Text style={bundleStyles.filePercent}>{pctOfTotal}%</Text>
1971
+ <Text style={bundleStyles.filePercent}>
1972
+ {pctOfTotal}%
1973
+ </Text>
1622
1974
  </View>
1623
1975
  <TouchableScale onPress={() => downloadBundleFile(file, analysis?.scriptURL)} style={bundleStyles.downloadFileBtn} hitSlop={8}>
1624
1976
  <DownloadIcon size={14} color={AppColors.sky500}/>
@@ -1632,7 +1984,9 @@ const BundleTab = React.memo(() => {
1632
1984
  bundleStyles.fileProgressBar,
1633
1985
  {
1634
1986
  width: `${Math.min(100, Math.max(4, Number(pctOfTotal) * 4))}%`,
1635
- backgroundColor: isUnused ? AppColors.red500 : categoryMeta.color,
1987
+ backgroundColor: isUnused
1988
+ ? AppColors.red500
1989
+ : categoryMeta.color,
1636
1990
  },
1637
1991
  ]}/>
1638
1992
  </View>
@@ -1691,7 +2045,6 @@ const BundleTab = React.memo(() => {
1691
2045
  {/* ══════════════════════════════════════════════════════════════════════ */}
1692
2046
  {activeSubTab === 'packages' && (<View style={{ flex: 1 }}>
1693
2047
  <ScrollView style={{ flex: 1 }} contentContainerStyle={bundleStyles.contentContainer} keyboardShouldPersistTaps="handled">
1694
-
1695
2048
  <View style={bundleStyles.filterContainer}>
1696
2049
  <View style={bundleStyles.searchRow}>
1697
2050
  <SearchIcon color={AppColors.grayTextWeak} size={15}/>
@@ -1704,14 +2057,25 @@ const BundleTab = React.memo(() => {
1704
2057
  </View>
1705
2058
 
1706
2059
  <Text style={bundleStyles.listHeaderCount}>
1707
- {t('bundle.showingDependencies', { count: filteredPackages.length })}
2060
+ {t('bundle.showingDependencies', {
2061
+ count: filteredPackages.length,
2062
+ })}
1708
2063
  </Text>
1709
2064
 
1710
2065
  {filteredPackages.map((pkg, index) => {
1711
- const cleanInstalledVersion = pkg.version ? pkg.version.replace(/^\^/, '') : '';
1712
- const hasUpdate = !!pkg.latestVersion && !!cleanInstalledVersion &&
2066
+ const cleanInstalledVersion = pkg.version
2067
+ ? pkg.version.replace(/^\^/, '')
2068
+ : '';
2069
+ const hasUpdate = !!pkg.latestVersion &&
2070
+ !!cleanInstalledVersion &&
1713
2071
  pkg.latestVersion !== cleanInstalledVersion;
1714
- return (<View key={pkg.id} style={[bundleStyles.fileCard, pkg.isDeprecated && { borderColor: AppColors.errorBorder, backgroundColor: AppColors.errorCardBg }]}>
2072
+ return (<View key={pkg.id} style={[
2073
+ bundleStyles.fileCard,
2074
+ pkg.isDeprecated && {
2075
+ borderColor: AppColors.errorBorder,
2076
+ backgroundColor: AppColors.errorCardBg,
2077
+ },
2078
+ ]}>
1715
2079
  {/* Header Row: #s.no + Logo + Package Name + Version Pill + Update/Deprecated Badges + Size & Copy */}
1716
2080
  <View style={bundleStyles.fileCardTop}>
1717
2081
  <View style={bundleStyles.sNoBadge}>
@@ -1721,12 +2085,19 @@ const BundleTab = React.memo(() => {
1721
2085
  <PackageLogoRenderer name={pkg.name} color={pkg.color} size={28}/>
1722
2086
 
1723
2087
  <View style={{ flex: 1, paddingHorizontal: 4 }}>
1724
- <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6, flexWrap: 'wrap' }}>
2088
+ <View style={{
2089
+ flexDirection: 'row',
2090
+ alignItems: 'center',
2091
+ gap: 6,
2092
+ flexWrap: 'wrap',
2093
+ }}>
1725
2094
  <HighlightText text={pkg.name} search={search} style={bundleStyles.fileName} highlightStyle={bundleStyles.searchHighlight}/>
1726
2095
  <View style={bundleStyles.versionPill}>
1727
2096
  <Text style={bundleStyles.versionPillText}>
1728
2097
  {cleanInstalledVersion
1729
- ? t('bundle.versionPrefix', { version: cleanInstalledVersion })
2098
+ ? t('bundle.versionPrefix', {
2099
+ version: cleanInstalledVersion,
2100
+ })
1730
2101
  : t('bundle.bundled')}
1731
2102
  </Text>
1732
2103
  </View>
@@ -1737,34 +2108,72 @@ const BundleTab = React.memo(() => {
1737
2108
  </Text>
1738
2109
  </View>) : hasUpdate ? (<View style={bundleStyles.updateBadge}>
1739
2110
  <Text style={bundleStyles.updateBadgeText}>
1740
- {t('bundle.updateAvailable', { version: pkg.latestVersion })}
2111
+ {t('bundle.updateAvailable', {
2112
+ version: pkg.latestVersion,
2113
+ })}
1741
2114
  </Text>
1742
- </View>) : cleanInstalledVersion ? (<View style={[bundleStyles.updateBadge, { backgroundColor: AppColors.gray100, borderColor: AppColors.gray200 }]}>
1743
- <Text style={[bundleStyles.updateBadgeText, { color: AppColors.gray600 }]}>
2115
+ </View>) : cleanInstalledVersion ? (<View style={[
2116
+ bundleStyles.updateBadge,
2117
+ {
2118
+ backgroundColor: AppColors.gray100,
2119
+ borderColor: AppColors.gray200,
2120
+ },
2121
+ ]}>
2122
+ <Text style={[
2123
+ bundleStyles.updateBadgeText,
2124
+ { color: AppColors.gray600 },
2125
+ ]}>
1744
2126
  {t('bundle.upToDate')}
1745
2127
  </Text>
1746
- </View>) : (<View style={[bundleStyles.updateBadge, { backgroundColor: AppColors.gray100, borderColor: AppColors.gray200 }]}>
1747
- <Text style={[bundleStyles.updateBadgeText, { color: AppColors.gray600 }]}>
2128
+ </View>) : (<View style={[
2129
+ bundleStyles.updateBadge,
2130
+ {
2131
+ backgroundColor: AppColors.gray100,
2132
+ borderColor: AppColors.gray200,
2133
+ },
2134
+ ]}>
2135
+ <Text style={[
2136
+ bundleStyles.updateBadgeText,
2137
+ { color: AppColors.gray600 },
2138
+ ]}>
1748
2139
  {t('bundle.bundledBadge')}
1749
2140
  </Text>
1750
2141
  </View>)}
1751
2142
  </View>
1752
2143
  </View>
1753
2144
 
1754
- <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
2145
+ <View style={{
2146
+ flexDirection: 'row',
2147
+ alignItems: 'center',
2148
+ gap: 6,
2149
+ }}>
1755
2150
  <View style={bundleStyles.fileSizeBox}>
1756
2151
  <Text style={bundleStyles.fileSizeKb}>
1757
2152
  {t('bundle.fileSizeKb', { size: pkg.sizeKb })}
1758
2153
  </Text>
1759
- <Text style={bundleStyles.filePercent}>{pkg.percentage}%</Text>
2154
+ <Text style={bundleStyles.filePercent}>
2155
+ {pkg.percentage}%
2156
+ </Text>
1760
2157
  </View>
1761
2158
  <CopyButton value={() => pkg} label={t('bundle.packageDetailsJson')}/>
1762
2159
  </View>
1763
2160
  </View>
1764
2161
 
1765
2162
  {/* Subtitle Row: Category Chip + Description */}
1766
- <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6, marginTop: 4, marginBottom: 2 }}>
1767
- <View style={[bundleStyles.pkgBadge, { backgroundColor: `${pkg.color}1F`, borderColor: `${pkg.color}4D` }]}>
2163
+ <View style={{
2164
+ flexDirection: 'row',
2165
+ alignItems: 'center',
2166
+ gap: 6,
2167
+ marginTop: 4,
2168
+ marginBottom: 2,
2169
+ }}>
2170
+ <View style={[
2171
+ bundleStyles.pkgBadge,
2172
+ {
2173
+ backgroundColor: `${pkg.color}1F`,
2174
+ borderColor: `${pkg.color}4D`,
2175
+ },
2176
+ ]}>
1768
2177
  <Text style={[bundleStyles.pkgBadgeText, { color: pkg.color }]}>
1769
2178
  {pkg.category.toUpperCase()}
1770
2179
  </Text>
@@ -1776,7 +2185,11 @@ const BundleTab = React.memo(() => {
1776
2185
 
1777
2186
  {/* Deprecation Warning Banner if deprecated */}
1778
2187
  {pkg.isDeprecated && pkg.deprecationReason && (<View style={bundleStyles.deprecationBox}>
1779
- <View style={{ flexDirection: 'row', alignItems: 'center', gap: 5 }}>
2188
+ <View style={{
2189
+ flexDirection: 'row',
2190
+ alignItems: 'center',
2191
+ gap: 5,
2192
+ }}>
1780
2193
  <CircleAlertIcon color={AppColors.errorColor} size={12}/>
1781
2194
  <Text style={bundleStyles.deprecationText}>
1782
2195
  {pkg.deprecationReason}
@@ -1787,19 +2200,39 @@ const BundleTab = React.memo(() => {
1787
2200
  <View style={bundleStyles.fileProgressTrack}>
1788
2201
  <View style={[
1789
2202
  bundleStyles.fileProgressBar,
1790
- { width: `${pkg.percentage * 2}%`, backgroundColor: pkg.isDeprecated ? AppColors.red500 : pkg.color },
2203
+ {
2204
+ width: `${pkg.percentage * 2}%`,
2205
+ backgroundColor: pkg.isDeprecated
2206
+ ? AppColors.red500
2207
+ : pkg.color,
2208
+ },
1791
2209
  ]}/>
1792
2210
  </View>
1793
2211
 
1794
2212
  {/* Footer Row: Type Tag + Last Active + NPM Link Button */}
1795
- <View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginTop: 3 }}>
1796
- <View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
2213
+ <View style={{
2214
+ flexDirection: 'row',
2215
+ justifyContent: 'space-between',
2216
+ alignItems: 'center',
2217
+ marginTop: 3,
2218
+ }}>
2219
+ <View style={{
2220
+ flexDirection: 'row',
2221
+ alignItems: 'center',
2222
+ gap: 8,
2223
+ }}>
1797
2224
  <View style={bundleStyles.typeTag}>
1798
2225
  <Text style={bundleStyles.typeTagText}>
1799
- {pkg.type === 'direct' ? t('bundle.direct') : t('bundle.transitive')}
2226
+ {pkg.type === 'direct'
2227
+ ? t('bundle.direct')
2228
+ : t('bundle.transitive')}
1800
2229
  </Text>
1801
2230
  </View>
1802
- {pkg.lastActive && (<View style={{ flexDirection: 'row', alignItems: 'center', gap: 4 }}>
2231
+ {pkg.lastActive && (<View style={{
2232
+ flexDirection: 'row',
2233
+ alignItems: 'center',
2234
+ gap: 4,
2235
+ }}>
1803
2236
  <ClockIcon color={AppColors.grayTextWeak} size={11}/>
1804
2237
  <Text style={bundleStyles.lastActiveText}>
1805
2238
  {pkg.lastActive}
@@ -1807,15 +2240,22 @@ const BundleTab = React.memo(() => {
1807
2240
  </View>)}
1808
2241
  </View>
1809
2242
 
1810
- <View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
2243
+ <View style={{
2244
+ flexDirection: 'row',
2245
+ alignItems: 'center',
2246
+ gap: 8,
2247
+ }}>
1811
2248
  <Text style={bundleStyles.fileMetaText}>
1812
2249
  {t('bundle.minified', { size: pkg.sizeKb })}
1813
2250
  </Text>
1814
2251
  <TouchableScale accessible={true} accessibilityRole="link" accessibilityLabel={`Open ${pkg.name} on NPM`} accessibilityHint="Opens NPM package page in browser" onPress={() => {
1815
- const url = pkg.npmUrl || `https://www.npmjs.com/package/${pkg.name}`;
2252
+ const url = pkg.npmUrl ||
2253
+ `https://www.npmjs.com/package/${pkg.name}`;
1816
2254
  Linking.openURL(url).catch(() => { });
1817
2255
  }} style={bundleStyles.npmLinkBtn}>
1818
- <Text style={bundleStyles.npmLinkText}>{t('bundle.npmLink')}</Text>
2256
+ <Text style={bundleStyles.npmLinkText}>
2257
+ {t('bundle.npmLink')}
2258
+ </Text>
1819
2259
  </TouchableScale>
1820
2260
  </View>
1821
2261
  </View>
@@ -1828,7 +2268,9 @@ const BundleTab = React.memo(() => {
1828
2268
  <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
1829
2269
  <PackageIcon color={AppColors.purple} size={14}/>
1830
2270
  <Text style={bundleStyles.footerTitle}>
1831
- {t('bundle.dependenciesCount', { count: filteredPackages.length })}
2271
+ {t('bundle.dependenciesCount', {
2272
+ count: filteredPackages.length,
2273
+ })}
1832
2274
  </Text>
1833
2275
  </View>
1834
2276
  <Text style={bundleStyles.footerSizeVal}>
@@ -1845,22 +2287,36 @@ const BundleTab = React.memo(() => {
1845
2287
  {/* ══════════════════════════════════════════════════════════════════════ */}
1846
2288
  {activeSubTab === 'media' && (<View style={{ flex: 1 }}>
1847
2289
  <ScrollView style={{ flex: 1 }} contentContainerStyle={bundleStyles.contentContainer} keyboardShouldPersistTaps="handled">
1848
-
1849
2290
  <View style={bundleStyles.tipsCard}>
1850
- <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', marginBottom: 8 }}>
2291
+ <View style={{
2292
+ flexDirection: 'row',
2293
+ alignItems: 'center',
2294
+ justifyContent: 'space-between',
2295
+ marginBottom: 8,
2296
+ }}>
1851
2297
  <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
1852
2298
  <StorageIcon color={AppColors.purple} size={18}/>
1853
- <Text style={bundleStyles.tipsHeading}>{t('bundle.mediaAuditorTitle')}</Text>
2299
+ <Text style={bundleStyles.tipsHeading}>
2300
+ {t('bundle.mediaAuditorTitle')}
2301
+ </Text>
1854
2302
  </View>
1855
2303
  <CopyButton value={() => mediaFiles} label={t('bundle.mediaAssetsJson')}/>
1856
2304
  </View>
1857
2305
  <Text style={bundleStyles.tipDesc}>
1858
2306
  {t('bundle.mediaAuditorPrefix')}{' '}
1859
- <Text style={{ fontFamily: AppFonts.interBold, color: AppColors.purple }}>
2307
+ <Text style={{
2308
+ fontFamily: AppFonts.interBold,
2309
+ color: AppColors.purple,
2310
+ }}>
1860
2311
  {summary.images.pct + summary.fonts.pct}%
1861
2312
  </Text>{' '}
1862
- {t('bundle.mediaAuditorMid', { kb: summary.images.kb + summary.fonts.kb })}{' '}
1863
- <Text style={{ fontFamily: AppFonts.interBold, color: AppColors.emerald700 }}>
2313
+ {t('bundle.mediaAuditorMid', {
2314
+ kb: summary.images.kb + summary.fonts.kb,
2315
+ })}{' '}
2316
+ <Text style={{
2317
+ fontFamily: AppFonts.interBold,
2318
+ color: AppColors.emerald700,
2319
+ }}>
1864
2320
  {t('bundle.mediaSavings', { size: 540 })}
1865
2321
  </Text>{' '}
1866
2322
  {t('bundle.mediaAuditorSuffix')}
@@ -1884,7 +2340,11 @@ const BundleTab = React.memo(() => {
1884
2340
  <Text style={bundleStyles.filePath}>{file.path}</Text>
1885
2341
  </View>
1886
2342
 
1887
- <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
2343
+ <View style={{
2344
+ flexDirection: 'row',
2345
+ alignItems: 'center',
2346
+ gap: 6,
2347
+ }}>
1888
2348
  <View style={bundleStyles.fileSizeBox}>
1889
2349
  <Text style={bundleStyles.fileSizeKb}>
1890
2350
  {t('bundle.fileSizeKb', { size: file.sizeKb })}
@@ -1942,12 +2402,18 @@ const BundleTab = React.memo(() => {
1942
2402
  {/* ── 5. TAB: OPTIMIZER CHECKLIST ─────────────────────────────────────── */}
1943
2403
  {/* ══════════════════════════════════════════════════════════════════════ */}
1944
2404
  {activeSubTab === 'optimizer' && (<ScrollView style={{ flex: 1 }} contentContainerStyle={bundleStyles.contentContainer} keyboardShouldPersistTaps="handled">
1945
-
1946
2405
  <View style={bundleStyles.tipsCard}>
1947
- <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', marginBottom: 12 }}>
2406
+ <View style={{
2407
+ flexDirection: 'row',
2408
+ alignItems: 'center',
2409
+ justifyContent: 'space-between',
2410
+ marginBottom: 12,
2411
+ }}>
1948
2412
  <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
1949
2413
  <CircleAlertIcon color={AppColors.purple} size={18}/>
1950
- <Text style={bundleStyles.tipsHeading}>{t('bundle.optimizerTitle')}</Text>
2414
+ <Text style={bundleStyles.tipsHeading}>
2415
+ {t('bundle.optimizerTitle')}
2416
+ </Text>
1951
2417
  </View>
1952
2418
  <CopyButton value={() => [
1953
2419
  t('bundle.optTip1Title'),
@@ -1974,8 +2440,12 @@ const BundleTab = React.memo(() => {
1974
2440
  desc: isHermes
1975
2441
  ? t('bundle.optTip2DescActive')
1976
2442
  : t('bundle.optTip2DescInactive'),
1977
- badge: isHermes ? t('bundle.active') : t('bundle.actionRequired'),
1978
- badgeColor: isHermes ? AppColors.emerald500 : AppColors.amber500,
2443
+ badge: isHermes
2444
+ ? t('bundle.active')
2445
+ : t('bundle.actionRequired'),
2446
+ badgeColor: isHermes
2447
+ ? AppColors.emerald500
2448
+ : AppColors.amber500,
1979
2449
  },
1980
2450
  {
1981
2451
  iconType: 'font',
@@ -2011,8 +2481,17 @@ const BundleTab = React.memo(() => {
2011
2481
  <Text style={bundleStyles.tipTitle}>{tip.title}</Text>
2012
2482
  </View>
2013
2483
  <View style={bundleStyles.tipActionWrap}>
2014
- <View style={[bundleStyles.impactBadge, { backgroundColor: `${tip.badgeColor}18`, borderColor: `${tip.badgeColor}40` }]}>
2015
- <Text style={[bundleStyles.impactBadgeText, { color: tip.badgeColor }]}>
2484
+ <View style={[
2485
+ bundleStyles.impactBadge,
2486
+ {
2487
+ backgroundColor: `${tip.badgeColor}18`,
2488
+ borderColor: `${tip.badgeColor}40`,
2489
+ },
2490
+ ]}>
2491
+ <Text style={[
2492
+ bundleStyles.impactBadgeText,
2493
+ { color: tip.badgeColor },
2494
+ ]}>
2016
2495
  {tip.badge}
2017
2496
  </Text>
2018
2497
  </View>
@@ -2235,12 +2714,14 @@ const bundleStyles = StyleSheet.create({
2235
2714
  padding: 14,
2236
2715
  borderWidth: 1,
2237
2716
  borderColor: AppColors.dividerColor,
2717
+ overflow: 'hidden',
2238
2718
  },
2239
2719
  sectionHeaderRow: {
2240
2720
  flexDirection: 'row',
2241
2721
  justifyContent: 'space-between',
2242
2722
  alignItems: 'center',
2243
2723
  marginBottom: 10,
2724
+ gap: 8,
2244
2725
  },
2245
2726
  sectionTitle: {
2246
2727
  fontFamily: AppFonts.interBold,
@@ -3081,7 +3562,8 @@ const bundleStyles = StyleSheet.create({
3081
3562
  alignItems: 'center',
3082
3563
  backgroundColor: AppColors.white,
3083
3564
  paddingHorizontal: 16,
3084
- paddingVertical: 10,
3565
+ paddingTop: 10,
3566
+ paddingBottom: Platform.OS === 'ios' ? 24 : 12,
3085
3567
  borderTopWidth: 1,
3086
3568
  borderTopColor: AppColors.dividerColor,
3087
3569
  shadowColor: AppColors.primaryBlack,