react-native-inapp-inspector 1.1.23 → 1.1.25
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/dist/commonjs/components/AnalyticsEventCard.js +94 -88
- package/dist/commonjs/components/ConsoleLogCard.js +49 -25
- 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 +859 -402
- package/dist/commonjs/components/Inspector/InspectorContext.js +6 -1
- package/dist/commonjs/components/Inspector/InspectorHeader.js +13 -5
- package/dist/commonjs/components/Inspector/LogDetail.js +223 -111
- package/dist/commonjs/components/Inspector/MainScreen.js +1 -1
- package/dist/commonjs/components/Inspector/NetworkDetail.js +131 -81
- package/dist/commonjs/components/Inspector/PerformanceTab.js +20 -10
- package/dist/commonjs/components/NetworkIcons.d.ts +14 -0
- package/dist/commonjs/components/NetworkIcons.js +93 -1
- package/dist/commonjs/components/SegmentedTabs.js +31 -22
- 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/bundleAnalyzer.d.ts +6 -2
- package/dist/commonjs/customHooks/bundleAnalyzer.js +552 -114
- package/dist/commonjs/customHooks/consoleLogger.js +83 -45
- package/dist/commonjs/customHooks/networkLogger.js +10 -1
- 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 +53 -4
- package/dist/commonjs/index.d.ts +4 -0
- package/dist/commonjs/index.js +216 -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 +115 -9
- package/dist/commonjs/types/enums.js +103 -2
- package/dist/commonjs/types/interfaces.d.ts +19 -0
- package/dist/esm/components/AnalyticsEventCard.js +95 -89
- package/dist/esm/components/ConsoleLogCard.js +49 -25
- 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 +860 -403
- package/dist/esm/components/Inspector/InspectorContext.js +1 -0
- package/dist/esm/components/Inspector/InspectorHeader.js +14 -6
- package/dist/esm/components/Inspector/LogDetail.js +223 -111
- package/dist/esm/components/Inspector/MainScreen.js +2 -2
- package/dist/esm/components/Inspector/NetworkDetail.js +132 -49
- package/dist/esm/components/Inspector/PerformanceTab.js +20 -10
- package/dist/esm/components/NetworkIcons.d.ts +14 -0
- package/dist/esm/components/NetworkIcons.js +78 -0
- package/dist/esm/components/SegmentedTabs.js +31 -22
- 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/bundleAnalyzer.d.ts +6 -2
- package/dist/esm/customHooks/bundleAnalyzer.js +549 -114
- package/dist/esm/customHooks/consoleLogger.js +83 -45
- package/dist/esm/customHooks/networkLogger.js +10 -1
- 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 +53 -4
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +190 -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 +115 -9
- package/dist/esm/types/enums.js +102 -3
- package/dist/esm/types/interfaces.d.ts +19 -0
- package/package.json +1 -1
|
@@ -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>
|
|
@@ -724,94 +802,23 @@ const BundleTab = React.memo(() => {
|
|
|
724
802
|
</View>
|
|
725
803
|
</View>
|
|
726
804
|
|
|
727
|
-
{/*
|
|
728
|
-
{analysis && (<View style={bundleStyles.
|
|
729
|
-
<View style={
|
|
730
|
-
<
|
|
731
|
-
|
|
732
|
-
<Text style={bundleStyles.
|
|
733
|
-
{t('bundle.
|
|
734
|
-
mb: analysis.totalDevMb,
|
|
735
|
-
files: analysis.filesCount,
|
|
736
|
-
packages: analysis.packageCount,
|
|
737
|
-
})}
|
|
805
|
+
{/* Live analysis unavailable warning */}
|
|
806
|
+
{analysis && !analysis.isLive && (<View style={bundleStyles.liveWarningCard}>
|
|
807
|
+
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 6, flex: 1 }}>
|
|
808
|
+
<CircleAlertIcon color={AppColors.amber700} size={16}/>
|
|
809
|
+
<View style={{ flex: 1 }}>
|
|
810
|
+
<Text style={bundleStyles.liveWarningTitle}>
|
|
811
|
+
{t('bundle.liveAnalysisUnavailable')}
|
|
738
812
|
</Text>
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
</View>
|
|
742
|
-
|
|
743
|
-
{[
|
|
744
|
-
{
|
|
745
|
-
iconType: 'source',
|
|
746
|
-
label: t('bundle.splitAppSource'),
|
|
747
|
-
color: AppColors.sky500,
|
|
748
|
-
...analysis.splitUp.appSource,
|
|
749
|
-
},
|
|
750
|
-
{
|
|
751
|
-
iconType: 'deps',
|
|
752
|
-
label: t('bundle.splitNodeModules'),
|
|
753
|
-
color: AppColors.indigo500,
|
|
754
|
-
...analysis.splitUp.nodeModules,
|
|
755
|
-
},
|
|
756
|
-
{
|
|
757
|
-
iconType: 'media',
|
|
758
|
-
label: t('bundle.splitAssetsMedia'),
|
|
759
|
-
color: AppColors.pink500,
|
|
760
|
-
...analysis.splitUp.assetsMedia,
|
|
761
|
-
},
|
|
762
|
-
{
|
|
763
|
-
iconType: 'overhead',
|
|
764
|
-
label: t('bundle.splitMetroOverhead'),
|
|
765
|
-
color: AppColors.amber500,
|
|
766
|
-
...analysis.splitUp.metroDevOverhead,
|
|
767
|
-
},
|
|
768
|
-
].map((part, idx) => (<View key={idx} style={bundleStyles.catRowCard}>
|
|
769
|
-
<View style={bundleStyles.catRowTop}>
|
|
770
|
-
<BundleCategoryIcon type={part.iconType} size={18} color={part.color}/>
|
|
771
|
-
<View style={{ flex: 1 }}>
|
|
772
|
-
<Text style={bundleStyles.catRowTitle}>{part.label}</Text>
|
|
773
|
-
<Text style={bundleStyles.catRowDesc}>
|
|
774
|
-
{t('bundle.splitSize', { kb: part.kb, mb: part.mb })}
|
|
775
|
-
</Text>
|
|
776
|
-
</View>
|
|
777
|
-
<View style={{ alignItems: 'flex-end' }}>
|
|
778
|
-
<Text style={bundleStyles.catRowSize}>{part.pct}%</Text>
|
|
779
|
-
</View>
|
|
780
|
-
</View>
|
|
781
|
-
<View style={bundleStyles.catProgressTrack}>
|
|
782
|
-
<View style={[
|
|
783
|
-
bundleStyles.catProgressBar,
|
|
784
|
-
{ width: `${part.pct}%`, backgroundColor: part.color },
|
|
785
|
-
]}/>
|
|
786
|
-
</View>
|
|
787
|
-
</View>))}
|
|
788
|
-
|
|
789
|
-
{/* Production Binary Footprint */}
|
|
790
|
-
<View style={bundleStyles.totalSummaryCard}>
|
|
791
|
-
<View style={bundleStyles.totalSummaryRow}>
|
|
792
|
-
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
|
|
793
|
-
<PackageIcon color={AppColors.purple} size={16}/>
|
|
794
|
-
<Text style={bundleStyles.totalSummaryLabel}>
|
|
795
|
-
{t('bundle.productionFootprint')}
|
|
796
|
-
</Text>
|
|
797
|
-
</View>
|
|
798
|
-
<Text style={bundleStyles.totalSummaryValue}>
|
|
799
|
-
{t('bundle.productionValue', {
|
|
800
|
-
ios: analysis.production.ios.totalInstallMb,
|
|
801
|
-
android: analysis.production.android.totalInstallMb,
|
|
802
|
-
})}
|
|
813
|
+
<Text style={bundleStyles.liveWarningDesc}>
|
|
814
|
+
{t('bundle.liveAnalysisUnavailableSub')}
|
|
803
815
|
</Text>
|
|
804
816
|
</View>
|
|
805
|
-
<Text style={bundleStyles.totalSummaryFormula}>
|
|
806
|
-
{t('bundle.productionDownload', {
|
|
807
|
-
ios: analysis.production.ios.totalDownloadMb,
|
|
808
|
-
aab: analysis.production.androidAab.totalDownloadMb,
|
|
809
|
-
apk: analysis.production.androidApk.totalDownloadMb,
|
|
810
|
-
})}
|
|
811
|
-
</Text>
|
|
812
817
|
</View>
|
|
813
818
|
</View>)}
|
|
814
819
|
|
|
820
|
+
|
|
821
|
+
|
|
815
822
|
{/* Visual Type Distribution Ratio Bar */}
|
|
816
823
|
<View style={bundleStyles.sectionCard}>
|
|
817
824
|
<View style={bundleStyles.sectionHeaderRow}>
|
|
@@ -824,67 +831,64 @@ const BundleTab = React.memo(() => {
|
|
|
824
831
|
<CopyButton value={() => ({
|
|
825
832
|
totalMb: summary.totalMb,
|
|
826
833
|
totalKb: summary.totalKb,
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
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 },
|
|
832
838
|
})} label={t('bundle.treemapJson')}/>
|
|
833
839
|
</View>
|
|
834
840
|
|
|
835
|
-
{/* Stacked Colored Bar */}
|
|
841
|
+
{/* Stacked Colored Bar for Full 6.0 MB Bundle */}
|
|
836
842
|
<View style={bundleStyles.treemapBar}>
|
|
837
|
-
<View style={{ flex: summary.
|
|
838
|
-
<View style={{ flex: summary.
|
|
839
|
-
<View style={{ flex: summary.
|
|
840
|
-
<View style={{ flex: summary.
|
|
841
|
-
<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 }}/>
|
|
842
847
|
</View>
|
|
843
848
|
|
|
844
849
|
{/* Legend Grid */}
|
|
845
850
|
<View style={bundleStyles.legendGrid}>
|
|
846
851
|
<View style={bundleStyles.legendItem}>
|
|
847
|
-
<BundleCategoryIcon type="
|
|
848
|
-
<Text style={bundleStyles.legendText}>
|
|
849
|
-
{t('bundle.legendImages')}{' '}
|
|
850
|
-
<Text style={bundleStyles.legendVal}>
|
|
851
|
-
{t('bundle.legendValue', { kb: summary.images.kb, pct: summary.images.pct })}
|
|
852
|
-
</Text>
|
|
853
|
-
</Text>
|
|
854
|
-
</View>
|
|
855
|
-
<View style={bundleStyles.legendItem}>
|
|
856
|
-
<BundleCategoryIcon type="typescript" size={13} color={AppColors.sky500}/>
|
|
852
|
+
<BundleCategoryIcon type="javascript" size={13} color={AppColors.indigo500}/>
|
|
857
853
|
<Text style={bundleStyles.legendText}>
|
|
858
|
-
{
|
|
854
|
+
Node Modules{' '}
|
|
859
855
|
<Text style={bundleStyles.legendVal}>
|
|
860
|
-
{
|
|
856
|
+
{summary.nodeModulesKb >= 1024
|
|
857
|
+
? `${(summary.nodeModulesKb / 1024).toFixed(1)} MB`
|
|
858
|
+
: `${summary.nodeModulesKb} KB`} ({summary.nodeModulesPct}%)
|
|
861
859
|
</Text>
|
|
862
860
|
</Text>
|
|
863
861
|
</View>
|
|
864
862
|
<View style={bundleStyles.legendItem}>
|
|
865
|
-
<BundleCategoryIcon type="
|
|
863
|
+
<BundleCategoryIcon type="overhead" size={13} color={AppColors.amber500}/>
|
|
866
864
|
<Text style={bundleStyles.legendText}>
|
|
867
|
-
{
|
|
865
|
+
Metro Runtime{' '}
|
|
868
866
|
<Text style={bundleStyles.legendVal}>
|
|
869
|
-
{
|
|
867
|
+
{summary.metroKb >= 1024
|
|
868
|
+
? `${(summary.metroKb / 1024).toFixed(1)} MB`
|
|
869
|
+
: `${summary.metroKb} KB`} ({summary.metroPct}%)
|
|
870
870
|
</Text>
|
|
871
871
|
</Text>
|
|
872
872
|
</View>
|
|
873
873
|
<View style={bundleStyles.legendItem}>
|
|
874
|
-
<BundleCategoryIcon type="
|
|
874
|
+
<BundleCategoryIcon type="typescript" size={13} color={AppColors.sky500}/>
|
|
875
875
|
<Text style={bundleStyles.legendText}>
|
|
876
|
-
{
|
|
876
|
+
App Source{' '}
|
|
877
877
|
<Text style={bundleStyles.legendVal}>
|
|
878
|
-
{
|
|
878
|
+
{summary.appSourceKb >= 1024
|
|
879
|
+
? `${(summary.appSourceKb / 1024).toFixed(1)} MB`
|
|
880
|
+
: `${summary.appSourceKb} KB`} ({summary.appSourcePct}%)
|
|
879
881
|
</Text>
|
|
880
882
|
</Text>
|
|
881
883
|
</View>
|
|
882
884
|
<View style={bundleStyles.legendItem}>
|
|
883
|
-
<BundleCategoryIcon type="
|
|
885
|
+
<BundleCategoryIcon type="image" size={13} color={AppColors.pink500}/>
|
|
884
886
|
<Text style={bundleStyles.legendText}>
|
|
885
|
-
{
|
|
887
|
+
Assets & Media{' '}
|
|
886
888
|
<Text style={bundleStyles.legendVal}>
|
|
887
|
-
{
|
|
889
|
+
{summary.assetsMediaKb >= 1024
|
|
890
|
+
? `${(summary.assetsMediaKb / 1024).toFixed(1)} MB`
|
|
891
|
+
: `${summary.assetsMediaKb} KB`} ({summary.assetsMediaPct}%)
|
|
888
892
|
</Text>
|
|
889
893
|
</Text>
|
|
890
894
|
</View>
|
|
@@ -895,118 +899,333 @@ const BundleTab = React.memo(() => {
|
|
|
895
899
|
<View style={bundleStyles.sectionCard}>
|
|
896
900
|
<View style={bundleStyles.sectionHeaderRow}>
|
|
897
901
|
<Text style={bundleStyles.sectionTitle}>{t('bundle.categoryBreakdown')}</Text>
|
|
898
|
-
<CopyButton value={() => (
|
|
899
|
-
totalAssets: summary.totalCount,
|
|
900
|
-
totalSizeKb: summary.totalKb,
|
|
901
|
-
categories: {
|
|
902
|
-
images: summary.images,
|
|
903
|
-
javascript: summary.js,
|
|
904
|
-
fonts: summary.fonts,
|
|
905
|
-
typescript: summary.ts,
|
|
906
|
-
json: summary.json,
|
|
907
|
-
},
|
|
908
|
-
})} label={t('bundle.categoriesJson')}/>
|
|
902
|
+
<CopyButton value={() => summary} label={t('bundle.categoriesJson')}/>
|
|
909
903
|
</View>
|
|
910
904
|
|
|
911
|
-
{[
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
count: summary.images.count,
|
|
916
|
-
size: t('bundle.fileSizeKb', { size: summary.images.kb }),
|
|
917
|
-
pct: summary.images.pct,
|
|
918
|
-
color: AppColors.pink500,
|
|
919
|
-
desc: t('bundle.catImagesDesc'),
|
|
920
|
-
},
|
|
921
|
-
{
|
|
922
|
-
iconType: 'javascript',
|
|
923
|
-
title: t('bundle.catJsTitle'),
|
|
924
|
-
count: summary.js.count,
|
|
925
|
-
size: t('bundle.fileSizeKb', { size: summary.js.kb }),
|
|
926
|
-
pct: summary.js.pct,
|
|
927
|
-
color: AppColors.indigo500,
|
|
928
|
-
desc: t('bundle.catJsDesc'),
|
|
929
|
-
},
|
|
930
|
-
{
|
|
931
|
-
iconType: 'font',
|
|
932
|
-
title: t('bundle.catFontsTitle'),
|
|
933
|
-
count: summary.fonts.count,
|
|
934
|
-
size: t('bundle.fileSizeKb', { size: summary.fonts.kb }),
|
|
935
|
-
pct: summary.fonts.pct,
|
|
936
|
-
color: AppColors.purple500,
|
|
937
|
-
desc: bundleFiles
|
|
938
|
-
.filter(f => f.category === 'font')
|
|
939
|
-
.map(f => f.name)
|
|
940
|
-
.join(', ') || t('bundle.catFontsDesc'),
|
|
941
|
-
},
|
|
942
|
-
{
|
|
943
|
-
iconType: 'typescript',
|
|
944
|
-
title: t('bundle.catTsTitle'),
|
|
945
|
-
count: summary.ts.count,
|
|
946
|
-
size: t('bundle.fileSizeKb', { size: summary.ts.kb }),
|
|
947
|
-
pct: summary.ts.pct,
|
|
948
|
-
color: AppColors.sky500,
|
|
949
|
-
desc: t('bundle.catTsDesc'),
|
|
950
|
-
},
|
|
951
|
-
{
|
|
952
|
-
iconType: 'json',
|
|
953
|
-
title: t('bundle.catJsonTitle'),
|
|
954
|
-
count: summary.json.count,
|
|
955
|
-
size: t('bundle.fileSizeKb', { size: summary.json.kb }),
|
|
956
|
-
pct: summary.json.pct,
|
|
957
|
-
color: AppColors.emerald500,
|
|
958
|
-
desc: t('bundle.catJsonDesc'),
|
|
959
|
-
},
|
|
960
|
-
].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)}>
|
|
961
909
|
<View style={bundleStyles.catRowTop}>
|
|
962
|
-
<
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
</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}/>
|
|
968
915
|
</View>
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
<
|
|
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>
|
|
972
940
|
</View>
|
|
973
941
|
</View>
|
|
942
|
+
|
|
943
|
+
{/* Modern Rounded Progress Bar */}
|
|
974
944
|
<View style={bundleStyles.catProgressTrack}>
|
|
975
945
|
<View style={[
|
|
976
946
|
bundleStyles.catProgressBar,
|
|
977
947
|
{ width: `${cat.pct}%`, backgroundColor: cat.color },
|
|
978
948
|
]}/>
|
|
979
949
|
</View>
|
|
980
|
-
</
|
|
950
|
+
</Pressable>))}
|
|
981
951
|
|
|
982
|
-
{/* Total Summation Card */}
|
|
952
|
+
{/* Executive Total Summation Card */}
|
|
983
953
|
<View style={bundleStyles.totalSummaryCard}>
|
|
984
954
|
<View style={bundleStyles.totalSummaryRow}>
|
|
985
|
-
<View style={
|
|
986
|
-
<
|
|
987
|
-
|
|
988
|
-
|
|
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
|
|
989
974
|
</Text>
|
|
990
975
|
</View>
|
|
991
|
-
<Text style={bundleStyles.totalSummaryValue}>
|
|
992
|
-
{t('bundle.totalBundleValue', { kb: summary.totalKb, mb: summary.totalMb })}
|
|
993
|
-
</Text>
|
|
994
976
|
</View>
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
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>
|
|
1006
997
|
</View>
|
|
1007
998
|
</View>
|
|
1008
999
|
</ScrollView>)}
|
|
1009
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
|
+
|
|
1010
1229
|
{/* ══════════════════════════════════════════════════════════════════════ */}
|
|
1011
1230
|
{/* ── 2. TAB: FILES BREAKDOWN ─────────────────────────────────────────── */}
|
|
1012
1231
|
{/* ══════════════════════════════════════════════════════════════════════ */}
|
|
@@ -1098,8 +1317,8 @@ const BundleTab = React.memo(() => {
|
|
|
1098
1317
|
</View>
|
|
1099
1318
|
|
|
1100
1319
|
{/* ── MODE 1: DIRECTORY TREE VIEW ── */}
|
|
1101
|
-
{filesViewMode === 'tree' ? (<View style={bundleStyles.
|
|
1102
|
-
{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}/>))}
|
|
1103
1322
|
</View>) : (
|
|
1104
1323
|
/* ── MODE 2: FLAT LIST VIEW ── */
|
|
1105
1324
|
filteredFiles.map((file, index) => {
|
|
@@ -1505,16 +1724,16 @@ const BundleTab = React.memo(() => {
|
|
|
1505
1724
|
badgeColor: AppColors.indigo500,
|
|
1506
1725
|
},
|
|
1507
1726
|
].map((tip, tIdx) => (<View key={tIdx} style={bundleStyles.tipItem}>
|
|
1508
|
-
<View style={
|
|
1509
|
-
<View style={
|
|
1727
|
+
<View style={bundleStyles.tipItemHeader}>
|
|
1728
|
+
<View style={bundleStyles.tipTitleWrap}>
|
|
1510
1729
|
<View style={bundleStyles.sNoBadge}>
|
|
1511
1730
|
<Text style={bundleStyles.sNoText}>#{tIdx + 1}</Text>
|
|
1512
1731
|
</View>
|
|
1513
1732
|
<BundleCategoryIcon type={tip.iconType} size={14} color={tip.iconColor}/>
|
|
1514
1733
|
<Text style={bundleStyles.tipTitle}>{tip.title}</Text>
|
|
1515
1734
|
</View>
|
|
1516
|
-
<View style={
|
|
1517
|
-
<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` }]}>
|
|
1518
1737
|
<Text style={[bundleStyles.impactBadgeText, { color: tip.badgeColor }]}>
|
|
1519
1738
|
{tip.badge}
|
|
1520
1739
|
</Text>
|
|
@@ -1534,6 +1753,19 @@ const bundleStyles = StyleSheet.create({
|
|
|
1534
1753
|
paddingVertical: 8,
|
|
1535
1754
|
borderBottomWidth: 1,
|
|
1536
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,
|
|
1537
1769
|
},
|
|
1538
1770
|
analyzingContainer: {
|
|
1539
1771
|
flex: 1,
|
|
@@ -1606,7 +1838,7 @@ const bundleStyles = StyleSheet.create({
|
|
|
1606
1838
|
},
|
|
1607
1839
|
heroTopRow: {
|
|
1608
1840
|
flexDirection: 'row',
|
|
1609
|
-
alignItems: '
|
|
1841
|
+
alignItems: 'flex-start',
|
|
1610
1842
|
gap: 12,
|
|
1611
1843
|
marginBottom: 16,
|
|
1612
1844
|
},
|
|
@@ -1668,6 +1900,57 @@ const bundleStyles = StyleSheet.create({
|
|
|
1668
1900
|
height: 8,
|
|
1669
1901
|
borderRadius: 4,
|
|
1670
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
|
+
},
|
|
1671
1954
|
sectionCard: {
|
|
1672
1955
|
backgroundColor: AppColors.white,
|
|
1673
1956
|
borderRadius: 14,
|
|
@@ -1702,18 +1985,21 @@ const bundleStyles = StyleSheet.create({
|
|
|
1702
1985
|
legendGrid: {
|
|
1703
1986
|
flexDirection: 'row',
|
|
1704
1987
|
flexWrap: 'wrap',
|
|
1705
|
-
|
|
1988
|
+
justifyContent: 'space-between',
|
|
1989
|
+
rowGap: 8,
|
|
1990
|
+
columnGap: 10,
|
|
1706
1991
|
},
|
|
1707
1992
|
legendItem: {
|
|
1708
1993
|
flexDirection: 'row',
|
|
1709
1994
|
alignItems: 'center',
|
|
1710
|
-
gap:
|
|
1711
|
-
|
|
1995
|
+
gap: 6,
|
|
1996
|
+
width: '48%',
|
|
1712
1997
|
},
|
|
1713
1998
|
legendText: {
|
|
1714
1999
|
fontFamily: AppFonts.interMedium,
|
|
1715
2000
|
fontSize: 11,
|
|
1716
2001
|
color: AppColors.grayTextStrong,
|
|
2002
|
+
flex: 1,
|
|
1717
2003
|
},
|
|
1718
2004
|
legendVal: {
|
|
1719
2005
|
fontFamily: AppFonts.interBold,
|
|
@@ -1721,47 +2007,79 @@ const bundleStyles = StyleSheet.create({
|
|
|
1721
2007
|
},
|
|
1722
2008
|
catRowCard: {
|
|
1723
2009
|
backgroundColor: AppColors.grayBackground,
|
|
1724
|
-
borderRadius:
|
|
1725
|
-
padding:
|
|
2010
|
+
borderRadius: 12,
|
|
2011
|
+
padding: 12,
|
|
1726
2012
|
marginBottom: 8,
|
|
1727
2013
|
borderWidth: 1,
|
|
1728
2014
|
borderColor: AppColors.dividerColor,
|
|
2015
|
+
gap: 8,
|
|
1729
2016
|
},
|
|
1730
2017
|
catRowTop: {
|
|
1731
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,
|
|
1732
2027
|
alignItems: 'center',
|
|
1733
|
-
|
|
1734
|
-
|
|
2028
|
+
justifyContent: 'center',
|
|
2029
|
+
flexShrink: 0,
|
|
2030
|
+
},
|
|
2031
|
+
catRowTitleWrap: {
|
|
2032
|
+
flex: 1,
|
|
2033
|
+
minWidth: 0,
|
|
2034
|
+
gap: 2,
|
|
1735
2035
|
},
|
|
1736
2036
|
catRowTitle: {
|
|
1737
2037
|
fontFamily: AppFonts.interBold,
|
|
1738
|
-
fontSize:
|
|
2038
|
+
fontSize: 13,
|
|
1739
2039
|
color: AppColors.primaryBlack,
|
|
2040
|
+
flex: 1,
|
|
1740
2041
|
},
|
|
1741
2042
|
catRowDesc: {
|
|
1742
2043
|
fontFamily: AppFonts.interRegular,
|
|
1743
|
-
fontSize: 10,
|
|
2044
|
+
fontSize: 10.5,
|
|
1744
2045
|
color: AppColors.grayTextWeak,
|
|
2046
|
+
lineHeight: 14.5,
|
|
1745
2047
|
},
|
|
1746
2048
|
catRowSize: {
|
|
1747
2049
|
fontFamily: AppFonts.interBold,
|
|
1748
|
-
fontSize: 12,
|
|
2050
|
+
fontSize: 12.5,
|
|
1749
2051
|
color: AppColors.primaryBlack,
|
|
1750
2052
|
},
|
|
2053
|
+
catRowRight: {
|
|
2054
|
+
alignItems: 'flex-end',
|
|
2055
|
+
justifyContent: 'center',
|
|
2056
|
+
flexShrink: 0,
|
|
2057
|
+
minWidth: 64,
|
|
2058
|
+
},
|
|
1751
2059
|
catRowPct: {
|
|
1752
2060
|
fontFamily: AppFonts.interMedium,
|
|
1753
2061
|
fontSize: 10,
|
|
1754
2062
|
color: AppColors.grayTextWeak,
|
|
1755
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
|
+
},
|
|
1756
2073
|
catProgressTrack: {
|
|
1757
|
-
height:
|
|
1758
|
-
borderRadius: 2,
|
|
2074
|
+
height: 5,
|
|
2075
|
+
borderRadius: 2.5,
|
|
1759
2076
|
backgroundColor: `${AppColors.slate200}`,
|
|
1760
2077
|
overflow: 'hidden',
|
|
2078
|
+
marginTop: 2,
|
|
1761
2079
|
},
|
|
1762
2080
|
catProgressBar: {
|
|
1763
2081
|
height: '100%',
|
|
1764
|
-
borderRadius: 2,
|
|
2082
|
+
borderRadius: 2.5,
|
|
1765
2083
|
},
|
|
1766
2084
|
filterContainer: {
|
|
1767
2085
|
backgroundColor: AppColors.white,
|
|
@@ -1865,32 +2183,44 @@ const bundleStyles = StyleSheet.create({
|
|
|
1865
2183
|
fontSize: 11.5,
|
|
1866
2184
|
color: AppColors.purple,
|
|
1867
2185
|
},
|
|
1868
|
-
|
|
1869
|
-
gap: 6,
|
|
1870
|
-
},
|
|
1871
|
-
treeFolderContainer: {
|
|
2186
|
+
treeRootCard: {
|
|
1872
2187
|
backgroundColor: AppColors.white,
|
|
1873
|
-
borderRadius:
|
|
2188
|
+
borderRadius: 12,
|
|
1874
2189
|
borderWidth: 1,
|
|
1875
2190
|
borderColor: AppColors.dividerColor,
|
|
1876
2191
|
overflow: 'hidden',
|
|
1877
|
-
marginBottom:
|
|
2192
|
+
marginBottom: 16,
|
|
2193
|
+
},
|
|
2194
|
+
treeFolderBlock: {
|
|
2195
|
+
borderBottomWidth: StyleSheet.hairlineWidth,
|
|
2196
|
+
borderBottomColor: AppColors.dividerColor,
|
|
1878
2197
|
},
|
|
1879
|
-
|
|
2198
|
+
treeFolderRow: {
|
|
1880
2199
|
flexDirection: 'row',
|
|
1881
2200
|
alignItems: 'center',
|
|
1882
2201
|
justifyContent: 'space-between',
|
|
1883
|
-
paddingVertical:
|
|
2202
|
+
paddingVertical: 8,
|
|
1884
2203
|
paddingRight: 10,
|
|
1885
|
-
backgroundColor: `${AppColors.purple}
|
|
1886
|
-
borderBottomWidth: 1,
|
|
1887
|
-
borderBottomColor: `${AppColors.purple}14`,
|
|
2204
|
+
backgroundColor: `${AppColors.purple}06`,
|
|
1888
2205
|
},
|
|
1889
|
-
|
|
1890
|
-
|
|
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,
|
|
1891
2220
|
color: AppColors.purple,
|
|
1892
2221
|
fontFamily: AppFonts.interBold,
|
|
1893
|
-
width:
|
|
2222
|
+
width: 12,
|
|
2223
|
+
textAlign: 'center',
|
|
1894
2224
|
},
|
|
1895
2225
|
treeFolderName: {
|
|
1896
2226
|
fontFamily: AppFonts.interBold,
|
|
@@ -1898,44 +2228,75 @@ const bundleStyles = StyleSheet.create({
|
|
|
1898
2228
|
color: AppColors.primaryBlack,
|
|
1899
2229
|
},
|
|
1900
2230
|
treeCountBadge: {
|
|
1901
|
-
paddingHorizontal:
|
|
1902
|
-
paddingVertical: 1
|
|
1903
|
-
borderRadius:
|
|
1904
|
-
backgroundColor: `${AppColors.purple}
|
|
2231
|
+
paddingHorizontal: 5,
|
|
2232
|
+
paddingVertical: 1,
|
|
2233
|
+
borderRadius: 8,
|
|
2234
|
+
backgroundColor: `${AppColors.purple}14`,
|
|
1905
2235
|
},
|
|
1906
2236
|
treeCountBadgeText: {
|
|
1907
2237
|
fontFamily: AppFonts.interMedium,
|
|
1908
|
-
fontSize:
|
|
2238
|
+
fontSize: 9.5,
|
|
1909
2239
|
color: AppColors.purple,
|
|
1910
2240
|
},
|
|
1911
2241
|
treeFolderSize: {
|
|
1912
|
-
fontFamily: AppFonts.
|
|
1913
|
-
fontSize: 11
|
|
1914
|
-
color: AppColors.
|
|
2242
|
+
fontFamily: AppFonts.interMedium,
|
|
2243
|
+
fontSize: 11,
|
|
2244
|
+
color: AppColors.grayTextStrong,
|
|
1915
2245
|
},
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
paddingVertical: 6,
|
|
1919
|
-
paddingRight: 8,
|
|
1920
|
-
gap: 4,
|
|
2246
|
+
treeChildrenContainer: {
|
|
2247
|
+
backgroundColor: AppColors.white,
|
|
1921
2248
|
},
|
|
1922
|
-
|
|
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`,
|
|
1923
2257
|
backgroundColor: AppColors.white,
|
|
1924
|
-
borderRadius: 8,
|
|
1925
|
-
paddingVertical: 8,
|
|
1926
|
-
paddingHorizontal: 8,
|
|
1927
|
-
borderWidth: 1,
|
|
1928
|
-
borderColor: AppColors.dividerColor,
|
|
1929
|
-
marginBottom: 4,
|
|
1930
2258
|
},
|
|
1931
|
-
|
|
1932
|
-
|
|
2259
|
+
treeFileRowUnused: {
|
|
2260
|
+
backgroundColor: `${AppColors.red500}06`,
|
|
2261
|
+
},
|
|
2262
|
+
treeBranchGuide: {
|
|
2263
|
+
width: 12,
|
|
1933
2264
|
alignItems: 'center',
|
|
1934
2265
|
justifyContent: 'center',
|
|
1935
2266
|
},
|
|
1936
|
-
|
|
2267
|
+
treeBranchGuideText: {
|
|
1937
2268
|
fontFamily: AppFonts.interRegular,
|
|
1938
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,
|
|
1939
2300
|
color: AppColors.grayTextWeak,
|
|
1940
2301
|
},
|
|
1941
2302
|
fileCard: {
|
|
@@ -2195,6 +2556,28 @@ const bundleStyles = StyleSheet.create({
|
|
|
2195
2556
|
borderWidth: 1,
|
|
2196
2557
|
borderColor: AppColors.errorBorder,
|
|
2197
2558
|
},
|
|
2559
|
+
liveWarningCard: {
|
|
2560
|
+
flexDirection: 'row',
|
|
2561
|
+
alignItems: 'center',
|
|
2562
|
+
backgroundColor: AppColors.amber100,
|
|
2563
|
+
borderWidth: 1,
|
|
2564
|
+
borderColor: AppColors.amber200,
|
|
2565
|
+
borderRadius: 10,
|
|
2566
|
+
padding: 12,
|
|
2567
|
+
marginBottom: 12,
|
|
2568
|
+
},
|
|
2569
|
+
liveWarningTitle: {
|
|
2570
|
+
fontFamily: AppFonts.interBold,
|
|
2571
|
+
fontSize: 12,
|
|
2572
|
+
color: AppColors.amber800Warm,
|
|
2573
|
+
marginBottom: 2,
|
|
2574
|
+
},
|
|
2575
|
+
liveWarningDesc: {
|
|
2576
|
+
fontFamily: AppFonts.interRegular,
|
|
2577
|
+
fontSize: 10.5,
|
|
2578
|
+
lineHeight: 14,
|
|
2579
|
+
color: AppColors.amber800Warm,
|
|
2580
|
+
},
|
|
2198
2581
|
adviceBadge: {
|
|
2199
2582
|
paddingHorizontal: 8,
|
|
2200
2583
|
paddingVertical: 4,
|
|
@@ -2245,27 +2628,80 @@ const bundleStyles = StyleSheet.create({
|
|
|
2245
2628
|
},
|
|
2246
2629
|
totalSummaryCard: {
|
|
2247
2630
|
marginTop: 10,
|
|
2248
|
-
backgroundColor: `${AppColors.
|
|
2249
|
-
borderRadius:
|
|
2631
|
+
backgroundColor: `${AppColors.brandPurple}0B`,
|
|
2632
|
+
borderRadius: 12,
|
|
2250
2633
|
padding: 12,
|
|
2251
2634
|
borderWidth: 1,
|
|
2252
|
-
borderColor: `${AppColors.
|
|
2253
|
-
gap:
|
|
2635
|
+
borderColor: `${AppColors.brandPurple}25`,
|
|
2636
|
+
gap: 8,
|
|
2254
2637
|
},
|
|
2255
2638
|
totalSummaryRow: {
|
|
2256
2639
|
flexDirection: 'row',
|
|
2257
2640
|
justifyContent: 'space-between',
|
|
2258
2641
|
alignItems: 'center',
|
|
2259
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
|
+
},
|
|
2260
2656
|
totalSummaryLabel: {
|
|
2261
2657
|
fontFamily: AppFonts.interBold,
|
|
2262
|
-
fontSize: 11,
|
|
2263
|
-
color: AppColors.
|
|
2264
|
-
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,
|
|
2265
2667
|
},
|
|
2266
2668
|
totalSummaryValue: {
|
|
2267
2669
|
fontFamily: AppFonts.interBold,
|
|
2268
|
-
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,
|
|
2269
2705
|
color: AppColors.primaryBlack,
|
|
2270
2706
|
},
|
|
2271
2707
|
totalSummaryFormula: {
|
|
@@ -2300,24 +2736,45 @@ const bundleStyles = StyleSheet.create({
|
|
|
2300
2736
|
color: AppColors.purple,
|
|
2301
2737
|
},
|
|
2302
2738
|
tipItem: {
|
|
2303
|
-
marginBottom:
|
|
2739
|
+
marginBottom: 10,
|
|
2304
2740
|
backgroundColor: AppColors.white,
|
|
2305
|
-
padding:
|
|
2306
|
-
borderRadius:
|
|
2741
|
+
padding: 12,
|
|
2742
|
+
borderRadius: 10,
|
|
2307
2743
|
borderWidth: 1,
|
|
2308
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,
|
|
2309
2765
|
},
|
|
2310
2766
|
tipTitle: {
|
|
2311
2767
|
fontFamily: AppFonts.interBold,
|
|
2312
2768
|
fontSize: 12,
|
|
2313
2769
|
color: AppColors.purple900,
|
|
2770
|
+
flex: 1,
|
|
2771
|
+
flexShrink: 1,
|
|
2314
2772
|
},
|
|
2315
2773
|
tipDesc: {
|
|
2316
2774
|
fontFamily: AppFonts.interRegular,
|
|
2317
2775
|
fontSize: 11,
|
|
2318
2776
|
color: AppColors.gray500,
|
|
2319
2777
|
lineHeight: 16,
|
|
2320
|
-
marginTop: 4,
|
|
2321
2778
|
},
|
|
2322
2779
|
});
|
|
2323
2780
|
export default BundleTab;
|