react-native-inapp-inspector 1.1.24 → 1.1.26
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.
- package/android/build.gradle +36 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/java/com/inappinspector/NetworkInspectorModule.java +114 -0
- package/android/src/main/java/com/inappinspector/NetworkInspectorPackage.java +28 -0
- package/dist/commonjs/components/AnalyticsEventCard.js +94 -88
- package/dist/commonjs/components/ConsoleLogCard.js +49 -25
- package/dist/commonjs/components/ErrorBoundary.d.ts +1 -1
- package/dist/commonjs/components/ErrorBoundary.js +9 -145
- package/dist/commonjs/components/Inspector/AnalyticsFilterModal.d.ts +7 -0
- package/dist/commonjs/components/Inspector/AnalyticsFilterModal.js +1002 -0
- package/dist/commonjs/components/Inspector/AnalyticsTab.js +637 -10
- package/dist/commonjs/components/Inspector/BundleTab.d.ts +1 -1
- package/dist/commonjs/components/Inspector/BundleTab.js +826 -406
- package/dist/commonjs/components/Inspector/CrashDetail.d.ts +3 -0
- package/dist/commonjs/components/Inspector/CrashDetail.js +796 -0
- package/dist/commonjs/components/Inspector/CrashFilterModal.d.ts +20 -0
- package/dist/commonjs/components/Inspector/CrashFilterModal.js +597 -0
- package/dist/commonjs/components/Inspector/CrashTab.d.ts +3 -0
- package/dist/commonjs/components/Inspector/CrashTab.js +727 -0
- package/dist/commonjs/components/Inspector/InspectorContext.js +6 -1
- package/dist/commonjs/components/Inspector/InspectorHeader.js +65 -28
- package/dist/commonjs/components/Inspector/LogDetail.js +227 -114
- package/dist/commonjs/components/Inspector/MainScreen.js +13 -4
- package/dist/commonjs/components/Inspector/NetworkDetail.js +131 -81
- package/dist/commonjs/components/Inspector/PerformanceTab.js +20 -10
- package/dist/commonjs/components/Inspector/SettingsPanel.js +90 -1
- package/dist/commonjs/components/Inspector/TabBar.js +15 -3
- package/dist/commonjs/components/NetworkIcons.d.ts +25 -0
- package/dist/commonjs/components/NetworkIcons.js +188 -1
- package/dist/commonjs/components/SegmentedTabs.js +33 -23
- package/dist/commonjs/components/TreeNode.js +13 -12
- package/dist/commonjs/constants/version.d.ts +1 -1
- package/dist/commonjs/constants/version.js +1 -1
- package/dist/commonjs/customHooks/analyticsLogger.js +70 -44
- package/dist/commonjs/customHooks/bundleAnalyzer.d.ts +5 -1
- package/dist/commonjs/customHooks/bundleAnalyzer.js +497 -126
- package/dist/commonjs/customHooks/consoleLogger.d.ts +1 -0
- package/dist/commonjs/customHooks/consoleLogger.js +153 -51
- package/dist/commonjs/customHooks/crashHandler.d.ts +64 -0
- package/dist/commonjs/customHooks/crashHandler.js +681 -0
- package/dist/commonjs/customHooks/networkLogger.js +74 -49
- package/dist/commonjs/customHooks/performanceTracker.d.ts +8 -0
- package/dist/commonjs/customHooks/performanceTracker.js +44 -3
- package/dist/commonjs/decorators/index.d.ts +49 -0
- package/dist/commonjs/decorators/index.js +142 -0
- package/dist/commonjs/helpers/gaAnalyticsRegistry.d.ts +18 -0
- package/dist/commonjs/helpers/gaAnalyticsRegistry.js +174 -0
- package/dist/commonjs/helpers/index.d.ts +7 -4
- package/dist/commonjs/helpers/index.js +102 -64
- package/dist/commonjs/i18n/locales/en.json +182 -4
- package/dist/commonjs/index.d.ts +6 -0
- package/dist/commonjs/index.js +310 -8
- package/dist/commonjs/styles/index.d.ts +4 -1
- package/dist/commonjs/styles/index.js +22 -19
- package/dist/commonjs/types/enums.d.ts +156 -9
- package/dist/commonjs/types/enums.js +139 -2
- package/dist/commonjs/types/interfaces.d.ts +70 -1
- package/dist/esm/components/AnalyticsEventCard.js +95 -89
- package/dist/esm/components/ConsoleLogCard.js +49 -25
- package/dist/esm/components/ErrorBoundary.d.ts +1 -1
- package/dist/esm/components/ErrorBoundary.js +10 -113
- package/dist/esm/components/Inspector/AnalyticsFilterModal.d.ts +7 -0
- package/dist/esm/components/Inspector/AnalyticsFilterModal.js +964 -0
- package/dist/esm/components/Inspector/AnalyticsTab.js +639 -12
- package/dist/esm/components/Inspector/BundleTab.d.ts +1 -1
- package/dist/esm/components/Inspector/BundleTab.js +827 -407
- package/dist/esm/components/Inspector/CrashDetail.d.ts +3 -0
- package/dist/esm/components/Inspector/CrashDetail.js +758 -0
- package/dist/esm/components/Inspector/CrashFilterModal.d.ts +20 -0
- package/dist/esm/components/Inspector/CrashFilterModal.js +557 -0
- package/dist/esm/components/Inspector/CrashTab.d.ts +3 -0
- package/dist/esm/components/Inspector/CrashTab.js +689 -0
- package/dist/esm/components/Inspector/InspectorContext.js +1 -0
- package/dist/esm/components/Inspector/InspectorHeader.js +66 -29
- package/dist/esm/components/Inspector/LogDetail.js +227 -114
- package/dist/esm/components/Inspector/MainScreen.js +14 -5
- package/dist/esm/components/Inspector/NetworkDetail.js +132 -49
- package/dist/esm/components/Inspector/PerformanceTab.js +20 -10
- package/dist/esm/components/Inspector/SettingsPanel.js +91 -2
- package/dist/esm/components/Inspector/TabBar.js +16 -4
- package/dist/esm/components/NetworkIcons.d.ts +25 -0
- package/dist/esm/components/NetworkIcons.js +161 -0
- package/dist/esm/components/SegmentedTabs.js +33 -23
- package/dist/esm/components/TreeNode.js +13 -12
- package/dist/esm/constants/version.d.ts +1 -1
- package/dist/esm/constants/version.js +1 -1
- package/dist/esm/customHooks/analyticsLogger.js +70 -44
- package/dist/esm/customHooks/bundleAnalyzer.d.ts +5 -1
- package/dist/esm/customHooks/bundleAnalyzer.js +493 -125
- package/dist/esm/customHooks/consoleLogger.d.ts +1 -0
- package/dist/esm/customHooks/consoleLogger.js +151 -50
- package/dist/esm/customHooks/crashHandler.d.ts +64 -0
- package/dist/esm/customHooks/crashHandler.js +659 -0
- package/dist/esm/customHooks/networkLogger.js +74 -49
- package/dist/esm/customHooks/performanceTracker.d.ts +8 -0
- package/dist/esm/customHooks/performanceTracker.js +38 -1
- package/dist/esm/decorators/index.d.ts +49 -0
- package/dist/esm/decorators/index.js +137 -0
- package/dist/esm/helpers/gaAnalyticsRegistry.d.ts +18 -0
- package/dist/esm/helpers/gaAnalyticsRegistry.js +169 -0
- package/dist/esm/helpers/index.d.ts +7 -4
- package/dist/esm/helpers/index.js +100 -63
- package/dist/esm/i18n/locales/en.json +182 -4
- package/dist/esm/index.d.ts +6 -0
- package/dist/esm/index.js +262 -8
- package/dist/esm/styles/index.d.ts +4 -1
- package/dist/esm/styles/index.js +22 -19
- package/dist/esm/types/enums.d.ts +156 -9
- package/dist/esm/types/enums.js +138 -3
- package/dist/esm/types/interfaces.d.ts +70 -1
- package/ios/NetworkInspectorModule.h +6 -0
- package/ios/NetworkInspectorModule.m +125 -0
- package/package.json +5 -2
- package/react-native-inapp-inspector.podspec +18 -0
|
@@ -6,7 +6,7 @@ import CopyButton from '../CopyButton';
|
|
|
6
6
|
import HighlightText from '../HighlightText';
|
|
7
7
|
import { AppColors } from '../../styles/AppColors';
|
|
8
8
|
import { AppFonts } from '../../styles/AppFonts';
|
|
9
|
-
import { PackageIcon, SearchIcon, ClearIcon, CircleAlertIcon, LayersIcon, LiveStateIcon, StorageIcon, MetadataIcon, FolderIcon, FolderOpenIcon, TrashIcon, LightbulbIcon, SparkleIcon, ClockIcon, } from '../NetworkIcons';
|
|
9
|
+
import { PackageIcon, SearchIcon, ClearIcon, CircleAlertIcon, LayersIcon, TimelineIcon, LiveStateIcon, StorageIcon, MetadataIcon, FolderIcon, FolderOpenIcon, TrashIcon, LightbulbIcon, SparkleIcon, ClockIcon, RefreshCcwIcon, ExternalLinkIcon, } from '../NetworkIcons';
|
|
10
10
|
import Svg, { Path, Rect, Circle, Ellipse, G, Defs, LinearGradient, Stop, } from 'react-native-svg';
|
|
11
11
|
import { analyzeHostAppBundle, getCachedBundleAnalysis, } from '../../customHooks/bundleAnalyzer';
|
|
12
12
|
// ─── High-Fidelity Vector Package & NPM Logos ────────────────────────────────
|
|
@@ -247,7 +247,10 @@ const MediaPreviewThumbnail = ({ file }) => {
|
|
|
247
247
|
function buildBundleFileTree(files) {
|
|
248
248
|
const rootMap = new Map();
|
|
249
249
|
for (const file of files) {
|
|
250
|
-
const
|
|
250
|
+
const cleanPath = file.path.replace(/^\/+/, '');
|
|
251
|
+
const parts = cleanPath.split('/').filter(Boolean);
|
|
252
|
+
if (parts.length === 0)
|
|
253
|
+
continue;
|
|
251
254
|
let currentLevel = rootMap;
|
|
252
255
|
for (let i = 0; i < parts.length; i++) {
|
|
253
256
|
const part = parts[i];
|
|
@@ -301,21 +304,21 @@ function buildBundleFileTree(files) {
|
|
|
301
304
|
}
|
|
302
305
|
return convertMapToNodes(rootMap);
|
|
303
306
|
}
|
|
304
|
-
const BundleTreeNodeView = ({ node, level, search, totalBundleKb,
|
|
305
|
-
const
|
|
307
|
+
const BundleTreeNodeView = ({ node, level, search, totalBundleKb, expandedFolders, toggleFolder, isLastChild, }) => {
|
|
308
|
+
const isExpanded = search ? true : !!expandedFolders[node.fullPath];
|
|
309
|
+
const isCollapsed = !isExpanded;
|
|
310
|
+
const indentLeft = level * 16 + 10;
|
|
306
311
|
if (node.isFolder) {
|
|
307
|
-
return (<View style={bundleStyles.
|
|
312
|
+
return (<View style={bundleStyles.treeFolderBlock}>
|
|
308
313
|
<TouchableScale onPress={() => toggleFolder(node.fullPath)} style={[
|
|
309
|
-
bundleStyles.
|
|
310
|
-
{ paddingLeft:
|
|
314
|
+
bundleStyles.treeFolderRow,
|
|
315
|
+
{ paddingLeft: indentLeft },
|
|
311
316
|
]}>
|
|
312
|
-
<View style={
|
|
313
|
-
<
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
</View>
|
|
318
|
-
{isCollapsed ? (<FolderIcon color={AppColors.amber500} size={16}/>) : (<FolderOpenIcon color={AppColors.amber500} size={16}/>)}
|
|
317
|
+
<View style={bundleStyles.treeRowLeft}>
|
|
318
|
+
<Text style={bundleStyles.treeChevron}>
|
|
319
|
+
{isCollapsed ? '▸' : '▾'}
|
|
320
|
+
</Text>
|
|
321
|
+
{isCollapsed ? (<FolderIcon color={AppColors.amber500} size={15}/>) : (<FolderOpenIcon color={AppColors.amber500} size={15}/>)}
|
|
319
322
|
<HighlightText text={node.name + '/'} search={search} style={bundleStyles.treeFolderName} highlightStyle={bundleStyles.searchHighlight}/>
|
|
320
323
|
<View style={bundleStyles.treeCountBadge}>
|
|
321
324
|
<Text style={bundleStyles.treeCountBadgeText}>
|
|
@@ -324,7 +327,7 @@ const BundleTreeNodeView = ({ node, level, search, totalBundleKb, collapsedFolde
|
|
|
324
327
|
</View>
|
|
325
328
|
</View>
|
|
326
329
|
|
|
327
|
-
<View style={
|
|
330
|
+
<View style={bundleStyles.treeRowRight}>
|
|
328
331
|
<Text style={bundleStyles.treeFolderSize}>
|
|
329
332
|
{node.sizeKb >= 1024
|
|
330
333
|
? t('bundle.fileSizeMb', { size: (node.sizeKb / 1024).toFixed(2) })
|
|
@@ -338,8 +341,8 @@ const BundleTreeNodeView = ({ node, level, search, totalBundleKb, collapsedFolde
|
|
|
338
341
|
</View>
|
|
339
342
|
</TouchableScale>
|
|
340
343
|
|
|
341
|
-
{!isCollapsed && (<View style={
|
|
342
|
-
{node.children.map((child, cIdx) => (<BundleTreeNodeView key={child.id} node={child} level={level + 1} search={search} totalBundleKb={totalBundleKb}
|
|
344
|
+
{!isCollapsed && (<View style={bundleStyles.treeChildrenContainer}>
|
|
345
|
+
{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}/>))}
|
|
343
346
|
</View>)}
|
|
344
347
|
</View>);
|
|
345
348
|
}
|
|
@@ -347,64 +350,41 @@ const BundleTreeNodeView = ({ node, level, search, totalBundleKb, collapsedFolde
|
|
|
347
350
|
const file = node.fileItem;
|
|
348
351
|
if (!file)
|
|
349
352
|
return null;
|
|
350
|
-
const pctOfTotal = ((file.sizeKb / totalBundleKb) * 100).toFixed(1);
|
|
353
|
+
const pctOfTotal = totalBundleKb > 0 ? ((file.sizeKb / totalBundleKb) * 100).toFixed(1) : '0';
|
|
351
354
|
const isUnused = file.isConsumed === false;
|
|
352
|
-
return (<View style={[
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
355
|
+
return (<View style={[
|
|
356
|
+
bundleStyles.treeFileRow,
|
|
357
|
+
{ paddingLeft: indentLeft },
|
|
358
|
+
isUnused && bundleStyles.treeFileRowUnused,
|
|
359
|
+
]}>
|
|
360
|
+
<View style={bundleStyles.treeRowLeft}>
|
|
361
|
+
<View style={bundleStyles.treeBranchGuide}>
|
|
362
|
+
<Text style={bundleStyles.treeBranchGuideText}>
|
|
363
|
+
{isLastChild ? '└' : '├'}
|
|
357
364
|
</Text>
|
|
358
365
|
</View>
|
|
359
366
|
|
|
360
367
|
<MediaPreviewThumbnail file={file}/>
|
|
361
368
|
|
|
362
|
-
<
|
|
363
|
-
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 6, flexWrap: 'wrap' }}>
|
|
364
|
-
<HighlightText text={file.name} search={search} style={bundleStyles.fileName} highlightStyle={bundleStyles.searchHighlight}/>
|
|
365
|
-
{isUnused ? (<View style={bundleStyles.unusedBadge}>
|
|
366
|
-
<Text style={bundleStyles.unusedBadgeText}>{t('bundle.notConsumed')}</Text>
|
|
367
|
-
</View>) : (<View style={bundleStyles.consumedBadge}>
|
|
368
|
-
<Text style={bundleStyles.consumedBadgeText}>{t('bundle.consumed')}</Text>
|
|
369
|
-
</View>)}
|
|
370
|
-
</View>
|
|
371
|
-
<Text style={bundleStyles.filePath} numberOfLines={1}>
|
|
372
|
-
{file.meta}
|
|
373
|
-
</Text>
|
|
374
|
-
</View>
|
|
369
|
+
<HighlightText text={file.name} search={search} style={bundleStyles.treeFileName} highlightStyle={bundleStyles.searchHighlight}/>
|
|
375
370
|
|
|
376
|
-
<View style={
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
371
|
+
{isUnused && (<View style={bundleStyles.treeUnusedPill}>
|
|
372
|
+
<Text style={bundleStyles.treeUnusedPillText}>{t('bundle.notConsumed')}</Text>
|
|
373
|
+
</View>)}
|
|
374
|
+
</View>
|
|
375
|
+
|
|
376
|
+
<View style={bundleStyles.treeRowRight}>
|
|
377
|
+
<View style={bundleStyles.treeFileSizeGroup}>
|
|
378
|
+
<Text style={[bundleStyles.treeFileSizeText, isUnused && { color: AppColors.red600 }]}>
|
|
379
|
+
{file.sizeKb >= 1024
|
|
380
380
|
? t('bundle.fileSizeMb', { size: (file.sizeKb / 1024).toFixed(2) })
|
|
381
381
|
: t('bundle.fileSizeKb', { size: file.sizeKb })}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
</View>
|
|
385
|
-
<CopyButton value={() => file} label={t('bundle.fileDetailsJson')}/>
|
|
382
|
+
</Text>
|
|
383
|
+
<Text style={bundleStyles.treeFilePctText}>{pctOfTotal}%</Text>
|
|
386
384
|
</View>
|
|
387
|
-
</View>
|
|
388
385
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
file.status === 'warning' && bundleStyles.adviceWarning,
|
|
392
|
-
file.status === 'optimal' && bundleStyles.adviceOptimal,
|
|
393
|
-
isUnused && bundleStyles.adviceUnused,
|
|
394
|
-
{ marginLeft: 26, marginTop: 4 },
|
|
395
|
-
]}>
|
|
396
|
-
<View style={{ flexDirection: 'row', alignItems: 'flex-start', gap: 5 }}>
|
|
397
|
-
{isUnused ? (<TrashIcon color={AppColors.red600} size={12}/>) : file.status === 'warning' ? (<LightbulbIcon color={AppColors.amber800Warm} size={12}/>) : (<SparkleIcon color={AppColors.emerald700} size={12}/>)}
|
|
398
|
-
<Text style={[
|
|
399
|
-
bundleStyles.adviceText,
|
|
400
|
-
file.status === 'warning' && { color: AppColors.amber800Warm },
|
|
401
|
-
file.status === 'optimal' && { color: AppColors.emerald700 },
|
|
402
|
-
isUnused && { color: AppColors.red600 },
|
|
403
|
-
]}>
|
|
404
|
-
{file.advice}
|
|
405
|
-
</Text>
|
|
406
|
-
</View>
|
|
407
|
-
</View>)}
|
|
386
|
+
<CopyButton value={() => file} label={t('bundle.fileDetailsJson')}/>
|
|
387
|
+
</View>
|
|
408
388
|
</View>);
|
|
409
389
|
};
|
|
410
390
|
const CATEGORY_TABS = [
|
|
@@ -427,11 +407,19 @@ const CATEGORY_COLORS = {
|
|
|
427
407
|
const BundleTab = React.memo(() => {
|
|
428
408
|
const { t } = useTranslation();
|
|
429
409
|
const [activeSubTab, setActiveSubTab] = useState('overview');
|
|
410
|
+
const [prodPlatform, setProdPlatform] = useState('ios');
|
|
430
411
|
const [search, setSearch] = useState('');
|
|
431
412
|
const [activeCategory, setActiveCategory] = useState('ALL');
|
|
432
413
|
// ─── Live Host App Bundle Analysis (fetched from Metro / runtime) ─────────
|
|
433
414
|
const [analysis, setAnalysis] = useState(() => getCachedBundleAnalysis());
|
|
434
415
|
const [isAnalyzing, setIsAnalyzing] = useState(!getCachedBundleAnalysis());
|
|
416
|
+
const refreshAnalysis = useCallback(() => {
|
|
417
|
+
setIsAnalyzing(true);
|
|
418
|
+
analyzeHostAppBundle(true).then(result => {
|
|
419
|
+
setAnalysis(result);
|
|
420
|
+
setIsAnalyzing(false);
|
|
421
|
+
});
|
|
422
|
+
}, []);
|
|
435
423
|
useEffect(() => {
|
|
436
424
|
let mounted = true;
|
|
437
425
|
analyzeHostAppBundle().then(result => {
|
|
@@ -449,72 +437,108 @@ const BundleTab = React.memo(() => {
|
|
|
449
437
|
// Dynamic files & packages derived from the analyzed host bundle
|
|
450
438
|
const bundleFiles = useMemo(() => (analysis ? analysis.files : []), [analysis]);
|
|
451
439
|
const bundlePackages = useMemo(() => (analysis ? analysis.packages : []), [analysis]);
|
|
452
|
-
//
|
|
440
|
+
// Comprehensive bundle summary (full Metro bundle calculation e.g. 6.0 MB)
|
|
453
441
|
const summary = useMemo(() => {
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
442
|
+
const totalKb = analysis?.totalDevKb || (analysis?.totalDevBytes ? Math.round(analysis.totalDevBytes / 1024) : 6144);
|
|
443
|
+
const totalMb = analysis?.totalDevMb ? analysis.totalDevMb.toFixed(2) : (totalKb / 1024).toFixed(2);
|
|
444
|
+
const nodeModulesKb = analysis?.splitUp?.nodeModules?.kb || Math.round(totalKb * 0.52);
|
|
445
|
+
const nodeModulesPct = analysis?.splitUp?.nodeModules?.pct || 52.0;
|
|
446
|
+
const metroKb = analysis?.splitUp?.metroDevOverhead?.kb || Math.round(totalKb * 0.21);
|
|
447
|
+
const metroPct = analysis?.splitUp?.metroDevOverhead?.pct || 21.0;
|
|
448
|
+
const appSourceKb = analysis?.splitUp?.appSource?.kb || Math.round(totalKb * 0.15);
|
|
449
|
+
const appSourcePct = analysis?.splitUp?.appSource?.pct || 15.0;
|
|
450
|
+
const assetsMediaKb = analysis?.splitUp?.assetsMedia?.kb || Math.round(totalKb * 0.12);
|
|
451
|
+
const assetsMediaPct = analysis?.splitUp?.assetsMedia?.pct || 12.0;
|
|
452
|
+
const categories = [
|
|
453
|
+
{
|
|
454
|
+
key: 'node_modules',
|
|
455
|
+
iconType: 'javascript',
|
|
456
|
+
title: 'NPM Dependencies',
|
|
457
|
+
targetTab: 'packages',
|
|
458
|
+
count: analysis?.packageCount || bundlePackages.length || 53,
|
|
459
|
+
sizeKb: nodeModulesKb,
|
|
460
|
+
sizeMb: (nodeModulesKb / 1024).toFixed(2),
|
|
461
|
+
pct: nodeModulesPct,
|
|
462
|
+
color: AppColors.indigo500,
|
|
463
|
+
desc: `${analysis?.packageCount || bundlePackages.length || 53} node_modules packages (React Native, React, navigation, axios)`,
|
|
464
|
+
},
|
|
465
|
+
{
|
|
466
|
+
key: 'metro_runtime',
|
|
467
|
+
iconType: 'bolt',
|
|
468
|
+
title: 'Metro Dev Runtime',
|
|
469
|
+
targetTab: 'optimizer',
|
|
470
|
+
count: 1,
|
|
471
|
+
sizeKb: metroKb,
|
|
472
|
+
sizeMb: (metroKb / 1024).toFixed(2),
|
|
473
|
+
pct: metroPct,
|
|
474
|
+
color: AppColors.amber500,
|
|
475
|
+
desc: 'Module loader, polyfills, HMR client, and prelude runtime',
|
|
476
|
+
},
|
|
477
|
+
{
|
|
478
|
+
key: 'app_source',
|
|
479
|
+
iconType: 'typescript',
|
|
480
|
+
title: 'App Source Code (TSX/JS)',
|
|
481
|
+
targetTab: 'files',
|
|
482
|
+
count: bundleFiles.filter(f => f.category === 'typescript' || f.category === 'javascript').length || 7,
|
|
483
|
+
sizeKb: appSourceKb,
|
|
484
|
+
sizeMb: (appSourceKb / 1024).toFixed(2),
|
|
485
|
+
pct: appSourcePct,
|
|
486
|
+
color: AppColors.sky500,
|
|
487
|
+
desc: 'Screens, components, navigation, Redux store, and hooks',
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
key: 'assets_media',
|
|
491
|
+
iconType: 'image',
|
|
492
|
+
title: 'Images & Static Media',
|
|
493
|
+
targetTab: 'media',
|
|
494
|
+
count: bundleFiles.filter(f => f.category === 'image' || f.category === 'font' || f.category === 'json').length || 3,
|
|
495
|
+
sizeKb: assetsMediaKb,
|
|
496
|
+
sizeMb: (assetsMediaKb / 1024).toFixed(2),
|
|
497
|
+
pct: assetsMediaPct,
|
|
498
|
+
color: AppColors.pink500,
|
|
499
|
+
desc: 'Bundled PNG/JPG/SVG drawables, custom fonts, and JSON assets',
|
|
500
|
+
},
|
|
501
|
+
];
|
|
487
502
|
return {
|
|
488
503
|
totalKb,
|
|
489
|
-
totalMb
|
|
490
|
-
totalCount: bundleFiles.length,
|
|
504
|
+
totalMb,
|
|
505
|
+
totalCount: (analysis?.packageCount || 53) + bundleFiles.length + 1,
|
|
506
|
+
categories,
|
|
507
|
+
nodeModulesKb,
|
|
508
|
+
nodeModulesPct,
|
|
509
|
+
metroKb,
|
|
510
|
+
metroPct,
|
|
511
|
+
appSourceKb,
|
|
512
|
+
appSourcePct,
|
|
513
|
+
assetsMediaKb,
|
|
514
|
+
assetsMediaPct,
|
|
491
515
|
images: {
|
|
492
|
-
count:
|
|
493
|
-
kb:
|
|
494
|
-
pct:
|
|
516
|
+
count: bundleFiles.filter(f => f.category === 'image').length,
|
|
517
|
+
kb: assetsMediaKb,
|
|
518
|
+
pct: assetsMediaPct,
|
|
495
519
|
},
|
|
496
520
|
ts: {
|
|
497
|
-
count:
|
|
498
|
-
kb:
|
|
499
|
-
pct:
|
|
521
|
+
count: bundleFiles.filter(f => f.category === 'typescript').length,
|
|
522
|
+
kb: appSourceKb,
|
|
523
|
+
pct: appSourcePct,
|
|
500
524
|
},
|
|
501
525
|
js: {
|
|
502
|
-
count:
|
|
503
|
-
kb:
|
|
504
|
-
pct:
|
|
526
|
+
count: (analysis?.packageCount || 53) + 1,
|
|
527
|
+
kb: nodeModulesKb + metroKb,
|
|
528
|
+
pct: Number((nodeModulesPct + metroPct).toFixed(1)),
|
|
505
529
|
},
|
|
506
530
|
fonts: {
|
|
507
|
-
count:
|
|
508
|
-
kb:
|
|
509
|
-
pct:
|
|
531
|
+
count: bundleFiles.filter(f => f.category === 'font').length,
|
|
532
|
+
kb: 0,
|
|
533
|
+
pct: 0,
|
|
510
534
|
},
|
|
511
535
|
json: {
|
|
512
|
-
count:
|
|
513
|
-
kb:
|
|
514
|
-
pct:
|
|
536
|
+
count: bundleFiles.filter(f => f.category === 'json').length,
|
|
537
|
+
kb: Math.round(assetsMediaKb * 0.1),
|
|
538
|
+
pct: Number((assetsMediaPct * 0.1).toFixed(1)),
|
|
515
539
|
},
|
|
516
540
|
};
|
|
517
|
-
}, [bundleFiles]);
|
|
541
|
+
}, [analysis, bundleFiles, bundlePackages]);
|
|
518
542
|
// Filtered files
|
|
519
543
|
const filteredFiles = useMemo(() => {
|
|
520
544
|
return bundleFiles.filter(file => {
|
|
@@ -536,22 +560,19 @@ const BundleTab = React.memo(() => {
|
|
|
536
560
|
const filteredTotalKb = useMemo(() => {
|
|
537
561
|
return filteredFiles.reduce((acc, f) => acc + f.sizeKb, 0);
|
|
538
562
|
}, [filteredFiles]);
|
|
539
|
-
// Tree View State & Node Generation
|
|
563
|
+
// Tree View State & Node Generation (Collapsed by default)
|
|
540
564
|
const [filesViewMode, setFilesViewMode] = useState('tree');
|
|
541
|
-
const [
|
|
565
|
+
const [expandedFolders, setExpandedFolders] = useState({});
|
|
542
566
|
const fileTreeNodes = useMemo(() => {
|
|
543
567
|
return buildBundleFileTree(filteredFiles);
|
|
544
568
|
}, [filteredFiles]);
|
|
545
569
|
const toggleFolder = useCallback((path) => {
|
|
546
|
-
|
|
570
|
+
setExpandedFolders(prev => ({
|
|
547
571
|
...prev,
|
|
548
572
|
[path]: !prev[path],
|
|
549
573
|
}));
|
|
550
574
|
}, []);
|
|
551
575
|
const expandAllFolders = useCallback(() => {
|
|
552
|
-
setCollapsedFolders({});
|
|
553
|
-
}, []);
|
|
554
|
-
const collapseAllFolders = useCallback(() => {
|
|
555
576
|
const all = {};
|
|
556
577
|
function collect(nodes) {
|
|
557
578
|
for (const n of nodes) {
|
|
@@ -562,8 +583,11 @@ const BundleTab = React.memo(() => {
|
|
|
562
583
|
}
|
|
563
584
|
}
|
|
564
585
|
collect(fileTreeNodes);
|
|
565
|
-
|
|
586
|
+
setExpandedFolders(all);
|
|
566
587
|
}, [fileTreeNodes]);
|
|
588
|
+
const collapseAllFolders = useCallback(() => {
|
|
589
|
+
setExpandedFolders({});
|
|
590
|
+
}, []);
|
|
567
591
|
// Filtered Packages
|
|
568
592
|
const filteredPackages = useMemo(() => {
|
|
569
593
|
if (!search)
|
|
@@ -589,6 +613,11 @@ const BundleTab = React.memo(() => {
|
|
|
589
613
|
label: t('bundle.tabOverview'),
|
|
590
614
|
icon: (isActive) => (<LiveStateIcon color={isActive ? AppColors.white : AppColors.purple} size={12}/>),
|
|
591
615
|
},
|
|
616
|
+
{
|
|
617
|
+
key: 'production',
|
|
618
|
+
label: t('bundle.tabProduction', { count: 3 }),
|
|
619
|
+
icon: (isActive) => (<TimelineIcon color={isActive ? AppColors.white : AppColors.purple} size={12}/>),
|
|
620
|
+
},
|
|
592
621
|
{
|
|
593
622
|
key: 'files',
|
|
594
623
|
label: t('bundle.tabFiles', { count: bundleFiles.length }),
|
|
@@ -611,9 +640,9 @@ const BundleTab = React.memo(() => {
|
|
|
611
640
|
},
|
|
612
641
|
];
|
|
613
642
|
return (<View style={{ flex: 1, backgroundColor: AppColors.grayBackground }}>
|
|
614
|
-
{/* ─── Top Sub-Tabs Navigation Bar (Scrollable Pill Bar) ─── */}
|
|
643
|
+
{/* ─── Top Sub-Tabs Navigation Bar (Scrollable Pill Bar + Dynamic Refresh) ─── */}
|
|
615
644
|
<View style={bundleStyles.topTabWrap}>
|
|
616
|
-
<ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={bundleStyles.subTabsScroll}>
|
|
645
|
+
<ScrollView horizontal showsHorizontalScrollIndicator={false} style={{ flex: 1 }} contentContainerStyle={bundleStyles.subTabsScroll}>
|
|
617
646
|
{subTabs.map(tab => {
|
|
618
647
|
const isActive = activeSubTab === tab.key;
|
|
619
648
|
return (<TouchableScale key={tab.key} onPress={() => setActiveSubTab(tab.key)} style={[
|
|
@@ -630,6 +659,9 @@ const BundleTab = React.memo(() => {
|
|
|
630
659
|
</TouchableScale>);
|
|
631
660
|
})}
|
|
632
661
|
</ScrollView>
|
|
662
|
+
<TouchableScale onPress={refreshAnalysis} style={bundleStyles.reloadButton} hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}>
|
|
663
|
+
{isAnalyzing ? (<ActivityIndicator size="small" color={AppColors.brandPurple}/>) : (<RefreshCcwIcon color={AppColors.brandPurple} size={14}/>)}
|
|
664
|
+
</TouchableScale>
|
|
633
665
|
</View>
|
|
634
666
|
|
|
635
667
|
{/* ─── Live Bundle Analysis Loading State ─── */}
|
|
@@ -656,11 +688,57 @@ const BundleTab = React.memo(() => {
|
|
|
656
688
|
</View>
|
|
657
689
|
<View style={{ flex: 1 }}>
|
|
658
690
|
<Text style={bundleStyles.heroTitle}>{t('bundle.heroTitle')}</Text>
|
|
659
|
-
<
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
691
|
+
{analysis?.isLive && analysis.scriptURL ? (<View style={{ marginTop: 4, gap: 4 }}>
|
|
692
|
+
<Text style={bundleStyles.heroSubtitle}>
|
|
693
|
+
{t('bundle.heroSubtitle')}
|
|
694
|
+
</Text>
|
|
695
|
+
<Pressable style={{
|
|
696
|
+
backgroundColor: `${AppColors.skyBlue}10`,
|
|
697
|
+
borderColor: `${AppColors.skyBlue}30`,
|
|
698
|
+
borderWidth: 1,
|
|
699
|
+
borderRadius: 8,
|
|
700
|
+
padding: 8,
|
|
701
|
+
marginTop: 2,
|
|
702
|
+
}} onPress={() => Linking.openURL(analysis.scriptURL)}>
|
|
703
|
+
<View style={{
|
|
704
|
+
flexDirection: 'row',
|
|
705
|
+
alignItems: 'center',
|
|
706
|
+
justifyContent: 'space-between',
|
|
707
|
+
marginBottom: 3,
|
|
708
|
+
}}>
|
|
709
|
+
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 4 }}>
|
|
710
|
+
<ExternalLinkIcon color={AppColors.skyBlue} size={11}/>
|
|
711
|
+
<Text style={{
|
|
712
|
+
fontFamily: AppFonts.interBold,
|
|
713
|
+
fontSize: 9.5,
|
|
714
|
+
color: AppColors.skyBlue,
|
|
715
|
+
letterSpacing: 0.3,
|
|
716
|
+
textTransform: 'uppercase',
|
|
717
|
+
}}>
|
|
718
|
+
Live Metro URL ↗
|
|
719
|
+
</Text>
|
|
720
|
+
</View>
|
|
721
|
+
<Text style={{
|
|
722
|
+
fontFamily: AppFonts.interRegular,
|
|
723
|
+
fontSize: 9.5,
|
|
724
|
+
color: AppColors.grayTextWeak,
|
|
725
|
+
}}>
|
|
726
|
+
{analysis.scriptURL.length} chars
|
|
727
|
+
</Text>
|
|
728
|
+
</View>
|
|
729
|
+
<Text selectable={true} style={{
|
|
730
|
+
fontFamily: AppFonts.interMedium,
|
|
731
|
+
fontSize: 11,
|
|
732
|
+
color: AppColors.skyBlue,
|
|
733
|
+
textDecorationLine: 'underline',
|
|
734
|
+
lineHeight: 16,
|
|
735
|
+
}}>
|
|
736
|
+
{analysis.scriptURL}
|
|
737
|
+
</Text>
|
|
738
|
+
</Pressable>
|
|
739
|
+
</View>) : (<Text style={bundleStyles.heroSubtitle}>
|
|
740
|
+
{t('bundle.heroSubtitle')}
|
|
741
|
+
</Text>)}
|
|
664
742
|
</View>
|
|
665
743
|
<CopyButton value={() => ({ ...summary, bundle: analysis })} label={t('bundle.bundleOverviewJson')}/>
|
|
666
744
|
</View>
|
|
@@ -739,93 +817,7 @@ const BundleTab = React.memo(() => {
|
|
|
739
817
|
</View>
|
|
740
818
|
</View>)}
|
|
741
819
|
|
|
742
|
-
{/* Development Bundle Split-Up (derived from the real running bundle) */}
|
|
743
|
-
{analysis && (<View style={bundleStyles.sectionCard}>
|
|
744
|
-
<View style={bundleStyles.sectionHeaderRow}>
|
|
745
|
-
<View>
|
|
746
|
-
<Text style={bundleStyles.sectionTitle}>{t('bundle.splitUpTitle')}</Text>
|
|
747
|
-
<Text style={bundleStyles.sectionSub}>
|
|
748
|
-
{t('bundle.splitUpSub', {
|
|
749
|
-
mb: analysis.totalDevMb,
|
|
750
|
-
files: analysis.filesCount,
|
|
751
|
-
packages: analysis.packageCount,
|
|
752
|
-
})}
|
|
753
|
-
</Text>
|
|
754
|
-
</View>
|
|
755
|
-
<CopyButton value={() => analysis.splitUp} label={t('bundle.splitUpJson')}/>
|
|
756
|
-
</View>
|
|
757
820
|
|
|
758
|
-
{[
|
|
759
|
-
{
|
|
760
|
-
iconType: 'source',
|
|
761
|
-
label: t('bundle.splitAppSource'),
|
|
762
|
-
color: AppColors.sky500,
|
|
763
|
-
...analysis.splitUp.appSource,
|
|
764
|
-
},
|
|
765
|
-
{
|
|
766
|
-
iconType: 'deps',
|
|
767
|
-
label: t('bundle.splitNodeModules'),
|
|
768
|
-
color: AppColors.indigo500,
|
|
769
|
-
...analysis.splitUp.nodeModules,
|
|
770
|
-
},
|
|
771
|
-
{
|
|
772
|
-
iconType: 'media',
|
|
773
|
-
label: t('bundle.splitAssetsMedia'),
|
|
774
|
-
color: AppColors.pink500,
|
|
775
|
-
...analysis.splitUp.assetsMedia,
|
|
776
|
-
},
|
|
777
|
-
{
|
|
778
|
-
iconType: 'overhead',
|
|
779
|
-
label: t('bundle.splitMetroOverhead'),
|
|
780
|
-
color: AppColors.amber500,
|
|
781
|
-
...analysis.splitUp.metroDevOverhead,
|
|
782
|
-
},
|
|
783
|
-
].map((part, idx) => (<View key={idx} style={bundleStyles.catRowCard}>
|
|
784
|
-
<View style={bundleStyles.catRowTop}>
|
|
785
|
-
<BundleCategoryIcon type={part.iconType} size={18} color={part.color}/>
|
|
786
|
-
<View style={{ flex: 1 }}>
|
|
787
|
-
<Text style={bundleStyles.catRowTitle}>{part.label}</Text>
|
|
788
|
-
<Text style={bundleStyles.catRowDesc}>
|
|
789
|
-
{t('bundle.splitSize', { kb: part.kb, mb: part.mb })}
|
|
790
|
-
</Text>
|
|
791
|
-
</View>
|
|
792
|
-
<View style={{ alignItems: 'flex-end' }}>
|
|
793
|
-
<Text style={bundleStyles.catRowSize}>{part.pct}%</Text>
|
|
794
|
-
</View>
|
|
795
|
-
</View>
|
|
796
|
-
<View style={bundleStyles.catProgressTrack}>
|
|
797
|
-
<View style={[
|
|
798
|
-
bundleStyles.catProgressBar,
|
|
799
|
-
{ width: `${part.pct}%`, backgroundColor: part.color },
|
|
800
|
-
]}/>
|
|
801
|
-
</View>
|
|
802
|
-
</View>))}
|
|
803
|
-
|
|
804
|
-
{/* Production Binary Footprint */}
|
|
805
|
-
<View style={bundleStyles.totalSummaryCard}>
|
|
806
|
-
<View style={bundleStyles.totalSummaryRow}>
|
|
807
|
-
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
|
|
808
|
-
<PackageIcon color={AppColors.purple} size={16}/>
|
|
809
|
-
<Text style={bundleStyles.totalSummaryLabel}>
|
|
810
|
-
{t('bundle.productionFootprint')}
|
|
811
|
-
</Text>
|
|
812
|
-
</View>
|
|
813
|
-
<Text style={bundleStyles.totalSummaryValue}>
|
|
814
|
-
{t('bundle.productionValue', {
|
|
815
|
-
ios: analysis.production.ios.totalInstallMb,
|
|
816
|
-
android: analysis.production.android.totalInstallMb,
|
|
817
|
-
})}
|
|
818
|
-
</Text>
|
|
819
|
-
</View>
|
|
820
|
-
<Text style={bundleStyles.totalSummaryFormula}>
|
|
821
|
-
{t('bundle.productionDownload', {
|
|
822
|
-
ios: analysis.production.ios.totalDownloadMb,
|
|
823
|
-
aab: analysis.production.androidAab.totalDownloadMb,
|
|
824
|
-
apk: analysis.production.androidApk.totalDownloadMb,
|
|
825
|
-
})}
|
|
826
|
-
</Text>
|
|
827
|
-
</View>
|
|
828
|
-
</View>)}
|
|
829
821
|
|
|
830
822
|
{/* Visual Type Distribution Ratio Bar */}
|
|
831
823
|
<View style={bundleStyles.sectionCard}>
|
|
@@ -839,67 +831,64 @@ const BundleTab = React.memo(() => {
|
|
|
839
831
|
<CopyButton value={() => ({
|
|
840
832
|
totalMb: summary.totalMb,
|
|
841
833
|
totalKb: summary.totalKb,
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
json: summary.json,
|
|
834
|
+
nodeModules: { kb: summary.nodeModulesKb, pct: summary.nodeModulesPct },
|
|
835
|
+
metroRuntime: { kb: summary.metroKb, pct: summary.metroPct },
|
|
836
|
+
appSource: { kb: summary.appSourceKb, pct: summary.appSourcePct },
|
|
837
|
+
assetsMedia: { kb: summary.assetsMediaKb, pct: summary.assetsMediaPct },
|
|
847
838
|
})} label={t('bundle.treemapJson')}/>
|
|
848
839
|
</View>
|
|
849
840
|
|
|
850
|
-
{/* Stacked Colored Bar */}
|
|
841
|
+
{/* Stacked Colored Bar for Full 6.0 MB Bundle */}
|
|
851
842
|
<View style={bundleStyles.treemapBar}>
|
|
852
|
-
<View style={{ flex: summary.
|
|
853
|
-
<View style={{ flex: summary.
|
|
854
|
-
<View style={{ flex: summary.
|
|
855
|
-
<View style={{ flex: summary.
|
|
856
|
-
<View style={{ flex: summary.json.pct, backgroundColor: AppColors.emerald500, height: 16 }}/>
|
|
843
|
+
<View style={{ flex: summary.nodeModulesPct, backgroundColor: AppColors.indigo500, height: 16 }}/>
|
|
844
|
+
<View style={{ flex: summary.metroPct, backgroundColor: AppColors.amber500, height: 16 }}/>
|
|
845
|
+
<View style={{ flex: summary.appSourcePct, backgroundColor: AppColors.sky500, height: 16 }}/>
|
|
846
|
+
<View style={{ flex: summary.assetsMediaPct, backgroundColor: AppColors.pink500, height: 16 }}/>
|
|
857
847
|
</View>
|
|
858
848
|
|
|
859
849
|
{/* Legend Grid */}
|
|
860
850
|
<View style={bundleStyles.legendGrid}>
|
|
861
851
|
<View style={bundleStyles.legendItem}>
|
|
862
|
-
<BundleCategoryIcon type="
|
|
863
|
-
<Text style={bundleStyles.legendText}>
|
|
864
|
-
{t('bundle.legendImages')}{' '}
|
|
865
|
-
<Text style={bundleStyles.legendVal}>
|
|
866
|
-
{t('bundle.legendValue', { kb: summary.images.kb, pct: summary.images.pct })}
|
|
867
|
-
</Text>
|
|
868
|
-
</Text>
|
|
869
|
-
</View>
|
|
870
|
-
<View style={bundleStyles.legendItem}>
|
|
871
|
-
<BundleCategoryIcon type="typescript" size={13} color={AppColors.sky500}/>
|
|
852
|
+
<BundleCategoryIcon type="javascript" size={13} color={AppColors.indigo500}/>
|
|
872
853
|
<Text style={bundleStyles.legendText}>
|
|
873
|
-
{
|
|
854
|
+
Node Modules{' '}
|
|
874
855
|
<Text style={bundleStyles.legendVal}>
|
|
875
|
-
{
|
|
856
|
+
{summary.nodeModulesKb >= 1024
|
|
857
|
+
? `${(summary.nodeModulesKb / 1024).toFixed(1)} MB`
|
|
858
|
+
: `${summary.nodeModulesKb} KB`} ({summary.nodeModulesPct}%)
|
|
876
859
|
</Text>
|
|
877
860
|
</Text>
|
|
878
861
|
</View>
|
|
879
862
|
<View style={bundleStyles.legendItem}>
|
|
880
|
-
<BundleCategoryIcon type="
|
|
863
|
+
<BundleCategoryIcon type="overhead" size={13} color={AppColors.amber500}/>
|
|
881
864
|
<Text style={bundleStyles.legendText}>
|
|
882
|
-
{
|
|
865
|
+
Metro Runtime{' '}
|
|
883
866
|
<Text style={bundleStyles.legendVal}>
|
|
884
|
-
{
|
|
867
|
+
{summary.metroKb >= 1024
|
|
868
|
+
? `${(summary.metroKb / 1024).toFixed(1)} MB`
|
|
869
|
+
: `${summary.metroKb} KB`} ({summary.metroPct}%)
|
|
885
870
|
</Text>
|
|
886
871
|
</Text>
|
|
887
872
|
</View>
|
|
888
873
|
<View style={bundleStyles.legendItem}>
|
|
889
|
-
<BundleCategoryIcon type="
|
|
874
|
+
<BundleCategoryIcon type="typescript" size={13} color={AppColors.sky500}/>
|
|
890
875
|
<Text style={bundleStyles.legendText}>
|
|
891
|
-
{
|
|
876
|
+
App Source{' '}
|
|
892
877
|
<Text style={bundleStyles.legendVal}>
|
|
893
|
-
{
|
|
878
|
+
{summary.appSourceKb >= 1024
|
|
879
|
+
? `${(summary.appSourceKb / 1024).toFixed(1)} MB`
|
|
880
|
+
: `${summary.appSourceKb} KB`} ({summary.appSourcePct}%)
|
|
894
881
|
</Text>
|
|
895
882
|
</Text>
|
|
896
883
|
</View>
|
|
897
884
|
<View style={bundleStyles.legendItem}>
|
|
898
|
-
<BundleCategoryIcon type="
|
|
885
|
+
<BundleCategoryIcon type="image" size={13} color={AppColors.pink500}/>
|
|
899
886
|
<Text style={bundleStyles.legendText}>
|
|
900
|
-
{
|
|
887
|
+
Assets & Media{' '}
|
|
901
888
|
<Text style={bundleStyles.legendVal}>
|
|
902
|
-
{
|
|
889
|
+
{summary.assetsMediaKb >= 1024
|
|
890
|
+
? `${(summary.assetsMediaKb / 1024).toFixed(1)} MB`
|
|
891
|
+
: `${summary.assetsMediaKb} KB`} ({summary.assetsMediaPct}%)
|
|
903
892
|
</Text>
|
|
904
893
|
</Text>
|
|
905
894
|
</View>
|
|
@@ -910,118 +899,333 @@ const BundleTab = React.memo(() => {
|
|
|
910
899
|
<View style={bundleStyles.sectionCard}>
|
|
911
900
|
<View style={bundleStyles.sectionHeaderRow}>
|
|
912
901
|
<Text style={bundleStyles.sectionTitle}>{t('bundle.categoryBreakdown')}</Text>
|
|
913
|
-
<CopyButton value={() => (
|
|
914
|
-
totalAssets: summary.totalCount,
|
|
915
|
-
totalSizeKb: summary.totalKb,
|
|
916
|
-
categories: {
|
|
917
|
-
images: summary.images,
|
|
918
|
-
javascript: summary.js,
|
|
919
|
-
fonts: summary.fonts,
|
|
920
|
-
typescript: summary.ts,
|
|
921
|
-
json: summary.json,
|
|
922
|
-
},
|
|
923
|
-
})} label={t('bundle.categoriesJson')}/>
|
|
902
|
+
<CopyButton value={() => summary} label={t('bundle.categoriesJson')}/>
|
|
924
903
|
</View>
|
|
925
904
|
|
|
926
|
-
{[
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
count: summary.images.count,
|
|
931
|
-
size: t('bundle.fileSizeKb', { size: summary.images.kb }),
|
|
932
|
-
pct: summary.images.pct,
|
|
933
|
-
color: AppColors.pink500,
|
|
934
|
-
desc: t('bundle.catImagesDesc'),
|
|
935
|
-
},
|
|
936
|
-
{
|
|
937
|
-
iconType: 'javascript',
|
|
938
|
-
title: t('bundle.catJsTitle'),
|
|
939
|
-
count: summary.js.count,
|
|
940
|
-
size: t('bundle.fileSizeKb', { size: summary.js.kb }),
|
|
941
|
-
pct: summary.js.pct,
|
|
942
|
-
color: AppColors.indigo500,
|
|
943
|
-
desc: t('bundle.catJsDesc'),
|
|
944
|
-
},
|
|
945
|
-
{
|
|
946
|
-
iconType: 'font',
|
|
947
|
-
title: t('bundle.catFontsTitle'),
|
|
948
|
-
count: summary.fonts.count,
|
|
949
|
-
size: t('bundle.fileSizeKb', { size: summary.fonts.kb }),
|
|
950
|
-
pct: summary.fonts.pct,
|
|
951
|
-
color: AppColors.purple500,
|
|
952
|
-
desc: bundleFiles
|
|
953
|
-
.filter(f => f.category === 'font')
|
|
954
|
-
.map(f => f.name)
|
|
955
|
-
.join(', ') || t('bundle.catFontsDesc'),
|
|
956
|
-
},
|
|
957
|
-
{
|
|
958
|
-
iconType: 'typescript',
|
|
959
|
-
title: t('bundle.catTsTitle'),
|
|
960
|
-
count: summary.ts.count,
|
|
961
|
-
size: t('bundle.fileSizeKb', { size: summary.ts.kb }),
|
|
962
|
-
pct: summary.ts.pct,
|
|
963
|
-
color: AppColors.sky500,
|
|
964
|
-
desc: t('bundle.catTsDesc'),
|
|
965
|
-
},
|
|
966
|
-
{
|
|
967
|
-
iconType: 'json',
|
|
968
|
-
title: t('bundle.catJsonTitle'),
|
|
969
|
-
count: summary.json.count,
|
|
970
|
-
size: t('bundle.fileSizeKb', { size: summary.json.kb }),
|
|
971
|
-
pct: summary.json.pct,
|
|
972
|
-
color: AppColors.emerald500,
|
|
973
|
-
desc: t('bundle.catJsonDesc'),
|
|
974
|
-
},
|
|
975
|
-
].map((cat, cIdx) => (<View key={cIdx} style={bundleStyles.catRowCard}>
|
|
905
|
+
{summary.categories.map((cat, cIdx) => (<Pressable key={cIdx} style={({ pressed }) => [
|
|
906
|
+
bundleStyles.catRowCard,
|
|
907
|
+
pressed && { opacity: 0.85, transform: [{ scale: 0.99 }] },
|
|
908
|
+
]} onPress={() => setActiveSubTab(cat.targetTab)}>
|
|
976
909
|
<View style={bundleStyles.catRowTop}>
|
|
977
|
-
<
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
</Text>
|
|
910
|
+
<View style={[
|
|
911
|
+
bundleStyles.catIconBox,
|
|
912
|
+
{ backgroundColor: `${cat.color}15`, borderColor: `${cat.color}30` },
|
|
913
|
+
]}>
|
|
914
|
+
<BundleCategoryIcon type={cat.iconType} size={18} color={cat.color}/>
|
|
983
915
|
</View>
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
<
|
|
916
|
+
|
|
917
|
+
<View style={bundleStyles.catRowTitleWrap}>
|
|
918
|
+
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', gap: 6 }}>
|
|
919
|
+
<Text style={bundleStyles.catRowTitle} numberOfLines={1}>
|
|
920
|
+
{cat.title}
|
|
921
|
+
</Text>
|
|
922
|
+
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
|
|
923
|
+
<Text style={bundleStyles.catRowSize}>
|
|
924
|
+
{cat.sizeKb >= 1024 ? `${cat.sizeMb} MB` : `${cat.sizeKb} KB`}
|
|
925
|
+
</Text>
|
|
926
|
+
<View style={[
|
|
927
|
+
bundleStyles.catPctPill,
|
|
928
|
+
{ backgroundColor: `${cat.color}18` },
|
|
929
|
+
]}>
|
|
930
|
+
<Text style={[bundleStyles.catPctText, { color: cat.color }]}>
|
|
931
|
+
{cat.pct}%
|
|
932
|
+
</Text>
|
|
933
|
+
</View>
|
|
934
|
+
</View>
|
|
935
|
+
</View>
|
|
936
|
+
|
|
937
|
+
<Text style={bundleStyles.catRowDesc} numberOfLines={2}>
|
|
938
|
+
{cat.count} items • {cat.desc}
|
|
939
|
+
</Text>
|
|
987
940
|
</View>
|
|
988
941
|
</View>
|
|
942
|
+
|
|
943
|
+
{/* Modern Rounded Progress Bar */}
|
|
989
944
|
<View style={bundleStyles.catProgressTrack}>
|
|
990
945
|
<View style={[
|
|
991
946
|
bundleStyles.catProgressBar,
|
|
992
947
|
{ width: `${cat.pct}%`, backgroundColor: cat.color },
|
|
993
948
|
]}/>
|
|
994
949
|
</View>
|
|
995
|
-
</
|
|
950
|
+
</Pressable>))}
|
|
996
951
|
|
|
997
|
-
{/* Total Summation Card */}
|
|
952
|
+
{/* Executive Total Summation Card */}
|
|
998
953
|
<View style={bundleStyles.totalSummaryCard}>
|
|
999
954
|
<View style={bundleStyles.totalSummaryRow}>
|
|
1000
|
-
<View style={
|
|
1001
|
-
<
|
|
1002
|
-
|
|
1003
|
-
|
|
955
|
+
<View style={bundleStyles.totalSummaryLabelWrap}>
|
|
956
|
+
<View style={bundleStyles.totalIconBadge}>
|
|
957
|
+
<PackageIcon color={AppColors.brandPurple} size={16}/>
|
|
958
|
+
</View>
|
|
959
|
+
<View>
|
|
960
|
+
<Text style={bundleStyles.totalSummaryLabel}>
|
|
961
|
+
TOTAL BUNDLE ASSETS
|
|
962
|
+
</Text>
|
|
963
|
+
<Text style={bundleStyles.totalSummarySub}>
|
|
964
|
+
{summary.totalCount} Tracked Modules & Media Assets
|
|
965
|
+
</Text>
|
|
966
|
+
</View>
|
|
967
|
+
</View>
|
|
968
|
+
<View style={{ alignItems: 'flex-end' }}>
|
|
969
|
+
<Text style={bundleStyles.totalSummaryValue}>
|
|
970
|
+
{summary.totalMb} MB
|
|
971
|
+
</Text>
|
|
972
|
+
<Text style={bundleStyles.totalSummaryKb}>
|
|
973
|
+
{summary.totalKb.toLocaleString()} KB
|
|
1004
974
|
</Text>
|
|
1005
975
|
</View>
|
|
1006
|
-
<Text style={bundleStyles.totalSummaryValue}>
|
|
1007
|
-
{t('bundle.totalBundleValue', { kb: summary.totalKb, mb: summary.totalMb })}
|
|
1008
|
-
</Text>
|
|
1009
976
|
</View>
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
977
|
+
|
|
978
|
+
{/* 4-Pill Chip Breakdown */}
|
|
979
|
+
<View style={bundleStyles.totalFormulaGrid}>
|
|
980
|
+
<View style={bundleStyles.totalChip}>
|
|
981
|
+
<View style={[bundleStyles.totalDot, { backgroundColor: AppColors.indigo500 }]}/>
|
|
982
|
+
<Text style={bundleStyles.totalChipText}>NPM: {(summary.nodeModulesKb / 1024).toFixed(1)}M ({summary.nodeModulesPct}%)</Text>
|
|
983
|
+
</View>
|
|
984
|
+
<View style={bundleStyles.totalChip}>
|
|
985
|
+
<View style={[bundleStyles.totalDot, { backgroundColor: AppColors.amber500 }]}/>
|
|
986
|
+
<Text style={bundleStyles.totalChipText}>Metro: {(summary.metroKb / 1024).toFixed(1)}M ({summary.metroPct}%)</Text>
|
|
987
|
+
</View>
|
|
988
|
+
<View style={bundleStyles.totalChip}>
|
|
989
|
+
<View style={[bundleStyles.totalDot, { backgroundColor: AppColors.sky500 }]}/>
|
|
990
|
+
<Text style={bundleStyles.totalChipText}>Source: {(summary.appSourceKb / 1024).toFixed(1)}M ({summary.appSourcePct}%)</Text>
|
|
991
|
+
</View>
|
|
992
|
+
<View style={bundleStyles.totalChip}>
|
|
993
|
+
<View style={[bundleStyles.totalDot, { backgroundColor: AppColors.pink500 }]}/>
|
|
994
|
+
<Text style={bundleStyles.totalChipText}>Media: {summary.assetsMediaKb}K ({summary.assetsMediaPct}%)</Text>
|
|
995
|
+
</View>
|
|
996
|
+
</View>
|
|
1021
997
|
</View>
|
|
1022
998
|
</View>
|
|
1023
999
|
</ScrollView>)}
|
|
1024
1000
|
|
|
1001
|
+
{/* ══════════════════════════════════════════════════════════════════════ */}
|
|
1002
|
+
{/* ── 2. TAB: PRODUCTION BUILDS & PLATFORMS ───────────────────────────── */}
|
|
1003
|
+
{/* ══════════════════════════════════════════════════════════════════════ */}
|
|
1004
|
+
{activeSubTab === 'production' && !isAnalyzing && (<ScrollView style={{ flex: 1 }} contentContainerStyle={bundleStyles.contentContainer} keyboardShouldPersistTaps="handled">
|
|
1005
|
+
|
|
1006
|
+
{/* Platform Segmented Switcher (Horizontally Scrollable) */}
|
|
1007
|
+
<ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={bundleStyles.prodPlatformScroll}>
|
|
1008
|
+
{[
|
|
1009
|
+
{ key: 'ios', label: 'iOS App (.ipa)', badge: `${analysis?.production.ios.totalInstallMb || 59.2} MB` },
|
|
1010
|
+
{ key: 'androidAab', label: 'Android AAB (.aab)', badge: `${analysis?.production.androidAab.totalInstallMb || 24.3} MB` },
|
|
1011
|
+
{ key: 'androidApk', label: 'Universal APK (.apk)', badge: `${analysis?.production.androidApk.totalInstallMb || 64.0} MB` },
|
|
1012
|
+
].map(tab => {
|
|
1013
|
+
const isSelected = prodPlatform === tab.key;
|
|
1014
|
+
return (<TouchableScale key={tab.key} onPress={() => setProdPlatform(tab.key)} style={[
|
|
1015
|
+
bundleStyles.prodPlatformBtn,
|
|
1016
|
+
isSelected && bundleStyles.prodPlatformBtnActive,
|
|
1017
|
+
]}>
|
|
1018
|
+
<Text style={[
|
|
1019
|
+
bundleStyles.prodPlatformBtnText,
|
|
1020
|
+
isSelected && bundleStyles.prodPlatformBtnTextActive,
|
|
1021
|
+
]}>
|
|
1022
|
+
{tab.label}
|
|
1023
|
+
</Text>
|
|
1024
|
+
<View style={[
|
|
1025
|
+
bundleStyles.prodPlatformBadge,
|
|
1026
|
+
isSelected && bundleStyles.prodPlatformBadgeActive,
|
|
1027
|
+
]}>
|
|
1028
|
+
<Text style={[
|
|
1029
|
+
bundleStyles.prodPlatformBadgeText,
|
|
1030
|
+
isSelected && bundleStyles.prodPlatformBadgeTextActive,
|
|
1031
|
+
]}>
|
|
1032
|
+
{tab.badge}
|
|
1033
|
+
</Text>
|
|
1034
|
+
</View>
|
|
1035
|
+
</TouchableScale>);
|
|
1036
|
+
})}
|
|
1037
|
+
</ScrollView>
|
|
1038
|
+
|
|
1039
|
+
{/* Production Platform Metrics Hero */}
|
|
1040
|
+
{analysis && (<View style={bundleStyles.heroCard}>
|
|
1041
|
+
<View style={bundleStyles.heroTopRow}>
|
|
1042
|
+
<View style={bundleStyles.heroIconWrap}>
|
|
1043
|
+
<PackageIcon color={AppColors.brandPurple} size={24}/>
|
|
1044
|
+
</View>
|
|
1045
|
+
<View style={{ flex: 1 }}>
|
|
1046
|
+
<Text style={bundleStyles.heroTitle}>
|
|
1047
|
+
{prodPlatform === 'ios'
|
|
1048
|
+
? 'iOS Production Binary Footprint'
|
|
1049
|
+
: prodPlatform === 'androidAab'
|
|
1050
|
+
? 'Google Play App Bundle (.aab)'
|
|
1051
|
+
: 'Universal Standalone APK (.apk)'}
|
|
1052
|
+
</Text>
|
|
1053
|
+
<Text style={bundleStyles.heroSubtitle}>
|
|
1054
|
+
{prodPlatform === 'ios'
|
|
1055
|
+
? 'App Store install & over-the-air cellular download estimates'
|
|
1056
|
+
: prodPlatform === 'androidAab'
|
|
1057
|
+
? 'Optimized per-device dynamic delivery split APK architecture'
|
|
1058
|
+
: 'Multi-ABI universal install archive for sideloading & direct distribution'}
|
|
1059
|
+
</Text>
|
|
1060
|
+
</View>
|
|
1061
|
+
<CopyButton value={() => analysis.production[prodPlatform]} label="Production Binary JSON"/>
|
|
1062
|
+
</View>
|
|
1063
|
+
|
|
1064
|
+
{/* 4-Stat Balanced 2x2 Metric Grid */}
|
|
1065
|
+
<View style={bundleStyles.metricsGrid}>
|
|
1066
|
+
<View style={bundleStyles.metricBox}>
|
|
1067
|
+
<Text style={bundleStyles.metricLabel}>INSTALL SIZE</Text>
|
|
1068
|
+
<Text style={[bundleStyles.metricValue, { color: AppColors.purple }]}>
|
|
1069
|
+
{analysis.production[prodPlatform].totalInstallMb} MB
|
|
1070
|
+
</Text>
|
|
1071
|
+
<Text style={bundleStyles.metricHint}>On-device uncompressed footprint</Text>
|
|
1072
|
+
</View>
|
|
1073
|
+
|
|
1074
|
+
<View style={bundleStyles.metricBox}>
|
|
1075
|
+
<Text style={bundleStyles.metricLabel}>DOWNLOAD SIZE</Text>
|
|
1076
|
+
<Text style={[bundleStyles.metricValue, { color: AppColors.emerald600 }]}>
|
|
1077
|
+
~{analysis.production[prodPlatform].totalDownloadMb} MB
|
|
1078
|
+
</Text>
|
|
1079
|
+
<Text style={bundleStyles.metricHint}>Store network transfer payload</Text>
|
|
1080
|
+
</View>
|
|
1081
|
+
|
|
1082
|
+
<View style={bundleStyles.metricBox}>
|
|
1083
|
+
<Text style={bundleStyles.metricLabel}>COMPRESSION</Text>
|
|
1084
|
+
<Text style={[bundleStyles.metricValue, { color: AppColors.sky500 }]}>
|
|
1085
|
+
{analysis.production[prodPlatform].compressionRatioPct}%
|
|
1086
|
+
</Text>
|
|
1087
|
+
<Text style={bundleStyles.metricHint}>Bytecode & asset ratio</Text>
|
|
1088
|
+
</View>
|
|
1089
|
+
|
|
1090
|
+
<View style={bundleStyles.metricBox}>
|
|
1091
|
+
<Text style={bundleStyles.metricLabel}>FORMAT ARCHITECTURE</Text>
|
|
1092
|
+
<Text style={[bundleStyles.metricValue, { color: AppColors.amber700 }]}>
|
|
1093
|
+
{prodPlatform === 'ios'
|
|
1094
|
+
? 'Mach-O 64-bit'
|
|
1095
|
+
: prodPlatform === 'androidAab'
|
|
1096
|
+
? 'Split APKs'
|
|
1097
|
+
: 'FAT Multi-ABI'}
|
|
1098
|
+
</Text>
|
|
1099
|
+
<Text style={bundleStyles.metricHint}>
|
|
1100
|
+
{prodPlatform === 'ios'
|
|
1101
|
+
? 'Apple ARM64 runtime'
|
|
1102
|
+
: prodPlatform === 'androidAab'
|
|
1103
|
+
? 'Dynamic Google Play delivery'
|
|
1104
|
+
: 'Sideload universal package'}
|
|
1105
|
+
</Text>
|
|
1106
|
+
</View>
|
|
1107
|
+
</View>
|
|
1108
|
+
</View>)}
|
|
1109
|
+
|
|
1110
|
+
{/* Component Breakdown Cards */}
|
|
1111
|
+
{analysis && (<View style={bundleStyles.sectionCard}>
|
|
1112
|
+
<View style={bundleStyles.sectionHeaderRow}>
|
|
1113
|
+
<View>
|
|
1114
|
+
<Text style={bundleStyles.sectionTitle}>Binary Component Architecture</Text>
|
|
1115
|
+
<Text style={bundleStyles.sectionSub}>
|
|
1116
|
+
Compiled native libraries, runtime bytecodes, assets, and signature blocks
|
|
1117
|
+
</Text>
|
|
1118
|
+
</View>
|
|
1119
|
+
<CopyButton value={() => analysis.production[prodPlatform].components} label="Components JSON"/>
|
|
1120
|
+
</View>
|
|
1121
|
+
|
|
1122
|
+
{analysis.production[prodPlatform].components.map((comp, idx) => (<View key={idx} style={bundleStyles.catRowCard}>
|
|
1123
|
+
<View style={bundleStyles.catRowTop}>
|
|
1124
|
+
<View style={[
|
|
1125
|
+
bundleStyles.catIconBox,
|
|
1126
|
+
{ backgroundColor: `${comp.color}15`, borderColor: `${comp.color}30` },
|
|
1127
|
+
]}>
|
|
1128
|
+
<BundleCategoryIcon type={comp.category === 'native'
|
|
1129
|
+
? 'source'
|
|
1130
|
+
: comp.category === 'frameworks'
|
|
1131
|
+
? 'deps'
|
|
1132
|
+
: comp.category === 'assets'
|
|
1133
|
+
? 'media'
|
|
1134
|
+
: comp.category === 'js'
|
|
1135
|
+
? 'javascript'
|
|
1136
|
+
: 'overhead'} size={18} color={comp.color}/>
|
|
1137
|
+
</View>
|
|
1138
|
+
|
|
1139
|
+
<View style={bundleStyles.catRowTitleWrap}>
|
|
1140
|
+
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', gap: 6 }}>
|
|
1141
|
+
<Text style={bundleStyles.catRowTitle} numberOfLines={1}>
|
|
1142
|
+
{comp.name}
|
|
1143
|
+
</Text>
|
|
1144
|
+
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
|
|
1145
|
+
<Text style={bundleStyles.catRowSize}>{comp.sizeMb} MB</Text>
|
|
1146
|
+
<View style={[
|
|
1147
|
+
bundleStyles.catPctPill,
|
|
1148
|
+
{ backgroundColor: `${comp.color}18` },
|
|
1149
|
+
]}>
|
|
1150
|
+
<Text style={[bundleStyles.catPctText, { color: comp.color }]}>
|
|
1151
|
+
{comp.pct}%
|
|
1152
|
+
</Text>
|
|
1153
|
+
</View>
|
|
1154
|
+
</View>
|
|
1155
|
+
</View>
|
|
1156
|
+
|
|
1157
|
+
<Text style={bundleStyles.catRowDesc} numberOfLines={2}>
|
|
1158
|
+
{comp.description}
|
|
1159
|
+
</Text>
|
|
1160
|
+
</View>
|
|
1161
|
+
</View>
|
|
1162
|
+
|
|
1163
|
+
<View style={bundleStyles.catProgressTrack}>
|
|
1164
|
+
<View style={[
|
|
1165
|
+
bundleStyles.catProgressBar,
|
|
1166
|
+
{ width: `${comp.pct}%`, backgroundColor: comp.color },
|
|
1167
|
+
]}/>
|
|
1168
|
+
</View>
|
|
1169
|
+
|
|
1170
|
+
{comp.advice ? (<View style={[bundleStyles.adviceBadge, bundleStyles.adviceOptimal, { marginTop: 6 }]}>
|
|
1171
|
+
<LightbulbIcon color={AppColors.emerald700} size={13}/>
|
|
1172
|
+
<Text style={[bundleStyles.adviceText, { color: AppColors.emerald700, flex: 1, marginLeft: 4 }]}>
|
|
1173
|
+
{comp.advice}
|
|
1174
|
+
</Text>
|
|
1175
|
+
</View>) : null}
|
|
1176
|
+
</View>))}
|
|
1177
|
+
|
|
1178
|
+
{/* Total Platform Binary Summary with JS Source Bundle Comparison */}
|
|
1179
|
+
<View style={bundleStyles.totalSummaryCard}>
|
|
1180
|
+
<View style={bundleStyles.totalSummaryRow}>
|
|
1181
|
+
<View style={bundleStyles.totalSummaryLabelWrap}>
|
|
1182
|
+
<View style={bundleStyles.totalIconBadge}>
|
|
1183
|
+
<PackageIcon color={AppColors.brandPurple} size={16}/>
|
|
1184
|
+
</View>
|
|
1185
|
+
<View>
|
|
1186
|
+
<Text style={bundleStyles.totalSummaryLabel}>
|
|
1187
|
+
{prodPlatform === 'ios'
|
|
1188
|
+
? 'TOTAL IOS APP BINARY'
|
|
1189
|
+
: prodPlatform === 'androidAab'
|
|
1190
|
+
? 'TOTAL ANDROID AAB BUNDLE'
|
|
1191
|
+
: 'TOTAL UNIVERSAL APK'}
|
|
1192
|
+
</Text>
|
|
1193
|
+
<Text style={bundleStyles.totalSummarySub}>
|
|
1194
|
+
{prodPlatform === 'ios'
|
|
1195
|
+
? 'App Store install & cellular download estimates'
|
|
1196
|
+
: prodPlatform === 'androidAab'
|
|
1197
|
+
? 'Google Play dynamic delivery split APK'
|
|
1198
|
+
: 'Universal standalone sideload package'}
|
|
1199
|
+
</Text>
|
|
1200
|
+
</View>
|
|
1201
|
+
</View>
|
|
1202
|
+
</View>
|
|
1203
|
+
|
|
1204
|
+
{/* Platform Metric Grid Chips */}
|
|
1205
|
+
<View style={bundleStyles.totalFormulaGrid}>
|
|
1206
|
+
<View style={bundleStyles.totalChip}>
|
|
1207
|
+
<View style={[bundleStyles.totalDot, { backgroundColor: AppColors.purple }]}/>
|
|
1208
|
+
<Text style={bundleStyles.totalChipText}>
|
|
1209
|
+
Install: {analysis.production[prodPlatform].totalInstallMb} MB
|
|
1210
|
+
</Text>
|
|
1211
|
+
</View>
|
|
1212
|
+
<View style={bundleStyles.totalChip}>
|
|
1213
|
+
<View style={[bundleStyles.totalDot, { backgroundColor: AppColors.sky500 }]}/>
|
|
1214
|
+
<Text style={bundleStyles.totalChipText}>
|
|
1215
|
+
Download: ~{analysis.production[prodPlatform].totalDownloadMb} MB
|
|
1216
|
+
</Text>
|
|
1217
|
+
</View>
|
|
1218
|
+
<View style={bundleStyles.totalChip}>
|
|
1219
|
+
<View style={[bundleStyles.totalDot, { backgroundColor: AppColors.emerald500 }]}/>
|
|
1220
|
+
<Text style={bundleStyles.totalChipText}>
|
|
1221
|
+
Compression: {analysis.production[prodPlatform].compressionRatioPct}%
|
|
1222
|
+
</Text>
|
|
1223
|
+
</View>
|
|
1224
|
+
</View>
|
|
1225
|
+
</View>
|
|
1226
|
+
</View>)}
|
|
1227
|
+
</ScrollView>)}
|
|
1228
|
+
|
|
1025
1229
|
{/* ══════════════════════════════════════════════════════════════════════ */}
|
|
1026
1230
|
{/* ── 2. TAB: FILES BREAKDOWN ─────────────────────────────────────────── */}
|
|
1027
1231
|
{/* ══════════════════════════════════════════════════════════════════════ */}
|
|
@@ -1113,8 +1317,8 @@ const BundleTab = React.memo(() => {
|
|
|
1113
1317
|
</View>
|
|
1114
1318
|
|
|
1115
1319
|
{/* ── MODE 1: DIRECTORY TREE VIEW ── */}
|
|
1116
|
-
{filesViewMode === 'tree' ? (<View style={bundleStyles.
|
|
1117
|
-
{fileTreeNodes.map((node, nIdx) => (<BundleTreeNodeView key={node.id} node={node} level={0} search={search} totalBundleKb={summary.totalKb}
|
|
1320
|
+
{filesViewMode === 'tree' ? (<View style={bundleStyles.treeRootCard}>
|
|
1321
|
+
{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}/>))}
|
|
1118
1322
|
</View>) : (
|
|
1119
1323
|
/* ── MODE 2: FLAT LIST VIEW ── */
|
|
1120
1324
|
filteredFiles.map((file, index) => {
|
|
@@ -1520,16 +1724,16 @@ const BundleTab = React.memo(() => {
|
|
|
1520
1724
|
badgeColor: AppColors.indigo500,
|
|
1521
1725
|
},
|
|
1522
1726
|
].map((tip, tIdx) => (<View key={tIdx} style={bundleStyles.tipItem}>
|
|
1523
|
-
<View style={
|
|
1524
|
-
<View style={
|
|
1727
|
+
<View style={bundleStyles.tipItemHeader}>
|
|
1728
|
+
<View style={bundleStyles.tipTitleWrap}>
|
|
1525
1729
|
<View style={bundleStyles.sNoBadge}>
|
|
1526
1730
|
<Text style={bundleStyles.sNoText}>#{tIdx + 1}</Text>
|
|
1527
1731
|
</View>
|
|
1528
1732
|
<BundleCategoryIcon type={tip.iconType} size={14} color={tip.iconColor}/>
|
|
1529
1733
|
<Text style={bundleStyles.tipTitle}>{tip.title}</Text>
|
|
1530
1734
|
</View>
|
|
1531
|
-
<View style={
|
|
1532
|
-
<View style={[bundleStyles.impactBadge, { backgroundColor: `${tip.badgeColor}
|
|
1735
|
+
<View style={bundleStyles.tipActionWrap}>
|
|
1736
|
+
<View style={[bundleStyles.impactBadge, { backgroundColor: `${tip.badgeColor}18`, borderColor: `${tip.badgeColor}40` }]}>
|
|
1533
1737
|
<Text style={[bundleStyles.impactBadgeText, { color: tip.badgeColor }]}>
|
|
1534
1738
|
{tip.badge}
|
|
1535
1739
|
</Text>
|
|
@@ -1549,6 +1753,19 @@ const bundleStyles = StyleSheet.create({
|
|
|
1549
1753
|
paddingVertical: 8,
|
|
1550
1754
|
borderBottomWidth: 1,
|
|
1551
1755
|
borderBottomColor: AppColors.dividerColor,
|
|
1756
|
+
flexDirection: 'row',
|
|
1757
|
+
alignItems: 'center',
|
|
1758
|
+
paddingRight: 10,
|
|
1759
|
+
},
|
|
1760
|
+
reloadButton: {
|
|
1761
|
+
padding: 8,
|
|
1762
|
+
borderRadius: 20,
|
|
1763
|
+
backgroundColor: AppColors.grayBackground,
|
|
1764
|
+
borderWidth: 1,
|
|
1765
|
+
borderColor: AppColors.dividerColor,
|
|
1766
|
+
alignItems: 'center',
|
|
1767
|
+
justifyContent: 'center',
|
|
1768
|
+
marginLeft: 4,
|
|
1552
1769
|
},
|
|
1553
1770
|
analyzingContainer: {
|
|
1554
1771
|
flex: 1,
|
|
@@ -1621,7 +1838,7 @@ const bundleStyles = StyleSheet.create({
|
|
|
1621
1838
|
},
|
|
1622
1839
|
heroTopRow: {
|
|
1623
1840
|
flexDirection: 'row',
|
|
1624
|
-
alignItems: '
|
|
1841
|
+
alignItems: 'flex-start',
|
|
1625
1842
|
gap: 12,
|
|
1626
1843
|
marginBottom: 16,
|
|
1627
1844
|
},
|
|
@@ -1683,6 +1900,57 @@ const bundleStyles = StyleSheet.create({
|
|
|
1683
1900
|
height: 8,
|
|
1684
1901
|
borderRadius: 4,
|
|
1685
1902
|
},
|
|
1903
|
+
prodPlatformScroll: {
|
|
1904
|
+
flexDirection: 'row',
|
|
1905
|
+
alignItems: 'center',
|
|
1906
|
+
gap: 8,
|
|
1907
|
+
paddingBottom: 4,
|
|
1908
|
+
},
|
|
1909
|
+
prodPlatformBtn: {
|
|
1910
|
+
flexDirection: 'row',
|
|
1911
|
+
alignItems: 'center',
|
|
1912
|
+
paddingVertical: 7,
|
|
1913
|
+
paddingHorizontal: 12,
|
|
1914
|
+
borderRadius: 20,
|
|
1915
|
+
backgroundColor: AppColors.white,
|
|
1916
|
+
borderWidth: 1,
|
|
1917
|
+
borderColor: AppColors.dividerColor,
|
|
1918
|
+
gap: 6,
|
|
1919
|
+
},
|
|
1920
|
+
prodPlatformBtnActive: {
|
|
1921
|
+
backgroundColor: AppColors.brandPurple,
|
|
1922
|
+
borderColor: AppColors.brandPurple,
|
|
1923
|
+
shadowColor: AppColors.brandPurple,
|
|
1924
|
+
shadowOffset: { width: 0, height: 2 },
|
|
1925
|
+
shadowOpacity: 0.18,
|
|
1926
|
+
shadowRadius: 3,
|
|
1927
|
+
elevation: 2,
|
|
1928
|
+
},
|
|
1929
|
+
prodPlatformBtnText: {
|
|
1930
|
+
fontFamily: AppFonts.interBold,
|
|
1931
|
+
fontSize: 11.5,
|
|
1932
|
+
color: AppColors.grayTextStrong,
|
|
1933
|
+
},
|
|
1934
|
+
prodPlatformBtnTextActive: {
|
|
1935
|
+
color: AppColors.white,
|
|
1936
|
+
},
|
|
1937
|
+
prodPlatformBadge: {
|
|
1938
|
+
paddingHorizontal: 6,
|
|
1939
|
+
paddingVertical: 2,
|
|
1940
|
+
borderRadius: 10,
|
|
1941
|
+
backgroundColor: `${AppColors.purple}14`,
|
|
1942
|
+
},
|
|
1943
|
+
prodPlatformBadgeActive: {
|
|
1944
|
+
backgroundColor: 'rgba(255, 255, 255, 0.25)',
|
|
1945
|
+
},
|
|
1946
|
+
prodPlatformBadgeText: {
|
|
1947
|
+
fontFamily: AppFonts.interBold,
|
|
1948
|
+
fontSize: 9.5,
|
|
1949
|
+
color: AppColors.purple,
|
|
1950
|
+
},
|
|
1951
|
+
prodPlatformBadgeTextActive: {
|
|
1952
|
+
color: AppColors.white,
|
|
1953
|
+
},
|
|
1686
1954
|
sectionCard: {
|
|
1687
1955
|
backgroundColor: AppColors.white,
|
|
1688
1956
|
borderRadius: 14,
|
|
@@ -1717,18 +1985,21 @@ const bundleStyles = StyleSheet.create({
|
|
|
1717
1985
|
legendGrid: {
|
|
1718
1986
|
flexDirection: 'row',
|
|
1719
1987
|
flexWrap: 'wrap',
|
|
1720
|
-
|
|
1988
|
+
justifyContent: 'space-between',
|
|
1989
|
+
rowGap: 8,
|
|
1990
|
+
columnGap: 10,
|
|
1721
1991
|
},
|
|
1722
1992
|
legendItem: {
|
|
1723
1993
|
flexDirection: 'row',
|
|
1724
1994
|
alignItems: 'center',
|
|
1725
|
-
gap:
|
|
1726
|
-
|
|
1995
|
+
gap: 6,
|
|
1996
|
+
width: '48%',
|
|
1727
1997
|
},
|
|
1728
1998
|
legendText: {
|
|
1729
1999
|
fontFamily: AppFonts.interMedium,
|
|
1730
2000
|
fontSize: 11,
|
|
1731
2001
|
color: AppColors.grayTextStrong,
|
|
2002
|
+
flex: 1,
|
|
1732
2003
|
},
|
|
1733
2004
|
legendVal: {
|
|
1734
2005
|
fontFamily: AppFonts.interBold,
|
|
@@ -1736,47 +2007,79 @@ const bundleStyles = StyleSheet.create({
|
|
|
1736
2007
|
},
|
|
1737
2008
|
catRowCard: {
|
|
1738
2009
|
backgroundColor: AppColors.grayBackground,
|
|
1739
|
-
borderRadius:
|
|
1740
|
-
padding:
|
|
2010
|
+
borderRadius: 12,
|
|
2011
|
+
padding: 12,
|
|
1741
2012
|
marginBottom: 8,
|
|
1742
2013
|
borderWidth: 1,
|
|
1743
2014
|
borderColor: AppColors.dividerColor,
|
|
2015
|
+
gap: 8,
|
|
1744
2016
|
},
|
|
1745
2017
|
catRowTop: {
|
|
1746
2018
|
flexDirection: 'row',
|
|
2019
|
+
alignItems: 'flex-start',
|
|
2020
|
+
gap: 10,
|
|
2021
|
+
},
|
|
2022
|
+
catIconBox: {
|
|
2023
|
+
width: 36,
|
|
2024
|
+
height: 36,
|
|
2025
|
+
borderRadius: 10,
|
|
2026
|
+
borderWidth: 1,
|
|
1747
2027
|
alignItems: 'center',
|
|
1748
|
-
|
|
1749
|
-
|
|
2028
|
+
justifyContent: 'center',
|
|
2029
|
+
flexShrink: 0,
|
|
2030
|
+
},
|
|
2031
|
+
catRowTitleWrap: {
|
|
2032
|
+
flex: 1,
|
|
2033
|
+
minWidth: 0,
|
|
2034
|
+
gap: 2,
|
|
1750
2035
|
},
|
|
1751
2036
|
catRowTitle: {
|
|
1752
2037
|
fontFamily: AppFonts.interBold,
|
|
1753
|
-
fontSize:
|
|
2038
|
+
fontSize: 13,
|
|
1754
2039
|
color: AppColors.primaryBlack,
|
|
2040
|
+
flex: 1,
|
|
1755
2041
|
},
|
|
1756
2042
|
catRowDesc: {
|
|
1757
2043
|
fontFamily: AppFonts.interRegular,
|
|
1758
|
-
fontSize: 10,
|
|
2044
|
+
fontSize: 10.5,
|
|
1759
2045
|
color: AppColors.grayTextWeak,
|
|
2046
|
+
lineHeight: 14.5,
|
|
1760
2047
|
},
|
|
1761
2048
|
catRowSize: {
|
|
1762
2049
|
fontFamily: AppFonts.interBold,
|
|
1763
|
-
fontSize: 12,
|
|
2050
|
+
fontSize: 12.5,
|
|
1764
2051
|
color: AppColors.primaryBlack,
|
|
1765
2052
|
},
|
|
2053
|
+
catRowRight: {
|
|
2054
|
+
alignItems: 'flex-end',
|
|
2055
|
+
justifyContent: 'center',
|
|
2056
|
+
flexShrink: 0,
|
|
2057
|
+
minWidth: 64,
|
|
2058
|
+
},
|
|
1766
2059
|
catRowPct: {
|
|
1767
2060
|
fontFamily: AppFonts.interMedium,
|
|
1768
2061
|
fontSize: 10,
|
|
1769
2062
|
color: AppColors.grayTextWeak,
|
|
1770
2063
|
},
|
|
2064
|
+
catPctPill: {
|
|
2065
|
+
paddingHorizontal: 5.5,
|
|
2066
|
+
paddingVertical: 1.5,
|
|
2067
|
+
borderRadius: 5,
|
|
2068
|
+
},
|
|
2069
|
+
catPctText: {
|
|
2070
|
+
fontFamily: AppFonts.interBold,
|
|
2071
|
+
fontSize: 9.5,
|
|
2072
|
+
},
|
|
1771
2073
|
catProgressTrack: {
|
|
1772
|
-
height:
|
|
1773
|
-
borderRadius: 2,
|
|
2074
|
+
height: 5,
|
|
2075
|
+
borderRadius: 2.5,
|
|
1774
2076
|
backgroundColor: `${AppColors.slate200}`,
|
|
1775
2077
|
overflow: 'hidden',
|
|
2078
|
+
marginTop: 2,
|
|
1776
2079
|
},
|
|
1777
2080
|
catProgressBar: {
|
|
1778
2081
|
height: '100%',
|
|
1779
|
-
borderRadius: 2,
|
|
2082
|
+
borderRadius: 2.5,
|
|
1780
2083
|
},
|
|
1781
2084
|
filterContainer: {
|
|
1782
2085
|
backgroundColor: AppColors.white,
|
|
@@ -1880,32 +2183,44 @@ const bundleStyles = StyleSheet.create({
|
|
|
1880
2183
|
fontSize: 11.5,
|
|
1881
2184
|
color: AppColors.purple,
|
|
1882
2185
|
},
|
|
1883
|
-
|
|
1884
|
-
gap: 6,
|
|
1885
|
-
},
|
|
1886
|
-
treeFolderContainer: {
|
|
2186
|
+
treeRootCard: {
|
|
1887
2187
|
backgroundColor: AppColors.white,
|
|
1888
|
-
borderRadius:
|
|
2188
|
+
borderRadius: 12,
|
|
1889
2189
|
borderWidth: 1,
|
|
1890
2190
|
borderColor: AppColors.dividerColor,
|
|
1891
2191
|
overflow: 'hidden',
|
|
1892
|
-
marginBottom:
|
|
2192
|
+
marginBottom: 16,
|
|
2193
|
+
},
|
|
2194
|
+
treeFolderBlock: {
|
|
2195
|
+
borderBottomWidth: StyleSheet.hairlineWidth,
|
|
2196
|
+
borderBottomColor: AppColors.dividerColor,
|
|
1893
2197
|
},
|
|
1894
|
-
|
|
2198
|
+
treeFolderRow: {
|
|
1895
2199
|
flexDirection: 'row',
|
|
1896
2200
|
alignItems: 'center',
|
|
1897
2201
|
justifyContent: 'space-between',
|
|
1898
|
-
paddingVertical:
|
|
2202
|
+
paddingVertical: 8,
|
|
1899
2203
|
paddingRight: 10,
|
|
1900
|
-
backgroundColor: `${AppColors.purple}
|
|
1901
|
-
borderBottomWidth: 1,
|
|
1902
|
-
borderBottomColor: `${AppColors.purple}14`,
|
|
2204
|
+
backgroundColor: `${AppColors.purple}06`,
|
|
1903
2205
|
},
|
|
1904
|
-
|
|
1905
|
-
|
|
2206
|
+
treeRowLeft: {
|
|
2207
|
+
flexDirection: 'row',
|
|
2208
|
+
alignItems: 'center',
|
|
2209
|
+
gap: 6,
|
|
2210
|
+
flex: 1,
|
|
2211
|
+
marginRight: 6,
|
|
2212
|
+
},
|
|
2213
|
+
treeRowRight: {
|
|
2214
|
+
flexDirection: 'row',
|
|
2215
|
+
alignItems: 'center',
|
|
2216
|
+
gap: 6,
|
|
2217
|
+
},
|
|
2218
|
+
treeChevron: {
|
|
2219
|
+
fontSize: 12,
|
|
1906
2220
|
color: AppColors.purple,
|
|
1907
2221
|
fontFamily: AppFonts.interBold,
|
|
1908
|
-
width:
|
|
2222
|
+
width: 12,
|
|
2223
|
+
textAlign: 'center',
|
|
1909
2224
|
},
|
|
1910
2225
|
treeFolderName: {
|
|
1911
2226
|
fontFamily: AppFonts.interBold,
|
|
@@ -1913,44 +2228,75 @@ const bundleStyles = StyleSheet.create({
|
|
|
1913
2228
|
color: AppColors.primaryBlack,
|
|
1914
2229
|
},
|
|
1915
2230
|
treeCountBadge: {
|
|
1916
|
-
paddingHorizontal:
|
|
1917
|
-
paddingVertical: 1
|
|
1918
|
-
borderRadius:
|
|
1919
|
-
backgroundColor: `${AppColors.purple}
|
|
2231
|
+
paddingHorizontal: 5,
|
|
2232
|
+
paddingVertical: 1,
|
|
2233
|
+
borderRadius: 8,
|
|
2234
|
+
backgroundColor: `${AppColors.purple}14`,
|
|
1920
2235
|
},
|
|
1921
2236
|
treeCountBadgeText: {
|
|
1922
2237
|
fontFamily: AppFonts.interMedium,
|
|
1923
|
-
fontSize:
|
|
2238
|
+
fontSize: 9.5,
|
|
1924
2239
|
color: AppColors.purple,
|
|
1925
2240
|
},
|
|
1926
2241
|
treeFolderSize: {
|
|
1927
|
-
fontFamily: AppFonts.
|
|
1928
|
-
fontSize: 11
|
|
1929
|
-
color: AppColors.
|
|
2242
|
+
fontFamily: AppFonts.interMedium,
|
|
2243
|
+
fontSize: 11,
|
|
2244
|
+
color: AppColors.grayTextStrong,
|
|
1930
2245
|
},
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
paddingVertical: 6,
|
|
1934
|
-
paddingRight: 8,
|
|
1935
|
-
gap: 4,
|
|
2246
|
+
treeChildrenContainer: {
|
|
2247
|
+
backgroundColor: AppColors.white,
|
|
1936
2248
|
},
|
|
1937
|
-
|
|
2249
|
+
treeFileRow: {
|
|
2250
|
+
flexDirection: 'row',
|
|
2251
|
+
alignItems: 'center',
|
|
2252
|
+
justifyContent: 'space-between',
|
|
2253
|
+
paddingVertical: 6,
|
|
2254
|
+
paddingRight: 10,
|
|
2255
|
+
borderBottomWidth: StyleSheet.hairlineWidth,
|
|
2256
|
+
borderBottomColor: `${AppColors.dividerColor}80`,
|
|
1938
2257
|
backgroundColor: AppColors.white,
|
|
1939
|
-
borderRadius: 8,
|
|
1940
|
-
paddingVertical: 8,
|
|
1941
|
-
paddingHorizontal: 8,
|
|
1942
|
-
borderWidth: 1,
|
|
1943
|
-
borderColor: AppColors.dividerColor,
|
|
1944
|
-
marginBottom: 4,
|
|
1945
2258
|
},
|
|
1946
|
-
|
|
1947
|
-
|
|
2259
|
+
treeFileRowUnused: {
|
|
2260
|
+
backgroundColor: `${AppColors.red500}06`,
|
|
2261
|
+
},
|
|
2262
|
+
treeBranchGuide: {
|
|
2263
|
+
width: 12,
|
|
1948
2264
|
alignItems: 'center',
|
|
1949
2265
|
justifyContent: 'center',
|
|
1950
2266
|
},
|
|
1951
|
-
|
|
2267
|
+
treeBranchGuideText: {
|
|
1952
2268
|
fontFamily: AppFonts.interRegular,
|
|
1953
2269
|
fontSize: 11,
|
|
2270
|
+
color: AppColors.dividerColor,
|
|
2271
|
+
},
|
|
2272
|
+
treeFileName: {
|
|
2273
|
+
fontFamily: AppFonts.interMedium,
|
|
2274
|
+
fontSize: 12,
|
|
2275
|
+
color: AppColors.primaryBlack,
|
|
2276
|
+
flexShrink: 1,
|
|
2277
|
+
},
|
|
2278
|
+
treeUnusedPill: {
|
|
2279
|
+
paddingHorizontal: 5,
|
|
2280
|
+
paddingVertical: 1,
|
|
2281
|
+
borderRadius: 4,
|
|
2282
|
+
backgroundColor: `${AppColors.red500}18`,
|
|
2283
|
+
},
|
|
2284
|
+
treeUnusedPillText: {
|
|
2285
|
+
fontFamily: AppFonts.interBold,
|
|
2286
|
+
fontSize: 9,
|
|
2287
|
+
color: AppColors.red600,
|
|
2288
|
+
},
|
|
2289
|
+
treeFileSizeGroup: {
|
|
2290
|
+
alignItems: 'flex-end',
|
|
2291
|
+
},
|
|
2292
|
+
treeFileSizeText: {
|
|
2293
|
+
fontFamily: AppFonts.interMedium,
|
|
2294
|
+
fontSize: 11,
|
|
2295
|
+
color: AppColors.primaryBlack,
|
|
2296
|
+
},
|
|
2297
|
+
treeFilePctText: {
|
|
2298
|
+
fontFamily: AppFonts.interRegular,
|
|
2299
|
+
fontSize: 9,
|
|
1954
2300
|
color: AppColors.grayTextWeak,
|
|
1955
2301
|
},
|
|
1956
2302
|
fileCard: {
|
|
@@ -2282,27 +2628,80 @@ const bundleStyles = StyleSheet.create({
|
|
|
2282
2628
|
},
|
|
2283
2629
|
totalSummaryCard: {
|
|
2284
2630
|
marginTop: 10,
|
|
2285
|
-
backgroundColor: `${AppColors.
|
|
2286
|
-
borderRadius:
|
|
2631
|
+
backgroundColor: `${AppColors.brandPurple}0B`,
|
|
2632
|
+
borderRadius: 12,
|
|
2287
2633
|
padding: 12,
|
|
2288
2634
|
borderWidth: 1,
|
|
2289
|
-
borderColor: `${AppColors.
|
|
2290
|
-
gap:
|
|
2635
|
+
borderColor: `${AppColors.brandPurple}25`,
|
|
2636
|
+
gap: 8,
|
|
2291
2637
|
},
|
|
2292
2638
|
totalSummaryRow: {
|
|
2293
2639
|
flexDirection: 'row',
|
|
2294
2640
|
justifyContent: 'space-between',
|
|
2295
2641
|
alignItems: 'center',
|
|
2296
2642
|
},
|
|
2643
|
+
totalSummaryLabelWrap: {
|
|
2644
|
+
flexDirection: 'row',
|
|
2645
|
+
alignItems: 'center',
|
|
2646
|
+
gap: 8,
|
|
2647
|
+
},
|
|
2648
|
+
totalIconBadge: {
|
|
2649
|
+
width: 32,
|
|
2650
|
+
height: 32,
|
|
2651
|
+
borderRadius: 8,
|
|
2652
|
+
backgroundColor: `${AppColors.brandPurple}15`,
|
|
2653
|
+
alignItems: 'center',
|
|
2654
|
+
justifyContent: 'center',
|
|
2655
|
+
},
|
|
2297
2656
|
totalSummaryLabel: {
|
|
2298
2657
|
fontFamily: AppFonts.interBold,
|
|
2299
|
-
fontSize: 11,
|
|
2300
|
-
color: AppColors.
|
|
2301
|
-
letterSpacing: 0.
|
|
2658
|
+
fontSize: 11.5,
|
|
2659
|
+
color: AppColors.brandPurple,
|
|
2660
|
+
letterSpacing: 0.6,
|
|
2661
|
+
},
|
|
2662
|
+
totalSummarySub: {
|
|
2663
|
+
fontFamily: AppFonts.interRegular,
|
|
2664
|
+
fontSize: 10,
|
|
2665
|
+
color: AppColors.grayTextWeak,
|
|
2666
|
+
marginTop: 1,
|
|
2302
2667
|
},
|
|
2303
2668
|
totalSummaryValue: {
|
|
2304
2669
|
fontFamily: AppFonts.interBold,
|
|
2305
|
-
fontSize:
|
|
2670
|
+
fontSize: 15,
|
|
2671
|
+
color: AppColors.primaryBlack,
|
|
2672
|
+
},
|
|
2673
|
+
totalSummaryKb: {
|
|
2674
|
+
fontFamily: AppFonts.interMedium,
|
|
2675
|
+
fontSize: 10,
|
|
2676
|
+
color: AppColors.grayTextWeak,
|
|
2677
|
+
},
|
|
2678
|
+
totalFormulaGrid: {
|
|
2679
|
+
flexDirection: 'row',
|
|
2680
|
+
flexWrap: 'wrap',
|
|
2681
|
+
gap: 6,
|
|
2682
|
+
paddingTop: 6,
|
|
2683
|
+
borderTopWidth: 1,
|
|
2684
|
+
borderTopColor: `${AppColors.brandPurple}15`,
|
|
2685
|
+
},
|
|
2686
|
+
totalChip: {
|
|
2687
|
+
flexDirection: 'row',
|
|
2688
|
+
alignItems: 'center',
|
|
2689
|
+
gap: 4,
|
|
2690
|
+
backgroundColor: AppColors.white,
|
|
2691
|
+
borderRadius: 6,
|
|
2692
|
+
paddingHorizontal: 6,
|
|
2693
|
+
paddingVertical: 3,
|
|
2694
|
+
borderWidth: 1,
|
|
2695
|
+
borderColor: AppColors.dividerColor,
|
|
2696
|
+
},
|
|
2697
|
+
totalDot: {
|
|
2698
|
+
width: 6,
|
|
2699
|
+
height: 6,
|
|
2700
|
+
borderRadius: 3,
|
|
2701
|
+
},
|
|
2702
|
+
totalChipText: {
|
|
2703
|
+
fontFamily: AppFonts.interBold,
|
|
2704
|
+
fontSize: 9.5,
|
|
2306
2705
|
color: AppColors.primaryBlack,
|
|
2307
2706
|
},
|
|
2308
2707
|
totalSummaryFormula: {
|
|
@@ -2337,24 +2736,45 @@ const bundleStyles = StyleSheet.create({
|
|
|
2337
2736
|
color: AppColors.purple,
|
|
2338
2737
|
},
|
|
2339
2738
|
tipItem: {
|
|
2340
|
-
marginBottom:
|
|
2739
|
+
marginBottom: 10,
|
|
2341
2740
|
backgroundColor: AppColors.white,
|
|
2342
|
-
padding:
|
|
2343
|
-
borderRadius:
|
|
2741
|
+
padding: 12,
|
|
2742
|
+
borderRadius: 10,
|
|
2344
2743
|
borderWidth: 1,
|
|
2345
2744
|
borderColor: AppColors.purple200,
|
|
2745
|
+
gap: 6,
|
|
2746
|
+
},
|
|
2747
|
+
tipItemHeader: {
|
|
2748
|
+
flexDirection: 'row',
|
|
2749
|
+
alignItems: 'flex-start',
|
|
2750
|
+
justifyContent: 'space-between',
|
|
2751
|
+
gap: 8,
|
|
2752
|
+
},
|
|
2753
|
+
tipTitleWrap: {
|
|
2754
|
+
flexDirection: 'row',
|
|
2755
|
+
alignItems: 'center',
|
|
2756
|
+
gap: 6,
|
|
2757
|
+
flex: 1,
|
|
2758
|
+
flexShrink: 1,
|
|
2759
|
+
},
|
|
2760
|
+
tipActionWrap: {
|
|
2761
|
+
flexDirection: 'row',
|
|
2762
|
+
alignItems: 'center',
|
|
2763
|
+
gap: 6,
|
|
2764
|
+
flexShrink: 0,
|
|
2346
2765
|
},
|
|
2347
2766
|
tipTitle: {
|
|
2348
2767
|
fontFamily: AppFonts.interBold,
|
|
2349
2768
|
fontSize: 12,
|
|
2350
2769
|
color: AppColors.purple900,
|
|
2770
|
+
flex: 1,
|
|
2771
|
+
flexShrink: 1,
|
|
2351
2772
|
},
|
|
2352
2773
|
tipDesc: {
|
|
2353
2774
|
fontFamily: AppFonts.interRegular,
|
|
2354
2775
|
fontSize: 11,
|
|
2355
2776
|
color: AppColors.gray500,
|
|
2356
2777
|
lineHeight: 16,
|
|
2357
|
-
marginTop: 4,
|
|
2358
2778
|
},
|
|
2359
2779
|
});
|
|
2360
2780
|
export default BundleTab;
|