react-native-inapp-inspector 1.1.21 → 1.1.22
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/AnalyticsDetail.js +5 -3
- package/dist/commonjs/components/AnalyticsEventCard.d.ts +0 -1
- package/dist/commonjs/components/AnalyticsEventCard.js +85 -96
- package/dist/commonjs/components/Inspector/BundleTab.js +486 -651
- package/dist/commonjs/components/Inspector/PerformanceTab.js +185 -244
- package/dist/commonjs/components/Inspector/ReduxDetail.js +2 -2
- package/dist/commonjs/components/Inspector/ReduxTab.js +3 -3
- package/dist/commonjs/constants/version.d.ts +1 -1
- package/dist/commonjs/constants/version.js +1 -1
- package/dist/commonjs/customHooks/bundleAnalyzer.d.ts +115 -0
- package/dist/commonjs/customHooks/bundleAnalyzer.js +561 -0
- package/dist/commonjs/customHooks/performanceTracker.d.ts +84 -0
- package/dist/commonjs/customHooks/performanceTracker.js +541 -0
- package/dist/commonjs/helpers/index.d.ts +15 -0
- package/dist/commonjs/helpers/index.js +112 -1
- package/dist/commonjs/i18n/locales/en.json +225 -2
- package/dist/commonjs/styles/AppColors.d.ts +110 -0
- package/dist/commonjs/styles/AppColors.js +114 -0
- package/dist/esm/components/AnalyticsDetail.js +5 -3
- package/dist/esm/components/AnalyticsEventCard.d.ts +0 -1
- package/dist/esm/components/AnalyticsEventCard.js +85 -95
- package/dist/esm/components/Inspector/BundleTab.js +489 -654
- package/dist/esm/components/Inspector/PerformanceTab.js +185 -244
- package/dist/esm/components/Inspector/ReduxDetail.js +2 -2
- package/dist/esm/components/Inspector/ReduxTab.js +3 -3
- package/dist/esm/constants/version.d.ts +1 -1
- package/dist/esm/constants/version.js +1 -1
- package/dist/esm/customHooks/bundleAnalyzer.d.ts +115 -0
- package/dist/esm/customHooks/bundleAnalyzer.js +554 -0
- package/dist/esm/customHooks/performanceTracker.d.ts +84 -0
- package/dist/esm/customHooks/performanceTracker.js +537 -0
- package/dist/esm/helpers/index.d.ts +15 -0
- package/dist/esm/helpers/index.js +107 -0
- package/dist/esm/i18n/locales/en.json +225 -2
- package/dist/esm/styles/AppColors.d.ts +110 -0
- package/dist/esm/styles/AppColors.js +114 -0
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import React, { useState, useMemo, useCallback } from 'react';
|
|
2
|
-
import { Image, Linking, Pressable, ScrollView, StyleSheet, Text, TextInput, View, } from 'react-native';
|
|
3
|
-
import { useTranslation } from '../../i18n';
|
|
1
|
+
import React, { useState, useEffect, useMemo, useCallback } from 'react';
|
|
2
|
+
import { ActivityIndicator, Image, Linking, Pressable, ScrollView, StyleSheet, Text, TextInput, View, } from 'react-native';
|
|
3
|
+
import { useTranslation, t } from '../../i18n';
|
|
4
4
|
import TouchableScale from '../TouchableScale';
|
|
5
5
|
import CopyButton from '../CopyButton';
|
|
6
6
|
import HighlightText from '../HighlightText';
|
|
@@ -8,6 +8,7 @@ import { AppColors } from '../../styles/AppColors';
|
|
|
8
8
|
import { AppFonts } from '../../styles/AppFonts';
|
|
9
9
|
import { PackageIcon, SearchIcon, ClearIcon, CircleAlertIcon, LayersIcon, LiveStateIcon, StorageIcon, MetadataIcon, } from '../NetworkIcons';
|
|
10
10
|
import Svg, { Path, Rect, Circle, Ellipse, Defs, LinearGradient, Stop, } from 'react-native-svg';
|
|
11
|
+
import { analyzeHostAppBundle, getCachedBundleAnalysis, } from '../../customHooks/bundleAnalyzer';
|
|
11
12
|
// ─── High-Fidelity Vector Package & NPM Logos ────────────────────────────────
|
|
12
13
|
const PackageLogoRenderer = ({ name, size = 28 }) => {
|
|
13
14
|
const lowerName = name.toLowerCase();
|
|
@@ -19,13 +20,13 @@ const PackageLogoRenderer = ({ name, size = 28 }) => {
|
|
|
19
20
|
!lowerName.includes('gradient'))) {
|
|
20
21
|
return (<View style={[
|
|
21
22
|
bundleStyles.pkgLogoWrap,
|
|
22
|
-
{ width: size, height: size, backgroundColor:
|
|
23
|
+
{ width: size, height: size, backgroundColor: AppColors.npmDark },
|
|
23
24
|
]}>
|
|
24
25
|
<Svg width={size - 6} height={size - 6} viewBox="0 0 100 100" fill="none">
|
|
25
|
-
<Circle cx="50" cy="50" r="9" fill=
|
|
26
|
-
<Ellipse cx="50" cy="50" rx="42" ry="16" stroke=
|
|
27
|
-
<Ellipse cx="50" cy="50" rx="42" ry="16" stroke=
|
|
28
|
-
<Ellipse cx="50" cy="50" rx="42" ry="16" stroke=
|
|
26
|
+
<Circle cx="50" cy="50" r="9" fill={AppColors.reactCyan}/>
|
|
27
|
+
<Ellipse cx="50" cy="50" rx="42" ry="16" stroke={AppColors.reactCyan} strokeWidth="6"/>
|
|
28
|
+
<Ellipse cx="50" cy="50" rx="42" ry="16" stroke={AppColors.reactCyan} strokeWidth="6" transform="rotate(60 50 50)"/>
|
|
29
|
+
<Ellipse cx="50" cy="50" rx="42" ry="16" stroke={AppColors.reactCyan} strokeWidth="6" transform="rotate(120 50 50)"/>
|
|
29
30
|
</Svg>
|
|
30
31
|
</View>);
|
|
31
32
|
}
|
|
@@ -33,12 +34,12 @@ const PackageLogoRenderer = ({ name, size = 28 }) => {
|
|
|
33
34
|
if (lowerName.includes('navigation')) {
|
|
34
35
|
return (<View style={[
|
|
35
36
|
bundleStyles.pkgLogoWrap,
|
|
36
|
-
{ width: size, height: size, backgroundColor:
|
|
37
|
+
{ width: size, height: size, backgroundColor: AppColors.hermesPurple },
|
|
37
38
|
]}>
|
|
38
39
|
<Svg width={size - 8} height={size - 8} viewBox="0 0 100 100" fill="none">
|
|
39
|
-
<Circle cx="50" cy="50" r="38" stroke=
|
|
40
|
-
<Path d="M50 20 L68 50 L50 80 L32 50 Z" fill=
|
|
41
|
-
<Circle cx="50" cy="50" r="6" fill=
|
|
40
|
+
<Circle cx="50" cy="50" r="38" stroke={AppColors.white} strokeWidth="8"/>
|
|
41
|
+
<Path d="M50 20 L68 50 L50 80 L32 50 Z" fill={AppColors.reactCyan}/>
|
|
42
|
+
<Circle cx="50" cy="50" r="6" fill={AppColors.white}/>
|
|
42
43
|
</Svg>
|
|
43
44
|
</View>);
|
|
44
45
|
}
|
|
@@ -46,13 +47,13 @@ const PackageLogoRenderer = ({ name, size = 28 }) => {
|
|
|
46
47
|
if (lowerName.includes('svg')) {
|
|
47
48
|
return (<View style={[
|
|
48
49
|
bundleStyles.pkgLogoWrap,
|
|
49
|
-
{ width: size, height: size, backgroundColor:
|
|
50
|
+
{ width: size, height: size, backgroundColor: AppColors.pink400 },
|
|
50
51
|
]}>
|
|
51
52
|
<Svg width={size - 8} height={size - 8} viewBox="0 0 100 100" fill="none">
|
|
52
|
-
<Path d="M20 75 C35 25, 65 25, 80 75" stroke=
|
|
53
|
-
<Circle cx="20" cy="75" r="8" fill=
|
|
54
|
-
<Circle cx="80" cy="75" r="8" fill=
|
|
55
|
-
<Circle cx="50" cy="38" r="7" fill="#FCE7F3" stroke=
|
|
53
|
+
<Path d="M20 75 C35 25, 65 25, 80 75" stroke={AppColors.white} strokeWidth="10" strokeLinecap="round"/>
|
|
54
|
+
<Circle cx="20" cy="75" r="8" fill={AppColors.white}/>
|
|
55
|
+
<Circle cx="80" cy="75" r="8" fill={AppColors.white}/>
|
|
56
|
+
<Circle cx="50" cy="38" r="7" fill="#FCE7F3" stroke={AppColors.white} strokeWidth="3"/>
|
|
56
57
|
</Svg>
|
|
57
58
|
</View>);
|
|
58
59
|
}
|
|
@@ -60,11 +61,11 @@ const PackageLogoRenderer = ({ name, size = 28 }) => {
|
|
|
60
61
|
if (lowerName.includes('axios')) {
|
|
61
62
|
return (<View style={[
|
|
62
63
|
bundleStyles.pkgLogoWrap,
|
|
63
|
-
{ width: size, height: size, backgroundColor:
|
|
64
|
+
{ width: size, height: size, backgroundColor: AppColors.expoViolet },
|
|
64
65
|
]}>
|
|
65
66
|
<Svg width={size - 6} height={size - 6} viewBox="0 0 100 100" fill="none">
|
|
66
|
-
<Path d="M22 75 L50 20 L78 75 M33 58 L67 58" stroke=
|
|
67
|
-
<Circle cx="50" cy="20" r="7" fill=
|
|
67
|
+
<Path d="M22 75 L50 20 L78 75 M33 58 L67 58" stroke={AppColors.white} strokeWidth="12" strokeLinecap="round" strokeLinejoin="round"/>
|
|
68
|
+
<Circle cx="50" cy="20" r="7" fill={AppColors.reactCyan}/>
|
|
68
69
|
</Svg>
|
|
69
70
|
</View>);
|
|
70
71
|
}
|
|
@@ -72,11 +73,11 @@ const PackageLogoRenderer = ({ name, size = 28 }) => {
|
|
|
72
73
|
if (lowerName.includes('screens') || lowerName.includes('software-mansion')) {
|
|
73
74
|
return (<View style={[
|
|
74
75
|
bundleStyles.pkgLogoWrap,
|
|
75
|
-
{ width: size, height: size, backgroundColor:
|
|
76
|
+
{ width: size, height: size, backgroundColor: AppColors.reanimatedNavy },
|
|
76
77
|
]}>
|
|
77
78
|
<Svg width={size - 8} height={size - 8} viewBox="0 0 100 100" fill="none">
|
|
78
|
-
<Path d="M50 15 L88 35 L50 55 L12 35 Z" fill=
|
|
79
|
-
<Path d="M12 50 L50 70 L88 50" stroke=
|
|
79
|
+
<Path d="M50 15 L88 35 L50 55 L12 35 Z" fill={AppColors.sky400}/>
|
|
80
|
+
<Path d="M12 50 L50 70 L88 50" stroke={AppColors.white} strokeWidth="8" strokeLinecap="round"/>
|
|
80
81
|
<Path d="M12 65 L50 85 L88 65" stroke="#93C5FD" strokeWidth="8" strokeLinecap="round"/>
|
|
81
82
|
</Svg>
|
|
82
83
|
</View>);
|
|
@@ -85,7 +86,7 @@ const PackageLogoRenderer = ({ name, size = 28 }) => {
|
|
|
85
86
|
if (lowerName.includes('gradient')) {
|
|
86
87
|
return (<View style={[
|
|
87
88
|
bundleStyles.pkgLogoWrap,
|
|
88
|
-
{ width: size, height: size, backgroundColor:
|
|
89
|
+
{ width: size, height: size, backgroundColor: AppColors.rose600 },
|
|
89
90
|
]}>
|
|
90
91
|
<Svg width={size - 4} height={size - 4} viewBox="0 0 100 100" fill="none">
|
|
91
92
|
<Defs>
|
|
@@ -103,23 +104,23 @@ const PackageLogoRenderer = ({ name, size = 28 }) => {
|
|
|
103
104
|
if (lowerName.includes('i18n')) {
|
|
104
105
|
return (<View style={[
|
|
105
106
|
bundleStyles.pkgLogoWrap,
|
|
106
|
-
{ width: size, height: size, backgroundColor:
|
|
107
|
+
{ width: size, height: size, backgroundColor: AppColors.emerald600 },
|
|
107
108
|
]}>
|
|
108
109
|
<Svg width={size - 8} height={size - 8} viewBox="0 0 100 100" fill="none">
|
|
109
|
-
<Circle cx="50" cy="50" r="38" stroke=
|
|
110
|
-
<Ellipse cx="50" cy="50" rx="18" ry="38" stroke=
|
|
111
|
-
<Path d="M14 50 L86 50" stroke=
|
|
110
|
+
<Circle cx="50" cy="50" r="38" stroke={AppColors.white} strokeWidth="8"/>
|
|
111
|
+
<Ellipse cx="50" cy="50" rx="18" ry="38" stroke={AppColors.white} strokeWidth="6"/>
|
|
112
|
+
<Path d="M14 50 L86 50" stroke={AppColors.white} strokeWidth="7"/>
|
|
112
113
|
</Svg>
|
|
113
114
|
</View>);
|
|
114
115
|
}
|
|
115
116
|
// 8. Official Red NPM Logo Box (Universal Default NPM Branding)
|
|
116
117
|
return (<View style={[
|
|
117
118
|
bundleStyles.pkgLogoWrap,
|
|
118
|
-
{ width: size, height: size, backgroundColor:
|
|
119
|
+
{ width: size, height: size, backgroundColor: AppColors.npmRed },
|
|
119
120
|
]}>
|
|
120
121
|
<Svg width={size - 4} height={size - 4} viewBox="0 0 100 100" fill="none">
|
|
121
|
-
<Rect width="100" height="100" rx="14" fill=
|
|
122
|
-
<Path d="M18 24 H82 V76 H50 V40 H38 V76 H18 Z" fill=
|
|
122
|
+
<Rect width="100" height="100" rx="14" fill={AppColors.npmRed}/>
|
|
123
|
+
<Path d="M18 24 H82 V76 H50 V40 H38 V76 H18 Z" fill={AppColors.white}/>
|
|
123
124
|
</Svg>
|
|
124
125
|
</View>);
|
|
125
126
|
};
|
|
@@ -131,25 +132,25 @@ const MediaPreviewThumbnail = ({ file }) => {
|
|
|
131
132
|
return (<View style={[
|
|
132
133
|
bundleStyles.mediaThumbWrap,
|
|
133
134
|
{
|
|
134
|
-
backgroundColor:
|
|
135
|
+
backgroundColor: AppColors.errorCardBg,
|
|
135
136
|
alignItems: 'center',
|
|
136
137
|
justifyContent: 'center',
|
|
137
|
-
borderColor:
|
|
138
|
+
borderColor: AppColors.errorBorder,
|
|
138
139
|
},
|
|
139
140
|
]}>
|
|
140
141
|
<Svg width={22} height={22} viewBox="0 0 100 100" fill="none">
|
|
141
142
|
<Defs>
|
|
142
143
|
<LinearGradient id="svgGradThumb" x1="0" y1="0" x2="100" y2="100" gradientUnits="userSpaceOnUse">
|
|
143
|
-
<Stop offset="0" stopColor=
|
|
144
|
-
<Stop offset="1" stopColor=
|
|
144
|
+
<Stop offset="0" stopColor={AppColors.rose500}/>
|
|
145
|
+
<Stop offset="1" stopColor={AppColors.rose600}/>
|
|
145
146
|
</LinearGradient>
|
|
146
147
|
</Defs>
|
|
147
148
|
<Circle cx="50" cy="50" r="44" fill="url(#svgGradThumb)"/>
|
|
148
149
|
{/* Vector bezier path pen graphic */}
|
|
149
|
-
<Path d="M25 70 C35 30, 65 30, 75 70" stroke=
|
|
150
|
-
<Circle cx="25" cy="70" r="7" fill=
|
|
151
|
-
<Circle cx="75" cy="70" r="7" fill=
|
|
152
|
-
<Circle cx="50" cy="40" r="6" fill="#FFE4E6" stroke=
|
|
150
|
+
<Path d="M25 70 C35 30, 65 30, 75 70" stroke={AppColors.white} strokeWidth="8" strokeLinecap="round"/>
|
|
151
|
+
<Circle cx="25" cy="70" r="7" fill={AppColors.white}/>
|
|
152
|
+
<Circle cx="75" cy="70" r="7" fill={AppColors.white}/>
|
|
153
|
+
<Circle cx="50" cy="40" r="6" fill="#FFE4E6" stroke={AppColors.white} strokeWidth="2"/>
|
|
153
154
|
</Svg>
|
|
154
155
|
</View>);
|
|
155
156
|
}
|
|
@@ -247,7 +248,7 @@ const BundleTreeNodeView = ({ node, level, search, totalBundleKb, collapsedFolde
|
|
|
247
248
|
<HighlightText text={node.name + '/'} search={search} style={bundleStyles.treeFolderName} highlightStyle={bundleStyles.searchHighlight}/>
|
|
248
249
|
<View style={bundleStyles.treeCountBadge}>
|
|
249
250
|
<Text style={bundleStyles.treeCountBadgeText}>
|
|
250
|
-
{node.fileCount} {node.fileCount === 1 ? 'file' : 'files'}
|
|
251
|
+
{node.fileCount} {node.fileCount === 1 ? t('bundle.file') : t('bundle.files')}
|
|
251
252
|
</Text>
|
|
252
253
|
</View>
|
|
253
254
|
</View>
|
|
@@ -255,14 +256,14 @@ const BundleTreeNodeView = ({ node, level, search, totalBundleKb, collapsedFolde
|
|
|
255
256
|
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
|
|
256
257
|
<Text style={bundleStyles.treeFolderSize}>
|
|
257
258
|
{node.sizeKb >= 1024
|
|
258
|
-
?
|
|
259
|
-
:
|
|
259
|
+
? t('bundle.fileSizeMb', { size: (node.sizeKb / 1024).toFixed(2) })
|
|
260
|
+
: t('bundle.fileSizeKb', { size: node.sizeKb })}
|
|
260
261
|
</Text>
|
|
261
262
|
<CopyButton value={() => ({
|
|
262
263
|
folder: node.fullPath,
|
|
263
264
|
sizeKb: node.sizeKb,
|
|
264
265
|
fileCount: node.fileCount,
|
|
265
|
-
})} label={
|
|
266
|
+
})} label={t('bundle.copyFileInfo', { name: node.name })}/>
|
|
266
267
|
</View>
|
|
267
268
|
</TouchableScale>
|
|
268
269
|
|
|
@@ -291,9 +292,9 @@ const BundleTreeNodeView = ({ node, level, search, totalBundleKb, collapsedFolde
|
|
|
291
292
|
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 6, flexWrap: 'wrap' }}>
|
|
292
293
|
<HighlightText text={file.name} search={search} style={bundleStyles.fileName} highlightStyle={bundleStyles.searchHighlight}/>
|
|
293
294
|
{isUnused ? (<View style={bundleStyles.unusedBadge}>
|
|
294
|
-
<Text style={bundleStyles.unusedBadgeText}
|
|
295
|
+
<Text style={bundleStyles.unusedBadgeText}>{t('bundle.notConsumed')}</Text>
|
|
295
296
|
</View>) : (<View style={bundleStyles.consumedBadge}>
|
|
296
|
-
<Text style={bundleStyles.consumedBadgeText}
|
|
297
|
+
<Text style={bundleStyles.consumedBadgeText}>{t('bundle.consumed')}</Text>
|
|
297
298
|
</View>)}
|
|
298
299
|
</View>
|
|
299
300
|
<Text style={bundleStyles.filePath} numberOfLines={1}>
|
|
@@ -303,14 +304,14 @@ const BundleTreeNodeView = ({ node, level, search, totalBundleKb, collapsedFolde
|
|
|
303
304
|
|
|
304
305
|
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
|
|
305
306
|
<View style={bundleStyles.fileSizeBox}>
|
|
306
|
-
<Text style={[bundleStyles.fileSizeKb, isUnused && { color:
|
|
307
|
+
<Text style={[bundleStyles.fileSizeKb, isUnused && { color: AppColors.amber800Warm }]}>
|
|
307
308
|
{file.sizeKb >= 1024
|
|
308
|
-
?
|
|
309
|
-
:
|
|
309
|
+
? t('bundle.fileSizeMb', { size: (file.sizeKb / 1024).toFixed(2) })
|
|
310
|
+
: t('bundle.fileSizeKb', { size: file.sizeKb })}
|
|
310
311
|
</Text>
|
|
311
312
|
<Text style={bundleStyles.filePercent}>{pctOfTotal}%</Text>
|
|
312
313
|
</View>
|
|
313
|
-
<CopyButton value={() => file} label=
|
|
314
|
+
<CopyButton value={() => file} label={t('bundle.fileDetailsJson')}/>
|
|
314
315
|
</View>
|
|
315
316
|
</View>
|
|
316
317
|
|
|
@@ -323,9 +324,9 @@ const BundleTreeNodeView = ({ node, level, search, totalBundleKb, collapsedFolde
|
|
|
323
324
|
]}>
|
|
324
325
|
<Text style={[
|
|
325
326
|
bundleStyles.adviceText,
|
|
326
|
-
file.status === 'warning' && { color:
|
|
327
|
-
file.status === 'optimal' && { color:
|
|
328
|
-
isUnused && { color:
|
|
327
|
+
file.status === 'warning' && { color: AppColors.amber800Warm },
|
|
328
|
+
file.status === 'optimal' && { color: AppColors.emerald700 },
|
|
329
|
+
isUnused && { color: AppColors.red600 },
|
|
329
330
|
]}>
|
|
330
331
|
{isUnused ? '🗑️ ' : file.status === 'warning' ? '💡 ' : '✨ '}
|
|
331
332
|
{file.advice}
|
|
@@ -333,417 +334,48 @@ const BundleTreeNodeView = ({ node, level, search, totalBundleKb, collapsedFolde
|
|
|
333
334
|
</View>)}
|
|
334
335
|
</View>);
|
|
335
336
|
};
|
|
336
|
-
// ─── Sample Bundle Files Data ────────────────────────────────────────────────
|
|
337
|
-
const BUNDLE_FILES_DATA = [
|
|
338
|
-
// ── Images & Media ──
|
|
339
|
-
{
|
|
340
|
-
id: 'img-1',
|
|
341
|
-
name: 'banner_dark.png',
|
|
342
|
-
path: 'assets/images/banner_dark.png',
|
|
343
|
-
ext: 'PNG',
|
|
344
|
-
category: 'image',
|
|
345
|
-
sizeKb: 460,
|
|
346
|
-
meta: '1200×630px @2x • Raster Image',
|
|
347
|
-
color: '#EC4899',
|
|
348
|
-
status: 'warning',
|
|
349
|
-
advice: 'Convert to .webp to save ~65% (160 KB)',
|
|
350
|
-
previewUri: 'https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?w=200&auto=format&fit=crop&q=80',
|
|
351
|
-
},
|
|
352
|
-
{
|
|
353
|
-
id: 'img-2',
|
|
354
|
-
name: 'banner_light.svg',
|
|
355
|
-
path: 'assets/images/banner_light.svg',
|
|
356
|
-
ext: 'SVG',
|
|
357
|
-
category: 'image',
|
|
358
|
-
sizeKb: 34,
|
|
359
|
-
meta: 'Scalable Vector Graphic',
|
|
360
|
-
color: '#F43F5E',
|
|
361
|
-
status: 'optimal',
|
|
362
|
-
advice: 'Optimal vector format for sharp rendering',
|
|
363
|
-
previewUri: 'https://images.unsplash.com/photo-1579783902614-a3fb3927b675?w=200&auto=format&fit=crop&q=80',
|
|
364
|
-
},
|
|
365
|
-
{
|
|
366
|
-
id: 'img-3',
|
|
367
|
-
name: 'logo_hero@3x.png',
|
|
368
|
-
path: 'assets/brand/logo_hero@3x.png',
|
|
369
|
-
ext: 'PNG',
|
|
370
|
-
category: 'image',
|
|
371
|
-
sizeKb: 285,
|
|
372
|
-
meta: '768×768px @3x Retina Asset',
|
|
373
|
-
color: '#EC4899',
|
|
374
|
-
status: 'warning',
|
|
375
|
-
advice: 'High density PNG. Consider tintable SVG vector',
|
|
376
|
-
previewUri: 'https://images.unsplash.com/photo-1634017839464-5c339ebe3cb4?w=200&auto=format&fit=crop&q=80',
|
|
377
|
-
},
|
|
378
|
-
{
|
|
379
|
-
id: 'img-4',
|
|
380
|
-
name: 'avatar_placeholder.webp',
|
|
381
|
-
path: 'assets/placeholders/avatar_placeholder.webp',
|
|
382
|
-
ext: 'WEBP',
|
|
383
|
-
category: 'image',
|
|
384
|
-
sizeKb: 28,
|
|
385
|
-
meta: '256×256px Lossy WebP',
|
|
386
|
-
color: '#F472B6',
|
|
387
|
-
status: 'warning',
|
|
388
|
-
advice: 'Unused in bundle: 0 active imports detected (28 KB unused)',
|
|
389
|
-
previewUri: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=200&auto=format&fit=crop&q=80',
|
|
390
|
-
isConsumed: false,
|
|
391
|
-
},
|
|
392
|
-
{
|
|
393
|
-
id: 'img-5',
|
|
394
|
-
name: 'walkthrough_thumb.jpg',
|
|
395
|
-
path: 'assets/previews/walkthrough_thumb.jpg',
|
|
396
|
-
ext: 'JPG',
|
|
397
|
-
category: 'image',
|
|
398
|
-
sizeKb: 175,
|
|
399
|
-
meta: '1080×600px Progressive JPEG',
|
|
400
|
-
color: '#FB7185',
|
|
401
|
-
status: 'warning',
|
|
402
|
-
advice: 'Dead asset: 0 component references (175 KB unused)',
|
|
403
|
-
previewUri: 'https://images.unsplash.com/photo-1550745165-9bc0b252726f?w=200&auto=format&fit=crop&q=80',
|
|
404
|
-
isConsumed: false,
|
|
405
|
-
},
|
|
406
|
-
// ── TypeScript & JSX ──
|
|
407
|
-
{
|
|
408
|
-
id: 'ts-1',
|
|
409
|
-
name: 'LogDetail.tsx',
|
|
410
|
-
path: 'src/components/Inspector/LogDetail.tsx',
|
|
411
|
-
ext: 'TSX',
|
|
412
|
-
category: 'typescript',
|
|
413
|
-
sizeKb: 44,
|
|
414
|
-
meta: '1,238 lines • React Component',
|
|
415
|
-
color: '#38BDF8',
|
|
416
|
-
status: 'optimal',
|
|
417
|
-
advice: 'Clean React.memo and useCallback optimization',
|
|
418
|
-
},
|
|
419
|
-
{
|
|
420
|
-
id: 'ts-2',
|
|
421
|
-
name: 'ReduxDetail.tsx',
|
|
422
|
-
path: 'src/components/Inspector/ReduxDetail.tsx',
|
|
423
|
-
ext: 'TSX',
|
|
424
|
-
category: 'typescript',
|
|
425
|
-
sizeKb: 38,
|
|
426
|
-
meta: '680 lines • Slice state tree & action timeline',
|
|
427
|
-
color: '#38BDF8',
|
|
428
|
-
status: 'optimal',
|
|
429
|
-
advice: 'Multi-tab subview with live state diffing',
|
|
430
|
-
},
|
|
431
|
-
{
|
|
432
|
-
id: 'ts-3',
|
|
433
|
-
name: 'NetworkTab.tsx',
|
|
434
|
-
path: 'src/components/Inspector/NetworkTab.tsx',
|
|
435
|
-
ext: 'TSX',
|
|
436
|
-
category: 'typescript',
|
|
437
|
-
sizeKb: 15,
|
|
438
|
-
meta: '486 lines • Network request virtualization list',
|
|
439
|
-
color: '#0EA5E9',
|
|
440
|
-
status: 'optimal',
|
|
441
|
-
advice: 'FlatList windowSize=7 tuned for 60fps',
|
|
442
|
-
},
|
|
443
|
-
{
|
|
444
|
-
id: 'ts-4',
|
|
445
|
-
name: 'SettingsPanel.tsx',
|
|
446
|
-
path: 'src/components/Inspector/SettingsPanel.tsx',
|
|
447
|
-
ext: 'TSX',
|
|
448
|
-
category: 'typescript',
|
|
449
|
-
sizeKb: 65,
|
|
450
|
-
meta: '1,845 lines • Preferences & storage manager',
|
|
451
|
-
color: '#38BDF8',
|
|
452
|
-
status: 'info',
|
|
453
|
-
advice: 'Self-contained settings subviews',
|
|
454
|
-
},
|
|
455
|
-
{
|
|
456
|
-
id: 'ts-5',
|
|
457
|
-
name: 'consoleLogger.ts',
|
|
458
|
-
path: 'src/customHooks/consoleLogger.ts',
|
|
459
|
-
ext: 'TS',
|
|
460
|
-
category: 'typescript',
|
|
461
|
-
sizeKb: 12,
|
|
462
|
-
meta: '360 lines • Metro symbolication bridge',
|
|
463
|
-
color: '#0284C7',
|
|
464
|
-
status: 'optimal',
|
|
465
|
-
advice: 'Zero-overhead in-memory stack capture',
|
|
466
|
-
},
|
|
467
|
-
{
|
|
468
|
-
id: 'ts-6',
|
|
469
|
-
name: 'App.tsx (Playground)',
|
|
470
|
-
path: 'example/App.tsx',
|
|
471
|
-
ext: 'TSX',
|
|
472
|
-
category: 'typescript',
|
|
473
|
-
sizeKb: 25,
|
|
474
|
-
meta: '820 lines • Interactive test scenarios',
|
|
475
|
-
color: '#38BDF8',
|
|
476
|
-
status: 'optimal',
|
|
477
|
-
advice: 'Sample harness for logs, APIs, and Redux',
|
|
478
|
-
},
|
|
479
|
-
// ── JavaScript & Node Modules ──
|
|
480
|
-
{
|
|
481
|
-
id: 'js-1',
|
|
482
|
-
name: 'react-native/Libraries/Renderer',
|
|
483
|
-
path: 'node_modules/react-native/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js',
|
|
484
|
-
ext: 'JS',
|
|
485
|
-
category: 'javascript',
|
|
486
|
-
sizeKb: 480,
|
|
487
|
-
meta: 'Fabric & Paper UI reconciler bundle',
|
|
488
|
-
color: '#6366F1',
|
|
489
|
-
status: 'optimal',
|
|
490
|
-
advice: 'AOT compiled with Hermes Bytecode',
|
|
491
|
-
},
|
|
492
|
-
{
|
|
493
|
-
id: 'js-2',
|
|
494
|
-
name: '@react-navigation/native-stack',
|
|
495
|
-
path: 'node_modules/@react-navigation/native-stack/lib/module/index.js',
|
|
496
|
-
ext: 'JS',
|
|
497
|
-
category: 'javascript',
|
|
498
|
-
sizeKb: 195,
|
|
499
|
-
meta: 'Native screen animation & transition engine',
|
|
500
|
-
color: '#818CF8',
|
|
501
|
-
status: 'optimal',
|
|
502
|
-
advice: 'Optimized modular ES bundle',
|
|
503
|
-
},
|
|
504
|
-
{
|
|
505
|
-
id: 'js-3',
|
|
506
|
-
name: 'axios/dist/esm/axios.js',
|
|
507
|
-
path: 'node_modules/axios/dist/esm/axios.js',
|
|
508
|
-
ext: 'JS',
|
|
509
|
-
category: 'javascript',
|
|
510
|
-
sizeKb: 88,
|
|
511
|
-
meta: 'HTTP client core with interceptors',
|
|
512
|
-
color: '#F59E0B',
|
|
513
|
-
status: 'optimal',
|
|
514
|
-
advice: 'Standard lightweight network adapter',
|
|
515
|
-
},
|
|
516
|
-
{
|
|
517
|
-
id: 'js-4',
|
|
518
|
-
name: 'react-native-svg',
|
|
519
|
-
path: 'node_modules/react-native-svg/lib/module/index.js',
|
|
520
|
-
ext: 'JS',
|
|
521
|
-
category: 'javascript',
|
|
522
|
-
sizeKb: 140,
|
|
523
|
-
meta: 'Native SVG drawing and vector paths',
|
|
524
|
-
color: '#4F46E5',
|
|
525
|
-
status: 'optimal',
|
|
526
|
-
advice: 'Native Fabric/Paper vector bridge',
|
|
527
|
-
},
|
|
528
|
-
// ── Fonts & Typography ──
|
|
529
|
-
{
|
|
530
|
-
id: 'font-1',
|
|
531
|
-
name: 'Inter-Bold.ttf',
|
|
532
|
-
path: 'fonts/Inter-Bold.ttf',
|
|
533
|
-
ext: 'TTF',
|
|
534
|
-
category: 'font',
|
|
535
|
-
sizeKb: 165,
|
|
536
|
-
meta: 'Custom UI Font • Full Latin Charset (480 glyphs)',
|
|
537
|
-
color: '#8B5CF6',
|
|
538
|
-
status: 'info',
|
|
539
|
-
advice: 'High readability modern typography',
|
|
540
|
-
},
|
|
541
|
-
{
|
|
542
|
-
id: 'font-2',
|
|
543
|
-
name: 'Inter-Regular.ttf',
|
|
544
|
-
path: 'fonts/Inter-Regular.ttf',
|
|
545
|
-
ext: 'TTF',
|
|
546
|
-
category: 'font',
|
|
547
|
-
sizeKb: 160,
|
|
548
|
-
meta: 'Custom Body Font • 400 Weight (480 glyphs)',
|
|
549
|
-
color: '#8B5CF6',
|
|
550
|
-
status: 'info',
|
|
551
|
-
advice: 'Applied across inspector UI cards and text',
|
|
552
|
-
},
|
|
553
|
-
{
|
|
554
|
-
id: 'font-3',
|
|
555
|
-
name: 'JetBrainsMono-Regular.ttf',
|
|
556
|
-
path: 'fonts/JetBrainsMono-Regular.ttf',
|
|
557
|
-
ext: 'TTF',
|
|
558
|
-
category: 'font',
|
|
559
|
-
sizeKb: 95,
|
|
560
|
-
meta: 'Monospace Code Font • Raw / Table / Stack Traces',
|
|
561
|
-
color: '#A855F7',
|
|
562
|
-
status: 'optimal',
|
|
563
|
-
advice: 'Crisp font for code snippets and stack traces',
|
|
564
|
-
},
|
|
565
|
-
// ── JSON & Localizations ──
|
|
566
|
-
{
|
|
567
|
-
id: 'json-1',
|
|
568
|
-
name: 'en.json (i18n Translations)',
|
|
569
|
-
path: 'src/i18n/locales/en.json',
|
|
570
|
-
ext: 'JSON',
|
|
571
|
-
category: 'json',
|
|
572
|
-
sizeKb: 18,
|
|
573
|
-
meta: '124 localization translation keys',
|
|
574
|
-
color: '#10B981',
|
|
575
|
-
status: 'optimal',
|
|
576
|
-
advice: 'Static JSON locale dictionary',
|
|
577
|
-
},
|
|
578
|
-
{
|
|
579
|
-
id: 'json-2',
|
|
580
|
-
name: 'package.json',
|
|
581
|
-
path: 'package.json',
|
|
582
|
-
ext: 'JSON',
|
|
583
|
-
category: 'json',
|
|
584
|
-
sizeKb: 3,
|
|
585
|
-
meta: 'Manifest & peer dependencies config',
|
|
586
|
-
color: '#059669',
|
|
587
|
-
status: 'optimal',
|
|
588
|
-
advice: 'Stripped during production bundle export',
|
|
589
|
-
},
|
|
590
|
-
];
|
|
591
|
-
// ─── Packages / Node Modules Breakdown Data ──────────────────────────────────
|
|
592
|
-
const BUNDLE_PACKAGES_DATA = [
|
|
593
|
-
{
|
|
594
|
-
id: 'pkg-1',
|
|
595
|
-
name: 'react-native',
|
|
596
|
-
version: '0.85.3',
|
|
597
|
-
latestVersion: '0.85.3',
|
|
598
|
-
sizeKb: 680,
|
|
599
|
-
percentage: 38.5,
|
|
600
|
-
type: 'direct',
|
|
601
|
-
category: 'core',
|
|
602
|
-
color: '#38BDF8',
|
|
603
|
-
description: 'React Native runtime, Fabric UI engine, Native Bridge, and Turbomodules.',
|
|
604
|
-
logoUri: 'https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/React-icon.svg/512px-React-icon.svg.png',
|
|
605
|
-
lastActive: '3 days ago',
|
|
606
|
-
npmUrl: 'https://www.npmjs.com/package/react-native',
|
|
607
|
-
isDeprecated: false,
|
|
608
|
-
},
|
|
609
|
-
{
|
|
610
|
-
id: 'pkg-2',
|
|
611
|
-
name: '@react-navigation/native-stack',
|
|
612
|
-
version: '^7.16.0',
|
|
613
|
-
latestVersion: '7.16.0',
|
|
614
|
-
sizeKb: 245,
|
|
615
|
-
percentage: 13.9,
|
|
616
|
-
type: 'direct',
|
|
617
|
-
category: 'navigation',
|
|
618
|
-
color: '#818CF8',
|
|
619
|
-
description: 'Native screen router, UIViewController / Fragment animations and transitions.',
|
|
620
|
-
logoUri: 'https://reactnavigation.org/img/spiro.svg',
|
|
621
|
-
lastActive: '1 week ago',
|
|
622
|
-
npmUrl: 'https://www.npmjs.com/package/@react-navigation/native-stack',
|
|
623
|
-
isDeprecated: false,
|
|
624
|
-
},
|
|
625
|
-
{
|
|
626
|
-
id: 'pkg-3',
|
|
627
|
-
name: 'react',
|
|
628
|
-
version: '19.2.3',
|
|
629
|
-
latestVersion: '19.2.3',
|
|
630
|
-
sizeKb: 185,
|
|
631
|
-
percentage: 10.5,
|
|
632
|
-
type: 'direct',
|
|
633
|
-
category: 'core',
|
|
634
|
-
color: '#06B6D4',
|
|
635
|
-
description: 'React core virtual DOM reconciler, hooks engine, and JSX runtime.',
|
|
636
|
-
logoUri: 'https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/React-icon.svg/512px-React-icon.svg.png',
|
|
637
|
-
lastActive: '5 days ago',
|
|
638
|
-
npmUrl: 'https://www.npmjs.com/package/react',
|
|
639
|
-
isDeprecated: false,
|
|
640
|
-
},
|
|
641
|
-
{
|
|
642
|
-
id: 'pkg-4',
|
|
643
|
-
name: 'react-native-svg',
|
|
644
|
-
version: '^15.15.5',
|
|
645
|
-
latestVersion: '15.16.0',
|
|
646
|
-
sizeKb: 140,
|
|
647
|
-
percentage: 7.9,
|
|
648
|
-
type: 'direct',
|
|
649
|
-
category: 'ui',
|
|
650
|
-
color: '#F472B6',
|
|
651
|
-
description: 'Scalable Vector Graphics XML parser and native canvas path rasterizer.',
|
|
652
|
-
logoUri: 'https://upload.wikimedia.org/wikipedia/commons/thumb/0/02/SVG_logo.svg/512px-SVG_logo.svg.png',
|
|
653
|
-
lastActive: '2 days ago',
|
|
654
|
-
npmUrl: 'https://www.npmjs.com/package/react-native-svg',
|
|
655
|
-
isDeprecated: false,
|
|
656
|
-
},
|
|
657
|
-
{
|
|
658
|
-
id: 'pkg-5',
|
|
659
|
-
name: 'react-native-screens',
|
|
660
|
-
version: '^4.25.2',
|
|
661
|
-
latestVersion: '4.25.2',
|
|
662
|
-
sizeKb: 110,
|
|
663
|
-
percentage: 6.2,
|
|
664
|
-
type: 'transitive',
|
|
665
|
-
category: 'navigation',
|
|
666
|
-
color: '#A78BFA',
|
|
667
|
-
description: 'Native navigation container primitives for iOS and Android memory management.',
|
|
668
|
-
logoUri: 'https://reactnavigation.org/img/spiro.svg',
|
|
669
|
-
lastActive: '2 weeks ago',
|
|
670
|
-
npmUrl: 'https://www.npmjs.com/package/react-native-screens',
|
|
671
|
-
isDeprecated: false,
|
|
672
|
-
},
|
|
673
|
-
{
|
|
674
|
-
id: 'pkg-6',
|
|
675
|
-
name: 'axios',
|
|
676
|
-
version: '^1.17.0',
|
|
677
|
-
latestVersion: '1.18.0',
|
|
678
|
-
sizeKb: 88,
|
|
679
|
-
percentage: 5.0,
|
|
680
|
-
type: 'direct',
|
|
681
|
-
category: 'network',
|
|
682
|
-
color: '#F59E0B',
|
|
683
|
-
description: 'HTTP client, request/response interceptors, and adapter pipelines.',
|
|
684
|
-
logoUri: 'https://axios-http.com/assets/logo.svg',
|
|
685
|
-
lastActive: '4 days ago',
|
|
686
|
-
npmUrl: 'https://www.npmjs.com/package/axios',
|
|
687
|
-
isDeprecated: false,
|
|
688
|
-
},
|
|
689
|
-
{
|
|
690
|
-
id: 'pkg-7',
|
|
691
|
-
name: 'react-native-linear-gradient',
|
|
692
|
-
version: '^2.8.3',
|
|
693
|
-
latestVersion: '2.8.3',
|
|
694
|
-
sizeKb: 45,
|
|
695
|
-
percentage: 2.5,
|
|
696
|
-
type: 'direct',
|
|
697
|
-
category: 'ui',
|
|
698
|
-
color: '#FB7185',
|
|
699
|
-
description: 'Hardware accelerated native gradient drawing shaders.',
|
|
700
|
-
logoUri: 'https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/React-icon.svg/512px-React-icon.svg.png',
|
|
701
|
-
lastActive: '2 years ago',
|
|
702
|
-
npmUrl: 'https://www.npmjs.com/package/react-native-linear-gradient',
|
|
703
|
-
isDeprecated: true,
|
|
704
|
-
deprecationReason: 'Package maintenance is in transition. Consider expo-linear-gradient or @react-native-community/linear-gradient for Fabric support.',
|
|
705
|
-
},
|
|
706
|
-
{
|
|
707
|
-
id: 'pkg-8',
|
|
708
|
-
name: 'i18next & react-i18next',
|
|
709
|
-
version: '^26.3.6',
|
|
710
|
-
latestVersion: '26.3.6',
|
|
711
|
-
sizeKb: 62,
|
|
712
|
-
percentage: 3.5,
|
|
713
|
-
type: 'direct',
|
|
714
|
-
category: 'utils',
|
|
715
|
-
color: '#10B981',
|
|
716
|
-
description: 'Internationalization framework, language interpolator, and locale switcher.',
|
|
717
|
-
logoUri: 'https://raw.githubusercontent.com/i18next/i18next/master/assets/i18next-logo.png',
|
|
718
|
-
lastActive: '6 days ago',
|
|
719
|
-
npmUrl: 'https://www.npmjs.com/package/i18next',
|
|
720
|
-
isDeprecated: false,
|
|
721
|
-
},
|
|
722
|
-
];
|
|
723
337
|
const CATEGORY_TABS = [
|
|
724
|
-
{ key: 'ALL',
|
|
725
|
-
{ key: 'UNUSED',
|
|
726
|
-
{ key: 'CONSUMED',
|
|
727
|
-
{ key: 'image',
|
|
728
|
-
{ key: 'typescript',
|
|
729
|
-
{ key: 'javascript',
|
|
730
|
-
{ key: 'font',
|
|
731
|
-
{ key: 'json',
|
|
338
|
+
{ key: 'ALL', labelKey: 'bundle.catAll', icon: '📁' },
|
|
339
|
+
{ key: 'UNUSED', labelKey: 'bundle.catUnused', icon: '🚫' },
|
|
340
|
+
{ key: 'CONSUMED', labelKey: 'bundle.catConsumed', icon: '⚡' },
|
|
341
|
+
{ key: 'image', labelKey: 'bundle.catImages', icon: '🖼️' },
|
|
342
|
+
{ key: 'typescript', labelKey: 'bundle.catTypescript', icon: '📜' },
|
|
343
|
+
{ key: 'javascript', labelKey: 'bundle.catJavascript', icon: '⚙️' },
|
|
344
|
+
{ key: 'font', labelKey: 'bundle.catFonts', icon: '🔤' },
|
|
345
|
+
{ key: 'json', labelKey: 'bundle.catJson', icon: '📄' },
|
|
732
346
|
];
|
|
733
347
|
const CATEGORY_COLORS = {
|
|
734
|
-
image: { label: 'Images & Media', color:
|
|
735
|
-
typescript: { label: 'TypeScript', color:
|
|
736
|
-
javascript: { label: 'JavaScript', color:
|
|
737
|
-
font: { label: 'Fonts', color:
|
|
738
|
-
json: { label: 'JSON / Data', color:
|
|
348
|
+
image: { label: 'Images & Media', color: AppColors.pink500, bg: AppColors.pink100 },
|
|
349
|
+
typescript: { label: 'TypeScript', color: AppColors.sky500, bg: AppColors.sky100 },
|
|
350
|
+
javascript: { label: 'JavaScript', color: AppColors.indigo500, bg: AppColors.indigo50 },
|
|
351
|
+
font: { label: 'Fonts', color: AppColors.purple500, bg: AppColors.purple100 },
|
|
352
|
+
json: { label: 'JSON / Data', color: AppColors.emerald500, bg: AppColors.emerald100 },
|
|
739
353
|
};
|
|
740
354
|
const BundleTab = React.memo(() => {
|
|
741
355
|
const { t } = useTranslation();
|
|
742
356
|
const [activeSubTab, setActiveSubTab] = useState('overview');
|
|
743
357
|
const [search, setSearch] = useState('');
|
|
744
358
|
const [activeCategory, setActiveCategory] = useState('ALL');
|
|
359
|
+
// ─── Live Host App Bundle Analysis (fetched from Metro / runtime) ─────────
|
|
360
|
+
const [analysis, setAnalysis] = useState(() => getCachedBundleAnalysis());
|
|
361
|
+
const [isAnalyzing, setIsAnalyzing] = useState(!getCachedBundleAnalysis());
|
|
362
|
+
useEffect(() => {
|
|
363
|
+
let mounted = true;
|
|
364
|
+
analyzeHostAppBundle().then(result => {
|
|
365
|
+
if (mounted) {
|
|
366
|
+
setAnalysis(result);
|
|
367
|
+
setIsAnalyzing(false);
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
return () => {
|
|
371
|
+
mounted = false;
|
|
372
|
+
};
|
|
373
|
+
}, []);
|
|
745
374
|
// Check if Hermes is running
|
|
746
375
|
const isHermes = Boolean(global.HermesInternal);
|
|
376
|
+
// Dynamic files & packages derived from the analyzed host bundle
|
|
377
|
+
const bundleFiles = useMemo(() => (analysis ? analysis.files : []), [analysis]);
|
|
378
|
+
const bundlePackages = useMemo(() => (analysis ? analysis.packages : []), [analysis]);
|
|
747
379
|
// Group summary calculations
|
|
748
380
|
const summary = useMemo(() => {
|
|
749
381
|
let imagesKb = 0;
|
|
@@ -756,7 +388,7 @@ const BundleTab = React.memo(() => {
|
|
|
756
388
|
let jsCount = 0;
|
|
757
389
|
let fontsCount = 0;
|
|
758
390
|
let jsonCount = 0;
|
|
759
|
-
|
|
391
|
+
bundleFiles.forEach(f => {
|
|
760
392
|
if (f.category === 'image') {
|
|
761
393
|
imagesKb += f.sizeKb;
|
|
762
394
|
imagesCount++;
|
|
@@ -782,37 +414,37 @@ const BundleTab = React.memo(() => {
|
|
|
782
414
|
return {
|
|
783
415
|
totalKb,
|
|
784
416
|
totalMb: (totalKb / 1024).toFixed(2),
|
|
785
|
-
totalCount:
|
|
417
|
+
totalCount: bundleFiles.length,
|
|
786
418
|
images: {
|
|
787
419
|
count: imagesCount,
|
|
788
420
|
kb: imagesKb,
|
|
789
|
-
pct: Number(((imagesKb / totalKb) * 100).toFixed(1)),
|
|
421
|
+
pct: totalKb ? Number(((imagesKb / totalKb) * 100).toFixed(1)) : 0,
|
|
790
422
|
},
|
|
791
423
|
ts: {
|
|
792
424
|
count: tsCount,
|
|
793
425
|
kb: tsKb,
|
|
794
|
-
pct: Number(((tsKb / totalKb) * 100).toFixed(1)),
|
|
426
|
+
pct: totalKb ? Number(((tsKb / totalKb) * 100).toFixed(1)) : 0,
|
|
795
427
|
},
|
|
796
428
|
js: {
|
|
797
429
|
count: jsCount,
|
|
798
430
|
kb: jsKb,
|
|
799
|
-
pct: Number(((jsKb / totalKb) * 100).toFixed(1)),
|
|
431
|
+
pct: totalKb ? Number(((jsKb / totalKb) * 100).toFixed(1)) : 0,
|
|
800
432
|
},
|
|
801
433
|
fonts: {
|
|
802
434
|
count: fontsCount,
|
|
803
435
|
kb: fontsKb,
|
|
804
|
-
pct: Number(((fontsKb / totalKb) * 100).toFixed(1)),
|
|
436
|
+
pct: totalKb ? Number(((fontsKb / totalKb) * 100).toFixed(1)) : 0,
|
|
805
437
|
},
|
|
806
438
|
json: {
|
|
807
439
|
count: jsonCount,
|
|
808
440
|
kb: jsonKb,
|
|
809
|
-
pct: Number(((jsonKb / totalKb) * 100).toFixed(1)),
|
|
441
|
+
pct: totalKb ? Number(((jsonKb / totalKb) * 100).toFixed(1)) : 0,
|
|
810
442
|
},
|
|
811
443
|
};
|
|
812
|
-
}, []);
|
|
444
|
+
}, [bundleFiles]);
|
|
813
445
|
// Filtered files
|
|
814
446
|
const filteredFiles = useMemo(() => {
|
|
815
|
-
return
|
|
447
|
+
return bundleFiles.filter(file => {
|
|
816
448
|
const matchSearch = !search ||
|
|
817
449
|
file.name.toLowerCase().includes(search.toLowerCase()) ||
|
|
818
450
|
file.path.toLowerCase().includes(search.toLowerCase()) ||
|
|
@@ -827,7 +459,7 @@ const BundleTab = React.memo(() => {
|
|
|
827
459
|
: file.category === activeCategory;
|
|
828
460
|
return matchSearch && matchCategory;
|
|
829
461
|
});
|
|
830
|
-
}, [search, activeCategory]);
|
|
462
|
+
}, [search, activeCategory, bundleFiles]);
|
|
831
463
|
const filteredTotalKb = useMemo(() => {
|
|
832
464
|
return filteredFiles.reduce((acc, f) => acc + f.sizeKb, 0);
|
|
833
465
|
}, [filteredFiles]);
|
|
@@ -862,46 +494,46 @@ const BundleTab = React.memo(() => {
|
|
|
862
494
|
// Filtered Packages
|
|
863
495
|
const filteredPackages = useMemo(() => {
|
|
864
496
|
if (!search)
|
|
865
|
-
return
|
|
497
|
+
return bundlePackages;
|
|
866
498
|
const q = search.toLowerCase();
|
|
867
|
-
return
|
|
499
|
+
return bundlePackages.filter(pkg => pkg.name.toLowerCase().includes(q) ||
|
|
868
500
|
pkg.description.toLowerCase().includes(q) ||
|
|
869
501
|
pkg.category.toLowerCase().includes(q));
|
|
870
|
-
}, [search]);
|
|
502
|
+
}, [search, bundlePackages]);
|
|
871
503
|
const packagesTotalKb = useMemo(() => {
|
|
872
504
|
return filteredPackages.reduce((acc, p) => acc + p.sizeKb, 0);
|
|
873
505
|
}, [filteredPackages]);
|
|
874
506
|
// Media files (Images + Fonts)
|
|
875
507
|
const mediaFiles = useMemo(() => {
|
|
876
|
-
return
|
|
877
|
-
}, []);
|
|
508
|
+
return bundleFiles.filter(f => f.category === 'image' || f.category === 'font');
|
|
509
|
+
}, [bundleFiles]);
|
|
878
510
|
const mediaTotalKb = useMemo(() => {
|
|
879
511
|
return mediaFiles.reduce((acc, m) => acc + m.sizeKb, 0);
|
|
880
512
|
}, [mediaFiles]);
|
|
881
513
|
const subTabs = [
|
|
882
514
|
{
|
|
883
515
|
key: 'overview',
|
|
884
|
-
label: '
|
|
516
|
+
label: t('bundle.tabOverview'),
|
|
885
517
|
icon: (isActive) => (<LiveStateIcon color={isActive ? AppColors.white : AppColors.purple} size={12}/>),
|
|
886
518
|
},
|
|
887
519
|
{
|
|
888
520
|
key: 'files',
|
|
889
|
-
label:
|
|
521
|
+
label: t('bundle.tabFiles', { count: bundleFiles.length }),
|
|
890
522
|
icon: (isActive) => (<LayersIcon color={isActive ? AppColors.white : AppColors.purple} size={12}/>),
|
|
891
523
|
},
|
|
892
524
|
{
|
|
893
525
|
key: 'packages',
|
|
894
|
-
label:
|
|
526
|
+
label: t('bundle.tabPackages', { count: bundlePackages.length }),
|
|
895
527
|
icon: (isActive) => (<PackageIcon color={isActive ? AppColors.white : AppColors.purple} size={12}/>),
|
|
896
528
|
},
|
|
897
529
|
{
|
|
898
530
|
key: 'media',
|
|
899
|
-
label:
|
|
531
|
+
label: t('bundle.tabMedia', { count: mediaFiles.length }),
|
|
900
532
|
icon: (isActive) => (<StorageIcon color={isActive ? AppColors.white : AppColors.purple} size={12}/>),
|
|
901
533
|
},
|
|
902
534
|
{
|
|
903
535
|
key: 'optimizer',
|
|
904
|
-
label: '
|
|
536
|
+
label: t('bundle.tabOptimizer'),
|
|
905
537
|
icon: (isActive) => (<MetadataIcon color={isActive ? AppColors.white : AppColors.purple} size={12}/>),
|
|
906
538
|
},
|
|
907
539
|
];
|
|
@@ -927,10 +559,21 @@ const BundleTab = React.memo(() => {
|
|
|
927
559
|
</ScrollView>
|
|
928
560
|
</View>
|
|
929
561
|
|
|
562
|
+
{/* ─── Live Bundle Analysis Loading State ─── */}
|
|
563
|
+
{isAnalyzing && (<View style={bundleStyles.analyzingContainer}>
|
|
564
|
+
<ActivityIndicator size="small" color={AppColors.brandPurple}/>
|
|
565
|
+
<Text style={bundleStyles.analyzingText}>
|
|
566
|
+
{t('bundle.analyzingTitle')}
|
|
567
|
+
</Text>
|
|
568
|
+
<Text style={bundleStyles.analyzingHint}>
|
|
569
|
+
{t('bundle.analyzingHint')}
|
|
570
|
+
</Text>
|
|
571
|
+
</View>)}
|
|
572
|
+
|
|
930
573
|
{/* ══════════════════════════════════════════════════════════════════════ */}
|
|
931
574
|
{/* ── 1. TAB: OVERVIEW & TREEMAP ───────────────────────────────────────── */}
|
|
932
575
|
{/* ══════════════════════════════════════════════════════════════════════ */}
|
|
933
|
-
{activeSubTab === 'overview' && (<ScrollView style={{ flex: 1 }} contentContainerStyle={bundleStyles.contentContainer} keyboardShouldPersistTaps="handled">
|
|
576
|
+
{activeSubTab === 'overview' && !isAnalyzing && (<ScrollView style={{ flex: 1 }} contentContainerStyle={bundleStyles.contentContainer} keyboardShouldPersistTaps="handled">
|
|
934
577
|
|
|
935
578
|
{/* Hero Overview Card */}
|
|
936
579
|
<View style={bundleStyles.heroCard}>
|
|
@@ -939,42 +582,59 @@ const BundleTab = React.memo(() => {
|
|
|
939
582
|
<PackageIcon color={AppColors.brandPurple} size={24}/>
|
|
940
583
|
</View>
|
|
941
584
|
<View style={{ flex: 1 }}>
|
|
942
|
-
<Text style={bundleStyles.heroTitle}>
|
|
585
|
+
<Text style={bundleStyles.heroTitle}>{t('bundle.heroTitle')}</Text>
|
|
943
586
|
<Text style={bundleStyles.heroSubtitle}>
|
|
944
|
-
|
|
587
|
+
{analysis?.isLive
|
|
588
|
+
? t('bundle.heroSubtitleLive', { scriptUrl: analysis.scriptURL })
|
|
589
|
+
: t('bundle.heroSubtitle')}
|
|
945
590
|
</Text>
|
|
946
591
|
</View>
|
|
947
|
-
<CopyButton value={() => summary} label=
|
|
592
|
+
<CopyButton value={() => ({ ...summary, bundle: analysis })} label={t('bundle.bundleOverviewJson')}/>
|
|
948
593
|
</View>
|
|
949
594
|
|
|
950
595
|
{/* 4-Stat Metric Grid */}
|
|
951
596
|
<View style={bundleStyles.metricsGrid}>
|
|
952
597
|
<View style={bundleStyles.metricBox}>
|
|
953
|
-
<Text style={bundleStyles.metricLabel}>
|
|
954
|
-
<Text style={bundleStyles.metricValue}
|
|
955
|
-
|
|
598
|
+
<Text style={bundleStyles.metricLabel}>{t('bundle.devBundleSize')}</Text>
|
|
599
|
+
<Text style={bundleStyles.metricValue}>
|
|
600
|
+
{t('bundle.mbValue', {
|
|
601
|
+
size: analysis ? analysis.totalDevMb : summary.totalMb,
|
|
602
|
+
})}
|
|
603
|
+
</Text>
|
|
604
|
+
<Text style={bundleStyles.metricHint}>
|
|
605
|
+
{analysis
|
|
606
|
+
? t('bundle.devBundleHint', {
|
|
607
|
+
kb: analysis.totalDevKb,
|
|
608
|
+
modules: analysis.moduleCount,
|
|
609
|
+
})
|
|
610
|
+
: t('bundle.trackedFilesHint', { count: bundleFiles.length })}
|
|
611
|
+
</Text>
|
|
956
612
|
</View>
|
|
957
613
|
|
|
958
614
|
<View style={bundleStyles.metricBox}>
|
|
959
|
-
<Text style={bundleStyles.metricLabel}>
|
|
960
|
-
<Text style={[bundleStyles.metricValue, { color:
|
|
961
|
-
{summary.images.kb}
|
|
615
|
+
<Text style={bundleStyles.metricLabel}>{t('bundle.imagesMedia')}</Text>
|
|
616
|
+
<Text style={[bundleStyles.metricValue, { color: AppColors.pink500 }]}>
|
|
617
|
+
{t('bundle.fileSizeKb', { size: summary.images.kb })}
|
|
618
|
+
</Text>
|
|
619
|
+
<Text style={bundleStyles.metricHint}>
|
|
620
|
+
{t('bundle.pctOfTrackedAssets', { pct: summary.images.pct })}
|
|
962
621
|
</Text>
|
|
963
|
-
<Text style={bundleStyles.metricHint}>{summary.images.pct}% of package</Text>
|
|
964
622
|
</View>
|
|
965
623
|
|
|
966
624
|
<View style={bundleStyles.metricBox}>
|
|
967
|
-
<Text style={bundleStyles.metricLabel}>
|
|
968
|
-
<Text style={[bundleStyles.metricValue, { color:
|
|
969
|
-
{summary.ts.kb + summary.js.kb}
|
|
625
|
+
<Text style={bundleStyles.metricLabel}>{t('bundle.tsJsSource')}</Text>
|
|
626
|
+
<Text style={[bundleStyles.metricValue, { color: AppColors.sky500 }]}>
|
|
627
|
+
{t('bundle.fileSizeKb', { size: summary.ts.kb + summary.js.kb })}
|
|
970
628
|
</Text>
|
|
971
629
|
<Text style={bundleStyles.metricHint}>
|
|
972
|
-
{(
|
|
630
|
+
{t('bundle.pctOfTrackedAssets', {
|
|
631
|
+
pct: (summary.ts.pct + summary.js.pct).toFixed(1),
|
|
632
|
+
})}
|
|
973
633
|
</Text>
|
|
974
634
|
</View>
|
|
975
635
|
|
|
976
636
|
<View style={bundleStyles.metricBox}>
|
|
977
|
-
<Text style={bundleStyles.metricLabel}>
|
|
637
|
+
<Text style={bundleStyles.metricLabel}>{t('bundle.jsEngine')}</Text>
|
|
978
638
|
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 5, marginTop: 2 }}>
|
|
979
639
|
<View style={[
|
|
980
640
|
bundleStyles.statusIndicator,
|
|
@@ -984,17 +644,105 @@ const BundleTab = React.memo(() => {
|
|
|
984
644
|
{isHermes ? 'Hermes' : 'JSC'}
|
|
985
645
|
</Text>
|
|
986
646
|
</View>
|
|
987
|
-
<Text style={bundleStyles.metricHint}>
|
|
647
|
+
<Text style={bundleStyles.metricHint}>
|
|
648
|
+
{isHermes ? t('bundle.bytecodeAot') : t('bundle.standard')}
|
|
649
|
+
</Text>
|
|
988
650
|
</View>
|
|
989
651
|
</View>
|
|
990
652
|
</View>
|
|
991
653
|
|
|
654
|
+
{/* Development Bundle Split-Up (derived from the real running bundle) */}
|
|
655
|
+
{analysis && (<View style={bundleStyles.sectionCard}>
|
|
656
|
+
<View style={bundleStyles.sectionHeaderRow}>
|
|
657
|
+
<View>
|
|
658
|
+
<Text style={bundleStyles.sectionTitle}>{t('bundle.splitUpTitle')}</Text>
|
|
659
|
+
<Text style={bundleStyles.sectionSub}>
|
|
660
|
+
{t('bundle.splitUpSub', {
|
|
661
|
+
mb: analysis.totalDevMb,
|
|
662
|
+
files: analysis.filesCount,
|
|
663
|
+
packages: analysis.packageCount,
|
|
664
|
+
})}
|
|
665
|
+
</Text>
|
|
666
|
+
</View>
|
|
667
|
+
<CopyButton value={() => analysis.splitUp} label={t('bundle.splitUpJson')}/>
|
|
668
|
+
</View>
|
|
669
|
+
|
|
670
|
+
{[
|
|
671
|
+
{
|
|
672
|
+
label: t('bundle.splitAppSource'),
|
|
673
|
+
color: AppColors.sky500,
|
|
674
|
+
...analysis.splitUp.appSource,
|
|
675
|
+
},
|
|
676
|
+
{
|
|
677
|
+
label: t('bundle.splitNodeModules'),
|
|
678
|
+
color: AppColors.indigo500,
|
|
679
|
+
...analysis.splitUp.nodeModules,
|
|
680
|
+
},
|
|
681
|
+
{
|
|
682
|
+
label: t('bundle.splitAssetsMedia'),
|
|
683
|
+
color: AppColors.pink500,
|
|
684
|
+
...analysis.splitUp.assetsMedia,
|
|
685
|
+
},
|
|
686
|
+
{
|
|
687
|
+
label: t('bundle.splitMetroOverhead'),
|
|
688
|
+
color: AppColors.amber500,
|
|
689
|
+
...analysis.splitUp.metroDevOverhead,
|
|
690
|
+
},
|
|
691
|
+
].map((part, idx) => (<View key={idx} style={bundleStyles.catRowCard}>
|
|
692
|
+
<View style={bundleStyles.catRowTop}>
|
|
693
|
+
<Text style={bundleStyles.catRowIcon}>{['📦', '🧩', '🖼️', '⚙️'][idx]}</Text>
|
|
694
|
+
<View style={{ flex: 1 }}>
|
|
695
|
+
<Text style={bundleStyles.catRowTitle}>{part.label}</Text>
|
|
696
|
+
<Text style={bundleStyles.catRowDesc}>
|
|
697
|
+
{t('bundle.splitSize', { kb: part.kb, mb: part.mb })}
|
|
698
|
+
</Text>
|
|
699
|
+
</View>
|
|
700
|
+
<View style={{ alignItems: 'flex-end' }}>
|
|
701
|
+
<Text style={bundleStyles.catRowSize}>{part.pct}%</Text>
|
|
702
|
+
</View>
|
|
703
|
+
</View>
|
|
704
|
+
<View style={bundleStyles.catProgressTrack}>
|
|
705
|
+
<View style={[
|
|
706
|
+
bundleStyles.catProgressBar,
|
|
707
|
+
{ width: `${part.pct}%`, backgroundColor: part.color },
|
|
708
|
+
]}/>
|
|
709
|
+
</View>
|
|
710
|
+
</View>))}
|
|
711
|
+
|
|
712
|
+
{/* Production Binary Footprint */}
|
|
713
|
+
<View style={bundleStyles.totalSummaryCard}>
|
|
714
|
+
<View style={bundleStyles.totalSummaryRow}>
|
|
715
|
+
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
|
|
716
|
+
<PackageIcon color={AppColors.purple} size={16}/>
|
|
717
|
+
<Text style={bundleStyles.totalSummaryLabel}>
|
|
718
|
+
{t('bundle.productionFootprint')}
|
|
719
|
+
</Text>
|
|
720
|
+
</View>
|
|
721
|
+
<Text style={bundleStyles.totalSummaryValue}>
|
|
722
|
+
{t('bundle.productionValue', {
|
|
723
|
+
ios: analysis.production.ios.totalInstallMb,
|
|
724
|
+
android: analysis.production.android.totalInstallMb,
|
|
725
|
+
})}
|
|
726
|
+
</Text>
|
|
727
|
+
</View>
|
|
728
|
+
<Text style={bundleStyles.totalSummaryFormula}>
|
|
729
|
+
{t('bundle.productionDownload', {
|
|
730
|
+
ios: analysis.production.ios.totalDownloadMb,
|
|
731
|
+
aab: analysis.production.androidAab.totalDownloadMb,
|
|
732
|
+
apk: analysis.production.androidApk.totalDownloadMb,
|
|
733
|
+
})}
|
|
734
|
+
</Text>
|
|
735
|
+
</View>
|
|
736
|
+
</View>)}
|
|
737
|
+
|
|
992
738
|
{/* Visual Type Distribution Ratio Bar */}
|
|
993
739
|
<View style={bundleStyles.sectionCard}>
|
|
994
740
|
<View style={bundleStyles.sectionHeaderRow}>
|
|
995
741
|
<View>
|
|
996
|
-
<Text style={bundleStyles.sectionTitle}>
|
|
997
|
-
<Text style={bundleStyles.sectionSub}>
|
|
742
|
+
<Text style={bundleStyles.sectionTitle}>{t('bundle.treemapTitle')}</Text>
|
|
743
|
+
<Text style={bundleStyles.sectionSub}>
|
|
744
|
+
{t('bundle.treemapSub', { mb: summary.totalMb })}
|
|
745
|
+
</Text>
|
|
998
746
|
</View>
|
|
999
747
|
<CopyButton value={() => ({
|
|
1000
748
|
totalMb: summary.totalMb,
|
|
@@ -1004,48 +752,63 @@ const BundleTab = React.memo(() => {
|
|
|
1004
752
|
js: summary.js,
|
|
1005
753
|
fonts: summary.fonts,
|
|
1006
754
|
json: summary.json,
|
|
1007
|
-
})} label=
|
|
755
|
+
})} label={t('bundle.treemapJson')}/>
|
|
1008
756
|
</View>
|
|
1009
757
|
|
|
1010
758
|
{/* Stacked Colored Bar */}
|
|
1011
759
|
<View style={bundleStyles.treemapBar}>
|
|
1012
|
-
<View style={{ flex: summary.images.pct, backgroundColor:
|
|
1013
|
-
<View style={{ flex: summary.ts.pct, backgroundColor:
|
|
1014
|
-
<View style={{ flex: summary.js.pct, backgroundColor:
|
|
1015
|
-
<View style={{ flex: summary.fonts.pct, backgroundColor:
|
|
1016
|
-
<View style={{ flex: summary.json.pct, backgroundColor:
|
|
760
|
+
<View style={{ flex: summary.images.pct, backgroundColor: AppColors.pink500, height: 16 }}/>
|
|
761
|
+
<View style={{ flex: summary.ts.pct, backgroundColor: AppColors.sky500, height: 16 }}/>
|
|
762
|
+
<View style={{ flex: summary.js.pct, backgroundColor: AppColors.indigo500, height: 16 }}/>
|
|
763
|
+
<View style={{ flex: summary.fonts.pct, backgroundColor: AppColors.purple500, height: 16 }}/>
|
|
764
|
+
<View style={{ flex: summary.json.pct, backgroundColor: AppColors.emerald500, height: 16 }}/>
|
|
1017
765
|
</View>
|
|
1018
766
|
|
|
1019
767
|
{/* Legend Grid */}
|
|
1020
768
|
<View style={bundleStyles.legendGrid}>
|
|
1021
769
|
<View style={bundleStyles.legendItem}>
|
|
1022
|
-
<View style={[bundleStyles.legendDot, { backgroundColor:
|
|
770
|
+
<View style={[bundleStyles.legendDot, { backgroundColor: AppColors.pink500 }]}/>
|
|
1023
771
|
<Text style={bundleStyles.legendText}>
|
|
1024
|
-
|
|
772
|
+
{t('bundle.legendImages')}{' '}
|
|
773
|
+
<Text style={bundleStyles.legendVal}>
|
|
774
|
+
{t('bundle.legendValue', { kb: summary.images.kb, pct: summary.images.pct })}
|
|
775
|
+
</Text>
|
|
1025
776
|
</Text>
|
|
1026
777
|
</View>
|
|
1027
778
|
<View style={bundleStyles.legendItem}>
|
|
1028
|
-
<View style={[bundleStyles.legendDot, { backgroundColor:
|
|
779
|
+
<View style={[bundleStyles.legendDot, { backgroundColor: AppColors.sky500 }]}/>
|
|
1029
780
|
<Text style={bundleStyles.legendText}>
|
|
1030
|
-
|
|
781
|
+
{t('bundle.legendTs')}{' '}
|
|
782
|
+
<Text style={bundleStyles.legendVal}>
|
|
783
|
+
{t('bundle.legendValue', { kb: summary.ts.kb, pct: summary.ts.pct })}
|
|
784
|
+
</Text>
|
|
1031
785
|
</Text>
|
|
1032
786
|
</View>
|
|
1033
787
|
<View style={bundleStyles.legendItem}>
|
|
1034
|
-
<View style={[bundleStyles.legendDot, { backgroundColor:
|
|
788
|
+
<View style={[bundleStyles.legendDot, { backgroundColor: AppColors.indigo500 }]}/>
|
|
1035
789
|
<Text style={bundleStyles.legendText}>
|
|
1036
|
-
|
|
790
|
+
{t('bundle.legendJs')}{' '}
|
|
791
|
+
<Text style={bundleStyles.legendVal}>
|
|
792
|
+
{t('bundle.legendValue', { kb: summary.js.kb, pct: summary.js.pct })}
|
|
793
|
+
</Text>
|
|
1037
794
|
</Text>
|
|
1038
795
|
</View>
|
|
1039
796
|
<View style={bundleStyles.legendItem}>
|
|
1040
|
-
<View style={[bundleStyles.legendDot, { backgroundColor:
|
|
797
|
+
<View style={[bundleStyles.legendDot, { backgroundColor: AppColors.purple500 }]}/>
|
|
1041
798
|
<Text style={bundleStyles.legendText}>
|
|
1042
|
-
|
|
799
|
+
{t('bundle.legendFonts')}{' '}
|
|
800
|
+
<Text style={bundleStyles.legendVal}>
|
|
801
|
+
{t('bundle.legendValue', { kb: summary.fonts.kb, pct: summary.fonts.pct })}
|
|
802
|
+
</Text>
|
|
1043
803
|
</Text>
|
|
1044
804
|
</View>
|
|
1045
805
|
<View style={bundleStyles.legendItem}>
|
|
1046
|
-
<View style={[bundleStyles.legendDot, { backgroundColor:
|
|
806
|
+
<View style={[bundleStyles.legendDot, { backgroundColor: AppColors.emerald500 }]}/>
|
|
1047
807
|
<Text style={bundleStyles.legendText}>
|
|
1048
|
-
|
|
808
|
+
{t('bundle.legendJson')}{' '}
|
|
809
|
+
<Text style={bundleStyles.legendVal}>
|
|
810
|
+
{t('bundle.legendValue', { kb: summary.json.kb, pct: summary.json.pct })}
|
|
811
|
+
</Text>
|
|
1049
812
|
</Text>
|
|
1050
813
|
</View>
|
|
1051
814
|
</View>
|
|
@@ -1054,7 +817,7 @@ const BundleTab = React.memo(() => {
|
|
|
1054
817
|
{/* Detailed Category Footprint Cards */}
|
|
1055
818
|
<View style={bundleStyles.sectionCard}>
|
|
1056
819
|
<View style={bundleStyles.sectionHeaderRow}>
|
|
1057
|
-
<Text style={bundleStyles.sectionTitle}>
|
|
820
|
+
<Text style={bundleStyles.sectionTitle}>{t('bundle.categoryBreakdown')}</Text>
|
|
1058
821
|
<CopyButton value={() => ({
|
|
1059
822
|
totalAssets: summary.totalCount,
|
|
1060
823
|
totalSizeKb: summary.totalKb,
|
|
@@ -1065,61 +828,66 @@ const BundleTab = React.memo(() => {
|
|
|
1065
828
|
typescript: summary.ts,
|
|
1066
829
|
json: summary.json,
|
|
1067
830
|
},
|
|
1068
|
-
})} label=
|
|
831
|
+
})} label={t('bundle.categoriesJson')}/>
|
|
1069
832
|
</View>
|
|
1070
833
|
|
|
1071
834
|
{[
|
|
1072
835
|
{
|
|
1073
836
|
icon: '🖼️',
|
|
1074
|
-
title: '
|
|
837
|
+
title: t('bundle.catImagesTitle'),
|
|
1075
838
|
count: summary.images.count,
|
|
1076
|
-
size:
|
|
839
|
+
size: t('bundle.fileSizeKb', { size: summary.images.kb }),
|
|
1077
840
|
pct: summary.images.pct,
|
|
1078
|
-
color:
|
|
1079
|
-
desc: '
|
|
841
|
+
color: AppColors.pink500,
|
|
842
|
+
desc: t('bundle.catImagesDesc'),
|
|
1080
843
|
},
|
|
1081
844
|
{
|
|
1082
845
|
icon: '⚙️',
|
|
1083
|
-
title: '
|
|
846
|
+
title: t('bundle.catJsTitle'),
|
|
1084
847
|
count: summary.js.count,
|
|
1085
|
-
size:
|
|
848
|
+
size: t('bundle.fileSizeKb', { size: summary.js.kb }),
|
|
1086
849
|
pct: summary.js.pct,
|
|
1087
|
-
color:
|
|
1088
|
-
desc: '
|
|
850
|
+
color: AppColors.indigo500,
|
|
851
|
+
desc: t('bundle.catJsDesc'),
|
|
1089
852
|
},
|
|
1090
853
|
{
|
|
1091
854
|
icon: '🔤',
|
|
1092
|
-
title: '
|
|
855
|
+
title: t('bundle.catFontsTitle'),
|
|
1093
856
|
count: summary.fonts.count,
|
|
1094
|
-
size:
|
|
857
|
+
size: t('bundle.fileSizeKb', { size: summary.fonts.kb }),
|
|
1095
858
|
pct: summary.fonts.pct,
|
|
1096
|
-
color:
|
|
1097
|
-
desc:
|
|
859
|
+
color: AppColors.purple500,
|
|
860
|
+
desc: bundleFiles
|
|
861
|
+
.filter(f => f.category === 'font')
|
|
862
|
+
.map(f => f.name)
|
|
863
|
+
.join(', ') || t('bundle.catFontsDesc'),
|
|
1098
864
|
},
|
|
1099
865
|
{
|
|
1100
866
|
icon: '📜',
|
|
1101
|
-
title: '
|
|
867
|
+
title: t('bundle.catTsTitle'),
|
|
1102
868
|
count: summary.ts.count,
|
|
1103
|
-
size:
|
|
869
|
+
size: t('bundle.fileSizeKb', { size: summary.ts.kb }),
|
|
1104
870
|
pct: summary.ts.pct,
|
|
1105
|
-
color:
|
|
1106
|
-
desc: '
|
|
871
|
+
color: AppColors.sky500,
|
|
872
|
+
desc: t('bundle.catTsDesc'),
|
|
1107
873
|
},
|
|
1108
874
|
{
|
|
1109
875
|
icon: '📄',
|
|
1110
|
-
title: '
|
|
876
|
+
title: t('bundle.catJsonTitle'),
|
|
1111
877
|
count: summary.json.count,
|
|
1112
|
-
size:
|
|
878
|
+
size: t('bundle.fileSizeKb', { size: summary.json.kb }),
|
|
1113
879
|
pct: summary.json.pct,
|
|
1114
|
-
color:
|
|
1115
|
-
desc: '
|
|
880
|
+
color: AppColors.emerald500,
|
|
881
|
+
desc: t('bundle.catJsonDesc'),
|
|
1116
882
|
},
|
|
1117
883
|
].map((cat, cIdx) => (<View key={cIdx} style={bundleStyles.catRowCard}>
|
|
1118
884
|
<View style={bundleStyles.catRowTop}>
|
|
1119
885
|
<Text style={bundleStyles.catRowIcon}>{cat.icon}</Text>
|
|
1120
886
|
<View style={{ flex: 1 }}>
|
|
1121
887
|
<Text style={bundleStyles.catRowTitle}>{cat.title}</Text>
|
|
1122
|
-
<Text style={bundleStyles.catRowDesc}>
|
|
888
|
+
<Text style={bundleStyles.catRowDesc}>
|
|
889
|
+
{t('bundle.catRowSub', { count: cat.count, desc: cat.desc })}
|
|
890
|
+
</Text>
|
|
1123
891
|
</View>
|
|
1124
892
|
<View style={{ alignItems: 'flex-end' }}>
|
|
1125
893
|
<Text style={bundleStyles.catRowSize}>{cat.size}</Text>
|
|
@@ -1139,14 +907,24 @@ const BundleTab = React.memo(() => {
|
|
|
1139
907
|
<View style={bundleStyles.totalSummaryRow}>
|
|
1140
908
|
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
|
|
1141
909
|
<PackageIcon color={AppColors.purple} size={16}/>
|
|
1142
|
-
<Text style={bundleStyles.totalSummaryLabel}>
|
|
910
|
+
<Text style={bundleStyles.totalSummaryLabel}>
|
|
911
|
+
{t('bundle.totalBundleAssets')}
|
|
912
|
+
</Text>
|
|
1143
913
|
</View>
|
|
1144
914
|
<Text style={bundleStyles.totalSummaryValue}>
|
|
1145
|
-
{summary.totalKb
|
|
915
|
+
{t('bundle.totalBundleValue', { kb: summary.totalKb, mb: summary.totalMb })}
|
|
1146
916
|
</Text>
|
|
1147
917
|
</View>
|
|
1148
918
|
<Text style={bundleStyles.totalSummaryFormula}>
|
|
1149
|
-
|
|
919
|
+
{t('bundle.totalFormula', {
|
|
920
|
+
images: summary.images.kb,
|
|
921
|
+
js: summary.js.kb,
|
|
922
|
+
fonts: summary.fonts.kb,
|
|
923
|
+
ts: summary.ts.kb,
|
|
924
|
+
json: summary.json.kb,
|
|
925
|
+
total: summary.totalKb,
|
|
926
|
+
count: summary.totalCount,
|
|
927
|
+
})}
|
|
1150
928
|
</Text>
|
|
1151
929
|
</View>
|
|
1152
930
|
</View>
|
|
@@ -1155,16 +933,16 @@ const BundleTab = React.memo(() => {
|
|
|
1155
933
|
{/* ══════════════════════════════════════════════════════════════════════ */}
|
|
1156
934
|
{/* ── 2. TAB: FILES BREAKDOWN ─────────────────────────────────────────── */}
|
|
1157
935
|
{/* ══════════════════════════════════════════════════════════════════════ */}
|
|
1158
|
-
{activeSubTab === 'files' && (<View style={{ flex: 1 }}>
|
|
936
|
+
{activeSubTab === 'files' && !isAnalyzing && (<View style={{ flex: 1 }}>
|
|
1159
937
|
{/* Search & Category Filter */}
|
|
1160
938
|
<View style={bundleStyles.filterContainer}>
|
|
1161
939
|
<View style={bundleStyles.searchRow}>
|
|
1162
940
|
<SearchIcon color={AppColors.grayTextWeak} size={15}/>
|
|
1163
|
-
<TextInput placeholder=
|
|
941
|
+
<TextInput placeholder={t('bundle.searchFilesPlaceholder')} placeholderTextColor={AppColors.grayTextWeak} value={search} onChangeText={setSearch} style={bundleStyles.searchInput} autoCorrect={false} autoCapitalize="none"/>
|
|
1164
942
|
{search.length > 0 && (<Pressable onPress={() => setSearch('')} hitSlop={10}>
|
|
1165
943
|
<ClearIcon color={AppColors.grayTextWeak} size={14}/>
|
|
1166
944
|
</Pressable>)}
|
|
1167
|
-
<CopyButton value={() => filteredFiles} label=
|
|
945
|
+
<CopyButton value={() => filteredFiles} label={t('bundle.filteredFilesJson')}/>
|
|
1168
946
|
</View>
|
|
1169
947
|
|
|
1170
948
|
{/* Category Filter Chips */}
|
|
@@ -1172,24 +950,24 @@ const BundleTab = React.memo(() => {
|
|
|
1172
950
|
{CATEGORY_TABS.map(tab => {
|
|
1173
951
|
const isActive = activeCategory === tab.key;
|
|
1174
952
|
const count = tab.key === 'ALL'
|
|
1175
|
-
?
|
|
953
|
+
? bundleFiles.length
|
|
1176
954
|
: tab.key === 'UNUSED'
|
|
1177
|
-
?
|
|
955
|
+
? bundleFiles.filter(f => f.isConsumed === false).length
|
|
1178
956
|
: tab.key === 'CONSUMED'
|
|
1179
|
-
?
|
|
1180
|
-
:
|
|
957
|
+
? bundleFiles.filter(f => f.isConsumed !== false).length
|
|
958
|
+
: bundleFiles.filter(f => f.category === tab.key).length;
|
|
1181
959
|
return (<TouchableScale key={tab.key} onPress={() => setActiveCategory(tab.key)} style={[
|
|
1182
960
|
bundleStyles.catPill,
|
|
1183
961
|
isActive && bundleStyles.catPillActive,
|
|
1184
|
-
tab.key === 'UNUSED' && isActive && { backgroundColor:
|
|
1185
|
-
tab.key === 'CONSUMED' && isActive && { backgroundColor:
|
|
962
|
+
tab.key === 'UNUSED' && isActive && { backgroundColor: AppColors.red500, borderColor: AppColors.red600 },
|
|
963
|
+
tab.key === 'CONSUMED' && isActive && { backgroundColor: AppColors.emerald600, borderColor: AppColors.emerald700 },
|
|
1186
964
|
]}>
|
|
1187
965
|
<Text style={bundleStyles.catPillIcon}>{tab.icon}</Text>
|
|
1188
966
|
<Text style={[
|
|
1189
967
|
bundleStyles.catPillText,
|
|
1190
968
|
isActive && bundleStyles.catPillTextActive,
|
|
1191
969
|
]}>
|
|
1192
|
-
{tab.
|
|
970
|
+
{t(tab.labelKey)} ({count})
|
|
1193
971
|
</Text>
|
|
1194
972
|
</TouchableScale>);
|
|
1195
973
|
})}
|
|
@@ -1210,7 +988,7 @@ const BundleTab = React.memo(() => {
|
|
|
1210
988
|
bundleStyles.viewModeBtnText,
|
|
1211
989
|
filesViewMode === 'tree' && bundleStyles.viewModeBtnTextActive,
|
|
1212
990
|
]}>
|
|
1213
|
-
|
|
991
|
+
{t('bundle.treeView')}
|
|
1214
992
|
</Text>
|
|
1215
993
|
</TouchableScale>
|
|
1216
994
|
<TouchableScale onPress={() => setFilesViewMode('list')} style={[
|
|
@@ -1221,21 +999,24 @@ const BundleTab = React.memo(() => {
|
|
|
1221
999
|
bundleStyles.viewModeBtnText,
|
|
1222
1000
|
filesViewMode === 'list' && bundleStyles.viewModeBtnTextActive,
|
|
1223
1001
|
]}>
|
|
1224
|
-
|
|
1002
|
+
{t('bundle.flatList')}
|
|
1225
1003
|
</Text>
|
|
1226
1004
|
</TouchableScale>
|
|
1227
1005
|
</View>
|
|
1228
1006
|
|
|
1229
1007
|
{filesViewMode === 'tree' ? (<View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
|
|
1230
1008
|
<TouchableScale onPress={expandAllFolders}>
|
|
1231
|
-
<Text style={bundleStyles.treeActionLink}>
|
|
1009
|
+
<Text style={bundleStyles.treeActionLink}>{t('bundle.expand')}</Text>
|
|
1232
1010
|
</TouchableScale>
|
|
1233
1011
|
<Text style={{ color: AppColors.grayTextWeak, fontSize: 11 }}>•</Text>
|
|
1234
1012
|
<TouchableScale onPress={collapseAllFolders}>
|
|
1235
|
-
<Text style={bundleStyles.treeActionLink}>
|
|
1013
|
+
<Text style={bundleStyles.treeActionLink}>{t('bundle.collapse')}</Text>
|
|
1236
1014
|
</TouchableScale>
|
|
1237
1015
|
</View>) : (<Text style={bundleStyles.listHeaderCount}>
|
|
1238
|
-
|
|
1016
|
+
{t('bundle.showingFilesOf', {
|
|
1017
|
+
shown: filteredFiles.length,
|
|
1018
|
+
total: bundleFiles.length,
|
|
1019
|
+
})}
|
|
1239
1020
|
</Text>)}
|
|
1240
1021
|
</View>
|
|
1241
1022
|
|
|
@@ -1260,9 +1041,9 @@ const BundleTab = React.memo(() => {
|
|
|
1260
1041
|
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 6, flexWrap: 'wrap' }}>
|
|
1261
1042
|
<HighlightText text={file.name} search={search} style={bundleStyles.fileName} highlightStyle={bundleStyles.searchHighlight}/>
|
|
1262
1043
|
{isUnused ? (<View style={bundleStyles.unusedBadge}>
|
|
1263
|
-
<Text style={bundleStyles.unusedBadgeText}
|
|
1044
|
+
<Text style={bundleStyles.unusedBadgeText}>{t('bundle.notConsumed')}</Text>
|
|
1264
1045
|
</View>) : (<View style={bundleStyles.consumedBadge}>
|
|
1265
|
-
<Text style={bundleStyles.consumedBadgeText}
|
|
1046
|
+
<Text style={bundleStyles.consumedBadgeText}>{t('bundle.consumed')}</Text>
|
|
1266
1047
|
</View>)}
|
|
1267
1048
|
</View>
|
|
1268
1049
|
<HighlightText text={file.path} search={search} style={bundleStyles.filePath} highlightStyle={bundleStyles.searchHighlight} numberOfLines={1}/>
|
|
@@ -1270,14 +1051,14 @@ const BundleTab = React.memo(() => {
|
|
|
1270
1051
|
|
|
1271
1052
|
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
|
|
1272
1053
|
<View style={bundleStyles.fileSizeBox}>
|
|
1273
|
-
<Text style={[bundleStyles.fileSizeKb, isUnused && { color:
|
|
1054
|
+
<Text style={[bundleStyles.fileSizeKb, isUnused && { color: AppColors.amber800Warm }]}>
|
|
1274
1055
|
{file.sizeKb >= 1024
|
|
1275
|
-
?
|
|
1276
|
-
:
|
|
1056
|
+
? t('bundle.fileSizeMb', { size: (file.sizeKb / 1024).toFixed(2) })
|
|
1057
|
+
: t('bundle.fileSizeKb', { size: file.sizeKb })}
|
|
1277
1058
|
</Text>
|
|
1278
1059
|
<Text style={bundleStyles.filePercent}>{pctOfTotal}%</Text>
|
|
1279
1060
|
</View>
|
|
1280
|
-
<CopyButton value={() => file} label=
|
|
1061
|
+
<CopyButton value={() => file} label={t('bundle.fileDetailsJson')}/>
|
|
1281
1062
|
</View>
|
|
1282
1063
|
</View>
|
|
1283
1064
|
|
|
@@ -1286,7 +1067,7 @@ const BundleTab = React.memo(() => {
|
|
|
1286
1067
|
bundleStyles.fileProgressBar,
|
|
1287
1068
|
{
|
|
1288
1069
|
width: `${Math.min(100, Math.max(4, Number(pctOfTotal) * 4))}%`,
|
|
1289
|
-
backgroundColor: isUnused ?
|
|
1070
|
+
backgroundColor: isUnused ? AppColors.red500 : categoryMeta.color,
|
|
1290
1071
|
},
|
|
1291
1072
|
]}/>
|
|
1292
1073
|
</View>
|
|
@@ -1301,9 +1082,9 @@ const BundleTab = React.memo(() => {
|
|
|
1301
1082
|
]}>
|
|
1302
1083
|
<Text style={[
|
|
1303
1084
|
bundleStyles.adviceText,
|
|
1304
|
-
file.status === 'warning' && { color:
|
|
1305
|
-
file.status === 'optimal' && { color:
|
|
1306
|
-
isUnused && { color:
|
|
1085
|
+
file.status === 'warning' && { color: AppColors.amber800Warm },
|
|
1086
|
+
file.status === 'optimal' && { color: AppColors.emerald700 },
|
|
1087
|
+
isUnused && { color: AppColors.red600 },
|
|
1307
1088
|
]}>
|
|
1308
1089
|
{isUnused ? '🗑️ ' : file.status === 'warning' ? '💡 ' : '✨ '}
|
|
1309
1090
|
{file.advice}
|
|
@@ -1319,11 +1100,17 @@ const BundleTab = React.memo(() => {
|
|
|
1319
1100
|
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
|
|
1320
1101
|
<LayersIcon color={AppColors.purple} size={14}/>
|
|
1321
1102
|
<Text style={bundleStyles.footerTitle}>
|
|
1322
|
-
{
|
|
1103
|
+
{t('bundle.filesOf', {
|
|
1104
|
+
count: filteredFiles.length,
|
|
1105
|
+
total: summary.totalCount,
|
|
1106
|
+
})}
|
|
1323
1107
|
</Text>
|
|
1324
1108
|
</View>
|
|
1325
1109
|
<Text style={bundleStyles.footerSizeVal}>
|
|
1326
|
-
{
|
|
1110
|
+
{t('bundle.kbMbValue', {
|
|
1111
|
+
kb: filteredTotalKb,
|
|
1112
|
+
mb: (filteredTotalKb / 1024).toFixed(2),
|
|
1113
|
+
})}
|
|
1327
1114
|
</Text>
|
|
1328
1115
|
</View>
|
|
1329
1116
|
</View>)}
|
|
@@ -1331,28 +1118,29 @@ const BundleTab = React.memo(() => {
|
|
|
1331
1118
|
{/* ══════════════════════════════════════════════════════════════════════ */}
|
|
1332
1119
|
{/* ── 3. TAB: PACKAGES & NODE_MODULES ─────────────────────────────────── */}
|
|
1333
1120
|
{/* ══════════════════════════════════════════════════════════════════════ */}
|
|
1334
|
-
{activeSubTab === 'packages' && (<View style={{ flex: 1 }}>
|
|
1121
|
+
{activeSubTab === 'packages' && !isAnalyzing && (<View style={{ flex: 1 }}>
|
|
1335
1122
|
<ScrollView style={{ flex: 1 }} contentContainerStyle={bundleStyles.contentContainer} keyboardShouldPersistTaps="handled">
|
|
1336
1123
|
|
|
1337
1124
|
<View style={bundleStyles.filterContainer}>
|
|
1338
1125
|
<View style={bundleStyles.searchRow}>
|
|
1339
1126
|
<SearchIcon color={AppColors.grayTextWeak} size={15}/>
|
|
1340
|
-
<TextInput placeholder=
|
|
1127
|
+
<TextInput placeholder={t('bundle.searchPackagesPlaceholder')} placeholderTextColor={AppColors.grayTextWeak} value={search} onChangeText={setSearch} style={bundleStyles.searchInput} autoCorrect={false} autoCapitalize="none"/>
|
|
1341
1128
|
{search.length > 0 && (<Pressable onPress={() => setSearch('')} hitSlop={10}>
|
|
1342
1129
|
<ClearIcon color={AppColors.grayTextWeak} size={14}/>
|
|
1343
1130
|
</Pressable>)}
|
|
1344
|
-
<CopyButton value={() => filteredPackages} label=
|
|
1131
|
+
<CopyButton value={() => filteredPackages} label={t('bundle.dependenciesJson')}/>
|
|
1345
1132
|
</View>
|
|
1346
1133
|
</View>
|
|
1347
1134
|
|
|
1348
1135
|
<Text style={bundleStyles.listHeaderCount}>
|
|
1349
|
-
|
|
1136
|
+
{t('bundle.showingDependencies', { count: filteredPackages.length })}
|
|
1350
1137
|
</Text>
|
|
1351
1138
|
|
|
1352
1139
|
{filteredPackages.map((pkg, index) => {
|
|
1353
|
-
const cleanInstalledVersion = pkg.version.replace(/^\^/, '');
|
|
1354
|
-
const hasUpdate = pkg.latestVersion &&
|
|
1355
|
-
|
|
1140
|
+
const cleanInstalledVersion = pkg.version ? pkg.version.replace(/^\^/, '') : '';
|
|
1141
|
+
const hasUpdate = !!pkg.latestVersion && !!cleanInstalledVersion &&
|
|
1142
|
+
pkg.latestVersion !== cleanInstalledVersion;
|
|
1143
|
+
return (<View key={pkg.id} style={[bundleStyles.fileCard, pkg.isDeprecated && { borderColor: AppColors.errorBorder, backgroundColor: '#FFFDFD' }]}>
|
|
1356
1144
|
{/* Header Row: #s.no + Logo + Package Name + Version Pill + Update/Deprecated Badges + Size & Copy */}
|
|
1357
1145
|
<View style={bundleStyles.fileCardTop}>
|
|
1358
1146
|
<View style={bundleStyles.sNoBadge}>
|
|
@@ -1366,26 +1154,40 @@ const BundleTab = React.memo(() => {
|
|
|
1366
1154
|
<HighlightText text={pkg.name} search={search} style={bundleStyles.fileName} highlightStyle={bundleStyles.searchHighlight}/>
|
|
1367
1155
|
<View style={bundleStyles.versionPill}>
|
|
1368
1156
|
<Text style={bundleStyles.versionPillText}>
|
|
1369
|
-
|
|
1157
|
+
{cleanInstalledVersion
|
|
1158
|
+
? t('bundle.versionPrefix', { version: cleanInstalledVersion })
|
|
1159
|
+
: t('bundle.bundled')}
|
|
1370
1160
|
</Text>
|
|
1371
1161
|
</View>
|
|
1372
1162
|
|
|
1373
1163
|
{pkg.isDeprecated ? (<View style={bundleStyles.deprecatedBadge}>
|
|
1374
|
-
<Text style={bundleStyles.deprecatedBadgeText}
|
|
1164
|
+
<Text style={bundleStyles.deprecatedBadgeText}>
|
|
1165
|
+
{t('bundle.deprecated')}
|
|
1166
|
+
</Text>
|
|
1375
1167
|
</View>) : hasUpdate ? (<View style={bundleStyles.updateBadge}>
|
|
1376
|
-
<Text style={bundleStyles.updateBadgeText}
|
|
1377
|
-
|
|
1378
|
-
|
|
1168
|
+
<Text style={bundleStyles.updateBadgeText}>
|
|
1169
|
+
{t('bundle.updateAvailable', { version: pkg.latestVersion })}
|
|
1170
|
+
</Text>
|
|
1171
|
+
</View>) : cleanInstalledVersion ? (<View style={[bundleStyles.updateBadge, { backgroundColor: AppColors.gray100, borderColor: AppColors.gray200 }]}>
|
|
1172
|
+
<Text style={[bundleStyles.updateBadgeText, { color: AppColors.gray600 }]}>
|
|
1173
|
+
{t('bundle.upToDate')}
|
|
1174
|
+
</Text>
|
|
1175
|
+
</View>) : (<View style={[bundleStyles.updateBadge, { backgroundColor: AppColors.gray100, borderColor: AppColors.gray200 }]}>
|
|
1176
|
+
<Text style={[bundleStyles.updateBadgeText, { color: AppColors.gray600 }]}>
|
|
1177
|
+
{t('bundle.bundledBadge')}
|
|
1178
|
+
</Text>
|
|
1379
1179
|
</View>)}
|
|
1380
1180
|
</View>
|
|
1381
1181
|
</View>
|
|
1382
1182
|
|
|
1383
1183
|
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
|
|
1384
1184
|
<View style={bundleStyles.fileSizeBox}>
|
|
1385
|
-
<Text style={bundleStyles.fileSizeKb}>
|
|
1185
|
+
<Text style={bundleStyles.fileSizeKb}>
|
|
1186
|
+
{t('bundle.fileSizeKb', { size: pkg.sizeKb })}
|
|
1187
|
+
</Text>
|
|
1386
1188
|
<Text style={bundleStyles.filePercent}>{pkg.percentage}%</Text>
|
|
1387
1189
|
</View>
|
|
1388
|
-
<CopyButton value={() => pkg} label=
|
|
1190
|
+
<CopyButton value={() => pkg} label={t('bundle.packageDetailsJson')}/>
|
|
1389
1191
|
</View>
|
|
1390
1192
|
</View>
|
|
1391
1193
|
|
|
@@ -1411,7 +1213,7 @@ const BundleTab = React.memo(() => {
|
|
|
1411
1213
|
<View style={bundleStyles.fileProgressTrack}>
|
|
1412
1214
|
<View style={[
|
|
1413
1215
|
bundleStyles.fileProgressBar,
|
|
1414
|
-
{ width: `${pkg.percentage * 2}%`, backgroundColor: pkg.isDeprecated ?
|
|
1216
|
+
{ width: `${pkg.percentage * 2}%`, backgroundColor: pkg.isDeprecated ? AppColors.red500 : pkg.color },
|
|
1415
1217
|
]}/>
|
|
1416
1218
|
</View>
|
|
1417
1219
|
|
|
@@ -1420,7 +1222,7 @@ const BundleTab = React.memo(() => {
|
|
|
1420
1222
|
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
|
|
1421
1223
|
<View style={bundleStyles.typeTag}>
|
|
1422
1224
|
<Text style={bundleStyles.typeTagText}>
|
|
1423
|
-
{pkg.type === 'direct' ? '
|
|
1225
|
+
{pkg.type === 'direct' ? t('bundle.direct') : t('bundle.transitive')}
|
|
1424
1226
|
</Text>
|
|
1425
1227
|
</View>
|
|
1426
1228
|
{pkg.lastActive && (<Text style={bundleStyles.lastActiveText}>
|
|
@@ -1430,13 +1232,13 @@ const BundleTab = React.memo(() => {
|
|
|
1430
1232
|
|
|
1431
1233
|
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
|
|
1432
1234
|
<Text style={bundleStyles.fileMetaText}>
|
|
1433
|
-
|
|
1235
|
+
{t('bundle.minified', { size: pkg.sizeKb })}
|
|
1434
1236
|
</Text>
|
|
1435
1237
|
<TouchableScale onPress={() => {
|
|
1436
1238
|
const url = pkg.npmUrl || `https://www.npmjs.com/package/${pkg.name}`;
|
|
1437
1239
|
Linking.openURL(url).catch(() => { });
|
|
1438
1240
|
}} style={bundleStyles.npmLinkBtn}>
|
|
1439
|
-
<Text style={bundleStyles.npmLinkText}>
|
|
1241
|
+
<Text style={bundleStyles.npmLinkText}>{t('bundle.npmLink')}</Text>
|
|
1440
1242
|
</TouchableScale>
|
|
1441
1243
|
</View>
|
|
1442
1244
|
</View>
|
|
@@ -1449,11 +1251,14 @@ const BundleTab = React.memo(() => {
|
|
|
1449
1251
|
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
|
|
1450
1252
|
<PackageIcon color={AppColors.purple} size={14}/>
|
|
1451
1253
|
<Text style={bundleStyles.footerTitle}>
|
|
1452
|
-
{filteredPackages.length}
|
|
1254
|
+
{t('bundle.dependenciesCount', { count: filteredPackages.length })}
|
|
1453
1255
|
</Text>
|
|
1454
1256
|
</View>
|
|
1455
1257
|
<Text style={bundleStyles.footerSizeVal}>
|
|
1456
|
-
{
|
|
1258
|
+
{t('bundle.kbMbValue', {
|
|
1259
|
+
kb: packagesTotalKb,
|
|
1260
|
+
mb: (packagesTotalKb / 1024).toFixed(2),
|
|
1261
|
+
})}
|
|
1457
1262
|
</Text>
|
|
1458
1263
|
</View>
|
|
1459
1264
|
</View>)}
|
|
@@ -1461,24 +1266,32 @@ const BundleTab = React.memo(() => {
|
|
|
1461
1266
|
{/* ══════════════════════════════════════════════════════════════════════ */}
|
|
1462
1267
|
{/* ── 4. TAB: MEDIA & ASSET AUDITOR ───────────────────────────────────── */}
|
|
1463
1268
|
{/* ══════════════════════════════════════════════════════════════════════ */}
|
|
1464
|
-
{activeSubTab === 'media' && (<View style={{ flex: 1 }}>
|
|
1269
|
+
{activeSubTab === 'media' && !isAnalyzing && (<View style={{ flex: 1 }}>
|
|
1465
1270
|
<ScrollView style={{ flex: 1 }} contentContainerStyle={bundleStyles.contentContainer} keyboardShouldPersistTaps="handled">
|
|
1466
1271
|
|
|
1467
1272
|
<View style={bundleStyles.tipsCard}>
|
|
1468
1273
|
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', marginBottom: 8 }}>
|
|
1469
1274
|
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
|
|
1470
1275
|
<StorageIcon color={AppColors.purple} size={18}/>
|
|
1471
|
-
<Text style={bundleStyles.tipsHeading}>
|
|
1276
|
+
<Text style={bundleStyles.tipsHeading}>{t('bundle.mediaAuditorTitle')}</Text>
|
|
1472
1277
|
</View>
|
|
1473
|
-
<CopyButton value={() => mediaFiles} label=
|
|
1278
|
+
<CopyButton value={() => mediaFiles} label={t('bundle.mediaAssetsJson')}/>
|
|
1474
1279
|
</View>
|
|
1475
1280
|
<Text style={bundleStyles.tipDesc}>
|
|
1476
|
-
|
|
1281
|
+
{t('bundle.mediaAuditorPrefix')}{' '}
|
|
1282
|
+
<Text style={{ fontFamily: AppFonts.interBold, color: AppColors.purple }}>
|
|
1283
|
+
{summary.images.pct + summary.fonts.pct}%
|
|
1284
|
+
</Text>{' '}
|
|
1285
|
+
{t('bundle.mediaAuditorMid', { kb: summary.images.kb + summary.fonts.kb })}{' '}
|
|
1286
|
+
<Text style={{ fontFamily: AppFonts.interBold, color: AppColors.emerald700 }}>
|
|
1287
|
+
{t('bundle.mediaSavings', { size: 540 })}
|
|
1288
|
+
</Text>{' '}
|
|
1289
|
+
{t('bundle.mediaAuditorSuffix')}
|
|
1477
1290
|
</Text>
|
|
1478
1291
|
</View>
|
|
1479
1292
|
|
|
1480
1293
|
<Text style={bundleStyles.listHeaderCount}>
|
|
1481
|
-
|
|
1294
|
+
{t('bundle.mediaAssetsList', { count: mediaFiles.length })}
|
|
1482
1295
|
</Text>
|
|
1483
1296
|
|
|
1484
1297
|
{mediaFiles.map((file, index) => (<View key={file.id} style={bundleStyles.fileCard}>
|
|
@@ -1496,9 +1309,11 @@ const BundleTab = React.memo(() => {
|
|
|
1496
1309
|
|
|
1497
1310
|
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
|
|
1498
1311
|
<View style={bundleStyles.fileSizeBox}>
|
|
1499
|
-
<Text style={bundleStyles.fileSizeKb}>
|
|
1312
|
+
<Text style={bundleStyles.fileSizeKb}>
|
|
1313
|
+
{t('bundle.fileSizeKb', { size: file.sizeKb })}
|
|
1314
|
+
</Text>
|
|
1500
1315
|
</View>
|
|
1501
|
-
<CopyButton value={() => file} label=
|
|
1316
|
+
<CopyButton value={() => file} label={t('bundle.mediaItemJson')}/>
|
|
1502
1317
|
</View>
|
|
1503
1318
|
</View>
|
|
1504
1319
|
|
|
@@ -1511,8 +1326,8 @@ const BundleTab = React.memo(() => {
|
|
|
1511
1326
|
]}>
|
|
1512
1327
|
<Text style={[
|
|
1513
1328
|
bundleStyles.adviceText,
|
|
1514
|
-
file.status === 'warning' && { color:
|
|
1515
|
-
file.status === 'optimal' && { color:
|
|
1329
|
+
file.status === 'warning' && { color: AppColors.amber800Warm },
|
|
1330
|
+
file.status === 'optimal' && { color: AppColors.emerald700 },
|
|
1516
1331
|
]}>
|
|
1517
1332
|
{file.status === 'warning' ? '💡 ' : '✨ '}
|
|
1518
1333
|
{file.advice}
|
|
@@ -1527,11 +1342,14 @@ const BundleTab = React.memo(() => {
|
|
|
1527
1342
|
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
|
|
1528
1343
|
<StorageIcon color={AppColors.purple} size={14}/>
|
|
1529
1344
|
<Text style={bundleStyles.footerTitle}>
|
|
1530
|
-
{mediaFiles.length}
|
|
1345
|
+
{t('bundle.mediaAndFonts', { count: mediaFiles.length })}
|
|
1531
1346
|
</Text>
|
|
1532
1347
|
</View>
|
|
1533
1348
|
<Text style={bundleStyles.footerSizeVal}>
|
|
1534
|
-
{
|
|
1349
|
+
{t('bundle.kbMbValue', {
|
|
1350
|
+
kb: mediaTotalKb,
|
|
1351
|
+
mb: (mediaTotalKb / 1024).toFixed(2),
|
|
1352
|
+
})}
|
|
1535
1353
|
</Text>
|
|
1536
1354
|
</View>
|
|
1537
1355
|
</View>)}
|
|
@@ -1545,49 +1363,49 @@ const BundleTab = React.memo(() => {
|
|
|
1545
1363
|
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', marginBottom: 12 }}>
|
|
1546
1364
|
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
|
|
1547
1365
|
<CircleAlertIcon color={AppColors.purple} size={18}/>
|
|
1548
|
-
<Text style={bundleStyles.tipsHeading}>
|
|
1366
|
+
<Text style={bundleStyles.tipsHeading}>{t('bundle.optimizerTitle')}</Text>
|
|
1549
1367
|
</View>
|
|
1550
1368
|
<CopyButton value={() => [
|
|
1551
|
-
'
|
|
1552
|
-
|
|
1553
|
-
'
|
|
1554
|
-
'
|
|
1555
|
-
'
|
|
1556
|
-
]} label=
|
|
1369
|
+
t('bundle.optTip1Title'),
|
|
1370
|
+
`${t('bundle.optTip2Title')} (${isHermes ? t('bundle.active') : t('bundle.actionRequired')})`,
|
|
1371
|
+
t('bundle.optTip3Title'),
|
|
1372
|
+
t('bundle.optTip4Title'),
|
|
1373
|
+
t('bundle.optTip5Title'),
|
|
1374
|
+
]} label={t('bundle.optimizationChecklist')}/>
|
|
1557
1375
|
</View>
|
|
1558
1376
|
|
|
1559
1377
|
{[
|
|
1560
1378
|
{
|
|
1561
|
-
title: '
|
|
1562
|
-
desc: '
|
|
1563
|
-
badge: '
|
|
1564
|
-
badgeColor:
|
|
1379
|
+
title: t('bundle.optTip1Title'),
|
|
1380
|
+
desc: t('bundle.optTip1Desc'),
|
|
1381
|
+
badge: t('bundle.highImpact'),
|
|
1382
|
+
badgeColor: AppColors.pink500,
|
|
1565
1383
|
},
|
|
1566
1384
|
{
|
|
1567
|
-
title: '
|
|
1385
|
+
title: t('bundle.optTip2Title'),
|
|
1568
1386
|
desc: isHermes
|
|
1569
|
-
? '
|
|
1570
|
-
: '
|
|
1571
|
-
badge: isHermes ? '
|
|
1572
|
-
badgeColor: isHermes ?
|
|
1387
|
+
? t('bundle.optTip2DescActive')
|
|
1388
|
+
: t('bundle.optTip2DescInactive'),
|
|
1389
|
+
badge: isHermes ? t('bundle.active') : t('bundle.actionRequired'),
|
|
1390
|
+
badgeColor: isHermes ? AppColors.emerald500 : AppColors.amber500,
|
|
1573
1391
|
},
|
|
1574
1392
|
{
|
|
1575
|
-
title: '
|
|
1576
|
-
desc: '
|
|
1577
|
-
badge: '
|
|
1578
|
-
badgeColor:
|
|
1393
|
+
title: t('bundle.optTip3Title'),
|
|
1394
|
+
desc: t('bundle.optTip3Desc'),
|
|
1395
|
+
badge: t('bundle.mediumImpact'),
|
|
1396
|
+
badgeColor: AppColors.purple500,
|
|
1579
1397
|
},
|
|
1580
1398
|
{
|
|
1581
|
-
title: '
|
|
1582
|
-
desc: '
|
|
1583
|
-
badge: '
|
|
1584
|
-
badgeColor:
|
|
1399
|
+
title: t('bundle.optTip4Title'),
|
|
1400
|
+
desc: t('bundle.optTip4Desc'),
|
|
1401
|
+
badge: t('bundle.bestPractice'),
|
|
1402
|
+
badgeColor: AppColors.sky500,
|
|
1585
1403
|
},
|
|
1586
1404
|
{
|
|
1587
|
-
title: '
|
|
1588
|
-
desc: '
|
|
1589
|
-
badge: '
|
|
1590
|
-
badgeColor:
|
|
1405
|
+
title: t('bundle.optTip5Title'),
|
|
1406
|
+
desc: t('bundle.optTip5Desc'),
|
|
1407
|
+
badge: t('bundle.bestPractice'),
|
|
1408
|
+
badgeColor: AppColors.indigo500,
|
|
1591
1409
|
},
|
|
1592
1410
|
].map((tip, tIdx) => (<View key={tIdx} style={bundleStyles.tipItem}>
|
|
1593
1411
|
<View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 2 }}>
|
|
@@ -1603,7 +1421,7 @@ const BundleTab = React.memo(() => {
|
|
|
1603
1421
|
{tip.badge}
|
|
1604
1422
|
</Text>
|
|
1605
1423
|
</View>
|
|
1606
|
-
<CopyButton value={() => tip} label=
|
|
1424
|
+
<CopyButton value={() => tip} label={t('bundle.tipDetails')}/>
|
|
1607
1425
|
</View>
|
|
1608
1426
|
</View>
|
|
1609
1427
|
<Text style={bundleStyles.tipDesc}>{tip.desc}</Text>
|
|
@@ -1619,6 +1437,23 @@ const bundleStyles = StyleSheet.create({
|
|
|
1619
1437
|
borderBottomWidth: 1,
|
|
1620
1438
|
borderBottomColor: AppColors.dividerColor,
|
|
1621
1439
|
},
|
|
1440
|
+
analyzingContainer: {
|
|
1441
|
+
flex: 1,
|
|
1442
|
+
alignItems: 'center',
|
|
1443
|
+
justifyContent: 'center',
|
|
1444
|
+
gap: 8,
|
|
1445
|
+
padding: 24,
|
|
1446
|
+
},
|
|
1447
|
+
analyzingText: {
|
|
1448
|
+
fontFamily: AppFonts.interBold,
|
|
1449
|
+
fontSize: 13,
|
|
1450
|
+
color: AppColors.primaryBlack,
|
|
1451
|
+
},
|
|
1452
|
+
analyzingHint: {
|
|
1453
|
+
fontFamily: AppFonts.interRegular,
|
|
1454
|
+
fontSize: 11,
|
|
1455
|
+
color: AppColors.grayTextWeak,
|
|
1456
|
+
},
|
|
1622
1457
|
subTabsScroll: {
|
|
1623
1458
|
paddingHorizontal: 12,
|
|
1624
1459
|
gap: 8,
|
|
@@ -2174,41 +2009,41 @@ const bundleStyles = StyleSheet.create({
|
|
|
2174
2009
|
paddingHorizontal: 7,
|
|
2175
2010
|
paddingVertical: 2,
|
|
2176
2011
|
borderRadius: 5,
|
|
2177
|
-
backgroundColor:
|
|
2012
|
+
backgroundColor: AppColors.npmRedTint,
|
|
2178
2013
|
borderWidth: 1,
|
|
2179
|
-
borderColor:
|
|
2014
|
+
borderColor: AppColors.npmRedBorder,
|
|
2180
2015
|
},
|
|
2181
2016
|
npmLinkText: {
|
|
2182
2017
|
fontFamily: AppFonts.interBold,
|
|
2183
2018
|
fontSize: 9.5,
|
|
2184
|
-
color:
|
|
2019
|
+
color: AppColors.npmRed,
|
|
2185
2020
|
},
|
|
2186
2021
|
deprecatedBadge: {
|
|
2187
2022
|
paddingHorizontal: 6,
|
|
2188
2023
|
paddingVertical: 1.5,
|
|
2189
2024
|
borderRadius: 4,
|
|
2190
|
-
backgroundColor:
|
|
2025
|
+
backgroundColor: AppColors.red100,
|
|
2191
2026
|
borderWidth: 1,
|
|
2192
|
-
borderColor:
|
|
2027
|
+
borderColor: AppColors.errorBorder,
|
|
2193
2028
|
},
|
|
2194
2029
|
deprecatedBadgeText: {
|
|
2195
2030
|
fontFamily: AppFonts.interBold,
|
|
2196
2031
|
fontSize: 9,
|
|
2197
|
-
color:
|
|
2032
|
+
color: AppColors.red600,
|
|
2198
2033
|
letterSpacing: 0.3,
|
|
2199
2034
|
},
|
|
2200
2035
|
updateBadge: {
|
|
2201
2036
|
paddingHorizontal: 6,
|
|
2202
2037
|
paddingVertical: 1.5,
|
|
2203
2038
|
borderRadius: 4,
|
|
2204
|
-
backgroundColor:
|
|
2039
|
+
backgroundColor: AppColors.green100,
|
|
2205
2040
|
borderWidth: 1,
|
|
2206
|
-
borderColor:
|
|
2041
|
+
borderColor: AppColors.greenBorder,
|
|
2207
2042
|
},
|
|
2208
2043
|
updateBadgeText: {
|
|
2209
2044
|
fontFamily: AppFonts.interBold,
|
|
2210
2045
|
fontSize: 9,
|
|
2211
|
-
color:
|
|
2046
|
+
color: AppColors.emerald700,
|
|
2212
2047
|
},
|
|
2213
2048
|
lastActiveText: {
|
|
2214
2049
|
fontFamily: AppFonts.interRegular,
|
|
@@ -2216,45 +2051,45 @@ const bundleStyles = StyleSheet.create({
|
|
|
2216
2051
|
color: AppColors.grayTextWeak,
|
|
2217
2052
|
},
|
|
2218
2053
|
deprecationBox: {
|
|
2219
|
-
backgroundColor:
|
|
2054
|
+
backgroundColor: AppColors.errorCardBg,
|
|
2220
2055
|
borderRadius: 6,
|
|
2221
2056
|
paddingHorizontal: 8,
|
|
2222
2057
|
paddingVertical: 5,
|
|
2223
2058
|
borderWidth: 1,
|
|
2224
|
-
borderColor:
|
|
2059
|
+
borderColor: AppColors.errorBorder,
|
|
2225
2060
|
marginTop: 3,
|
|
2226
2061
|
},
|
|
2227
2062
|
deprecationText: {
|
|
2228
2063
|
fontFamily: AppFonts.interMedium,
|
|
2229
2064
|
fontSize: 10.5,
|
|
2230
|
-
color:
|
|
2065
|
+
color: AppColors.rose700,
|
|
2231
2066
|
lineHeight: 14,
|
|
2232
2067
|
},
|
|
2233
2068
|
unusedBadge: {
|
|
2234
2069
|
paddingHorizontal: 6,
|
|
2235
2070
|
paddingVertical: 1.5,
|
|
2236
2071
|
borderRadius: 4,
|
|
2237
|
-
backgroundColor:
|
|
2072
|
+
backgroundColor: AppColors.red100,
|
|
2238
2073
|
borderWidth: 1,
|
|
2239
|
-
borderColor:
|
|
2074
|
+
borderColor: AppColors.errorBorder,
|
|
2240
2075
|
},
|
|
2241
2076
|
unusedBadgeText: {
|
|
2242
2077
|
fontFamily: AppFonts.interBold,
|
|
2243
2078
|
fontSize: 9.5,
|
|
2244
|
-
color:
|
|
2079
|
+
color: AppColors.red600,
|
|
2245
2080
|
},
|
|
2246
2081
|
consumedBadge: {
|
|
2247
2082
|
paddingHorizontal: 6,
|
|
2248
2083
|
paddingVertical: 1.5,
|
|
2249
2084
|
borderRadius: 4,
|
|
2250
|
-
backgroundColor:
|
|
2085
|
+
backgroundColor: AppColors.green100,
|
|
2251
2086
|
borderWidth: 1,
|
|
2252
|
-
borderColor:
|
|
2087
|
+
borderColor: AppColors.greenBorder,
|
|
2253
2088
|
},
|
|
2254
2089
|
consumedBadgeText: {
|
|
2255
2090
|
fontFamily: AppFonts.interBold,
|
|
2256
2091
|
fontSize: 9.5,
|
|
2257
|
-
color:
|
|
2092
|
+
color: AppColors.emerald600,
|
|
2258
2093
|
},
|
|
2259
2094
|
treeFolderChevronWrap: {
|
|
2260
2095
|
width: 18,
|
|
@@ -2265,13 +2100,13 @@ const bundleStyles = StyleSheet.create({
|
|
|
2265
2100
|
justifyContent: 'center',
|
|
2266
2101
|
},
|
|
2267
2102
|
treeFileCardUnused: {
|
|
2268
|
-
borderColor:
|
|
2269
|
-
backgroundColor:
|
|
2103
|
+
borderColor: AppColors.errorBorder,
|
|
2104
|
+
backgroundColor: AppColors.red50,
|
|
2270
2105
|
},
|
|
2271
2106
|
adviceUnused: {
|
|
2272
|
-
backgroundColor:
|
|
2107
|
+
backgroundColor: AppColors.errorCardBg,
|
|
2273
2108
|
borderWidth: 1,
|
|
2274
|
-
borderColor:
|
|
2109
|
+
borderColor: AppColors.errorBorder,
|
|
2275
2110
|
},
|
|
2276
2111
|
adviceBadge: {
|
|
2277
2112
|
paddingHorizontal: 8,
|
|
@@ -2280,14 +2115,14 @@ const bundleStyles = StyleSheet.create({
|
|
|
2280
2115
|
marginTop: 2,
|
|
2281
2116
|
},
|
|
2282
2117
|
adviceWarning: {
|
|
2283
|
-
backgroundColor:
|
|
2118
|
+
backgroundColor: AppColors.amber100,
|
|
2284
2119
|
borderWidth: 1,
|
|
2285
|
-
borderColor:
|
|
2120
|
+
borderColor: AppColors.amber200,
|
|
2286
2121
|
},
|
|
2287
2122
|
adviceOptimal: {
|
|
2288
|
-
backgroundColor:
|
|
2123
|
+
backgroundColor: AppColors.green100,
|
|
2289
2124
|
borderWidth: 1,
|
|
2290
|
-
borderColor:
|
|
2125
|
+
borderColor: AppColors.greenBorder,
|
|
2291
2126
|
},
|
|
2292
2127
|
adviceText: {
|
|
2293
2128
|
fontFamily: AppFonts.interMedium,
|
|
@@ -2305,21 +2140,21 @@ const bundleStyles = StyleSheet.create({
|
|
|
2305
2140
|
fontSize: 9,
|
|
2306
2141
|
},
|
|
2307
2142
|
searchHighlight: {
|
|
2308
|
-
backgroundColor:
|
|
2309
|
-
color:
|
|
2143
|
+
backgroundColor: AppColors.yellow200,
|
|
2144
|
+
color: AppColors.yellow800,
|
|
2310
2145
|
fontFamily: AppFonts.interBold,
|
|
2311
2146
|
},
|
|
2312
2147
|
tipsCard: {
|
|
2313
|
-
backgroundColor:
|
|
2148
|
+
backgroundColor: AppColors.purple50,
|
|
2314
2149
|
borderRadius: 14,
|
|
2315
2150
|
padding: 14,
|
|
2316
2151
|
borderWidth: 1,
|
|
2317
|
-
borderColor:
|
|
2152
|
+
borderColor: AppColors.purple200,
|
|
2318
2153
|
},
|
|
2319
2154
|
tipsHeading: {
|
|
2320
2155
|
fontFamily: AppFonts.interBold,
|
|
2321
2156
|
fontSize: 13.5,
|
|
2322
|
-
color:
|
|
2157
|
+
color: AppColors.purple800,
|
|
2323
2158
|
},
|
|
2324
2159
|
totalSummaryCard: {
|
|
2325
2160
|
marginTop: 10,
|
|
@@ -2383,17 +2218,17 @@ const bundleStyles = StyleSheet.create({
|
|
|
2383
2218
|
padding: 10,
|
|
2384
2219
|
borderRadius: 8,
|
|
2385
2220
|
borderWidth: 1,
|
|
2386
|
-
borderColor:
|
|
2221
|
+
borderColor: AppColors.purple200,
|
|
2387
2222
|
},
|
|
2388
2223
|
tipTitle: {
|
|
2389
2224
|
fontFamily: AppFonts.interBold,
|
|
2390
2225
|
fontSize: 12,
|
|
2391
|
-
color:
|
|
2226
|
+
color: AppColors.purple900,
|
|
2392
2227
|
},
|
|
2393
2228
|
tipDesc: {
|
|
2394
2229
|
fontFamily: AppFonts.interRegular,
|
|
2395
2230
|
fontSize: 11,
|
|
2396
|
-
color:
|
|
2231
|
+
color: AppColors.gray500,
|
|
2397
2232
|
lineHeight: 16,
|
|
2398
2233
|
marginTop: 4,
|
|
2399
2234
|
},
|