react-native-inapp-inspector 2.3.11 → 2.3.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/dist/commonjs/components/HeadersSection.js +3 -2
  2. package/dist/commonjs/components/Inspector/BundleTab.js +4 -4
  3. package/dist/commonjs/components/Inspector/DebuggingTab.js +29 -16
  4. package/dist/commonjs/components/Inspector/NetworkDetail.js +2 -2
  5. package/dist/commonjs/components/Inspector/NetworkFilterModal.js +18 -12
  6. package/dist/commonjs/components/Inspector/StorageTab.js +10 -4
  7. package/dist/commonjs/components/JsonViewer.js +3 -2
  8. package/dist/commonjs/components/NetworkIcons.d.ts +4 -1
  9. package/dist/commonjs/components/NetworkIcons.js +26 -3
  10. package/dist/commonjs/components/QRCodeView.js +15 -12
  11. package/dist/commonjs/constants/telemetryConfig.d.ts +3 -3
  12. package/dist/commonjs/constants/telemetryConfig.js +3 -3
  13. package/dist/commonjs/constants/version.d.ts +1 -1
  14. package/dist/commonjs/constants/version.js +1 -1
  15. package/dist/commonjs/helpers/qrGenerator.js +31 -157
  16. package/dist/esm/components/HeadersSection.js +3 -2
  17. package/dist/esm/components/Inspector/BundleTab.js +5 -5
  18. package/dist/esm/components/Inspector/DebuggingTab.js +30 -17
  19. package/dist/esm/components/Inspector/NetworkDetail.js +2 -2
  20. package/dist/esm/components/Inspector/NetworkFilterModal.js +19 -13
  21. package/dist/esm/components/Inspector/StorageTab.js +11 -5
  22. package/dist/esm/components/JsonViewer.js +4 -3
  23. package/dist/esm/components/NetworkIcons.d.ts +4 -1
  24. package/dist/esm/components/NetworkIcons.js +22 -2
  25. package/dist/esm/components/QRCodeView.js +15 -12
  26. package/dist/esm/constants/telemetryConfig.d.ts +3 -3
  27. package/dist/esm/constants/telemetryConfig.js +3 -3
  28. package/dist/esm/constants/version.d.ts +1 -1
  29. package/dist/esm/constants/version.js +1 -1
  30. package/dist/esm/helpers/qrGenerator.js +28 -157
  31. package/package.json +4 -2
  32. package/src/components/HeadersSection.tsx +7 -2
  33. package/src/components/Inspector/BundleTab.tsx +9 -4
  34. package/src/components/Inspector/DebuggingTab.tsx +37 -17
  35. package/src/components/Inspector/NetworkDetail.tsx +2 -2
  36. package/src/components/Inspector/NetworkFilterModal.tsx +32 -12
  37. package/src/components/Inspector/StorageTab.tsx +15 -4
  38. package/src/components/JsonViewer.tsx +8 -3
  39. package/src/components/NetworkIcons.tsx +71 -1
  40. package/src/components/QRCodeView.tsx +21 -17
  41. package/src/constants/telemetryConfig.ts +3 -5
  42. package/src/constants/version.ts +1 -1
  43. package/src/helpers/qrGenerator.ts +34 -169
  44. package/android/.gradle/8.9/checksums/checksums.lock +0 -0
  45. package/android/.gradle/8.9/checksums/md5-checksums.bin +0 -0
  46. package/android/.gradle/8.9/checksums/sha1-checksums.bin +0 -0
  47. package/android/.gradle/8.9/dependencies-accessors/gc.properties +0 -0
  48. package/android/.gradle/8.9/executionHistory/executionHistory.lock +0 -0
  49. package/android/.gradle/8.9/fileChanges/last-build.bin +0 -0
  50. package/android/.gradle/8.9/fileHashes/fileHashes.lock +0 -0
  51. package/android/.gradle/8.9/gc.properties +0 -0
  52. package/android/.gradle/9.2.0/checksums/checksums.lock +0 -0
  53. package/android/.gradle/9.2.0/fileChanges/last-build.bin +0 -0
  54. package/android/.gradle/9.2.0/fileHashes/fileHashes.bin +0 -0
  55. package/android/.gradle/9.2.0/fileHashes/fileHashes.lock +0 -0
  56. package/android/.gradle/9.2.0/gc.properties +0 -0
  57. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  58. package/android/.gradle/buildOutputCleanup/cache.properties +0 -2
  59. package/android/.gradle/vcs-1/gc.properties +0 -0
  60. package/android/build/reports/problems/problems-report.html +0 -659
@@ -1,167 +1,38 @@
1
+ import qrcode from 'qrcode-generator';
1
2
  export function generateQRMatrix(text, ecc = 'M') {
2
3
  try {
3
- return createQRMatrix(text, ecc);
4
- }
5
- catch {
6
- return createFallbackMatrix(text);
7
- }
8
- }
9
- function createQRMatrix(data, ecc) {
10
- const length = data.length;
11
- let version = 1;
12
- if (length > 14)
13
- version = 2;
14
- if (length > 26)
15
- version = 3;
16
- if (length > 42)
17
- version = 4;
18
- if (length > 62)
19
- version = 5;
20
- if (length > 84)
21
- version = 6;
22
- if (length > 106)
23
- version = 7;
24
- if (length > 122)
25
- version = 8;
26
- if (length > 152)
27
- version = 9;
28
- if (length > 180)
29
- version = 10;
30
- const size = version * 4 + 17;
31
- const matrix = Array(size)
32
- .fill(null)
33
- .map(() => Array(size).fill(null));
34
- addFinderPattern(matrix, 0, 0);
35
- addFinderPattern(matrix, size - 7, 0);
36
- addFinderPattern(matrix, 0, size - 7);
37
- for (let i = 8; i < size - 8; i++) {
38
- const val = i % 2 === 0;
39
- if (matrix[6][i] === null)
40
- matrix[6][i] = val;
41
- if (matrix[i][6] === null)
42
- matrix[i][6] = val;
43
- }
44
- if (version >= 2) {
45
- const pos = getAlignmentPositions(version);
46
- for (const r of pos) {
47
- for (const c of pos) {
48
- if (matrix[r][c] === null) {
49
- addAlignmentPattern(matrix, r - 2, c - 2);
50
- }
4
+ const qr = qrcode(0, ecc);
5
+ qr.addData(text);
6
+ qr.make();
7
+ const count = qr.getModuleCount();
8
+ const matrix = [];
9
+ for (let row = 0; row < count; row++) {
10
+ const rowData = [];
11
+ for (let col = 0; col < count; col++) {
12
+ rowData.push(qr.isDark(row, col));
51
13
  }
14
+ matrix.push(rowData);
52
15
  }
53
- }
54
- const bits = [];
55
- bits.push(false, true, false, false);
56
- for (let i = 7; i >= 0; i--) {
57
- bits.push(((length >> i) & 1) === 1);
58
- }
59
- for (let i = 0; i < length; i++) {
60
- const code = data.charCodeAt(i);
61
- for (let b = 7; b >= 0; b--) {
62
- bits.push(((code >> b) & 1) === 1);
63
- }
64
- }
65
- for (let i = 0; i < 4 && bits.length % 8 !== 0; i++) {
66
- bits.push(false);
67
- }
68
- while (bits.length % 8 !== 0) {
69
- bits.push(false);
70
- }
71
- const padBytes = [0xec, 0x11];
72
- let padIdx = 0;
73
- const totalDataBits = size * size * 0.6;
74
- while (bits.length < totalDataBits) {
75
- const b = padBytes[padIdx % 2];
76
- padIdx++;
77
- for (let bit = 7; bit >= 0; bit--) {
78
- bits.push(((b >> bit) & 1) === 1);
79
- }
80
- }
81
- let bitIdx = 0;
82
- let upwards = true;
83
- for (let col = size - 1; col > 0; col -= 2) {
84
- if (col === 6)
85
- col--;
86
- const rows = upwards
87
- ? Array.from({ length: size }, (_, i) => size - 1 - i)
88
- : Array.from({ length: size }, (_, i) => i);
89
- for (const row of rows) {
90
- for (const c of [col, col - 1]) {
91
- if (matrix[row][c] === null) {
92
- const bit = bitIdx < bits.length ? bits[bitIdx++] : false;
93
- const mask = (row + c) % 2 === 0;
94
- matrix[row][c] = bit !== mask;
16
+ return matrix;
17
+ }
18
+ catch (err) {
19
+ try {
20
+ const qr = qrcode(10, ecc);
21
+ qr.addData(text);
22
+ qr.make();
23
+ const count = qr.getModuleCount();
24
+ const matrix = [];
25
+ for (let row = 0; row < count; row++) {
26
+ const rowData = [];
27
+ for (let col = 0; col < count; col++) {
28
+ rowData.push(qr.isDark(row, col));
95
29
  }
30
+ matrix.push(rowData);
96
31
  }
32
+ return matrix;
97
33
  }
98
- upwards = !upwards;
99
- }
100
- return matrix.map(row => row.map(cell => cell ?? false));
101
- }
102
- function addFinderPattern(matrix, row, col) {
103
- for (let r = -1; r <= 7; r++) {
104
- for (let c = -1; c <= 7; c++) {
105
- const mr = row + r;
106
- const mc = col + c;
107
- if (mr >= 0 && mr < matrix.length && mc >= 0 && mc < matrix.length) {
108
- if ((r >= 0 && r <= 6 && (c === 0 || c === 6)) ||
109
- (c >= 0 && c <= 6 && (r === 0 || r === 6)) ||
110
- (r >= 2 && r <= 4 && c >= 2 && c <= 4)) {
111
- matrix[mr][mc] = true;
112
- }
113
- else if (r >= 0 && r <= 6 && c >= 0 && c <= 6) {
114
- matrix[mr][mc] = false;
115
- }
116
- else {
117
- matrix[mr][mc] = false;
118
- }
119
- }
34
+ catch {
35
+ return [];
120
36
  }
121
37
  }
122
38
  }
123
- function addAlignmentPattern(matrix, row, col) {
124
- for (let r = 0; r < 5; r++) {
125
- for (let c = 0; c < 5; c++) {
126
- const isOuter = r === 0 || r === 4 || c === 0 || c === 4;
127
- const isCenter = r === 2 && c === 2;
128
- matrix[row + r][col + c] = isOuter || isCenter;
129
- }
130
- }
131
- }
132
- function getAlignmentPositions(version) {
133
- if (version === 1)
134
- return [];
135
- if (version === 2)
136
- return [6, 18];
137
- if (version === 3)
138
- return [6, 22];
139
- if (version === 4)
140
- return [6, 26];
141
- if (version === 5)
142
- return [6, 30];
143
- if (version === 6)
144
- return [6, 34];
145
- if (version === 7)
146
- return [6, 22, 38];
147
- if (version === 8)
148
- return [6, 24, 42];
149
- if (version === 9)
150
- return [6, 26, 46];
151
- return [6, 28, 50];
152
- }
153
- function createFallbackMatrix(text) {
154
- const size = 25;
155
- const matrix = Array(size)
156
- .fill(false)
157
- .map(() => Array(size).fill(false));
158
- addFinderPattern(matrix, 0, 0);
159
- addFinderPattern(matrix, size - 7, 0);
160
- addFinderPattern(matrix, 0, size - 7);
161
- for (let i = 0; i < text.length; i++) {
162
- const r = (i * 3) % (size - 10) + 8;
163
- const c = (i * 7) % (size - 10) + 8;
164
- matrix[r][c] = true;
165
- }
166
- return matrix;
167
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-inapp-inspector",
3
- "version": "2.3.11",
3
+ "version": "2.3.13",
4
4
  "description": "The zero-config, all-in-one in-app debugger for React Native & Expo. Inspect Network (fetch/axios), Console logs, Stack Traces, Redux State, Firebase Analytics, and JS Bundle size directly on device.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -174,6 +174,8 @@
174
174
  "typescript": "5.8.3"
175
175
  },
176
176
  "dependencies": {
177
- "@react-native-clipboard/clipboard": "1.16.3"
177
+ "@react-native-clipboard/clipboard": "1.16.3",
178
+ "@types/qrcode-generator": "^0.0.16",
179
+ "qrcode-generator": "^2.0.4"
178
180
  }
179
181
  }
@@ -79,10 +79,15 @@ const HeaderRow = React.memo(function HeaderRow({
79
79
  {value}
80
80
  </Text>
81
81
  {isLong && (
82
- <View style={styles.htExpandRow}>
82
+ <View style={[styles.htExpandRow, {flexDirection: 'row', alignItems: 'center', gap: 4}]}>
83
83
  <Text style={styles.htExpandText}>
84
- {expanded ? 'less' : 'more'}
84
+ {expanded ? 'less' : 'more'}
85
85
  </Text>
86
+ <ChevronIcon
87
+ direction={expanded ? 'up' : 'down'}
88
+ color={AppColors.purple}
89
+ size={10}
90
+ />
86
91
  </View>
87
92
  )}
88
93
  </Pressable>
@@ -48,6 +48,7 @@ import {
48
48
  AppleIcon,
49
49
  AndroidIcon,
50
50
  DownloadIcon,
51
+ ChevronIcon,
51
52
  } from '../NetworkIcons';
52
53
 
53
54
  import Svg, {
@@ -808,9 +809,13 @@ const BundleTreeNodeView: React.FC<{
808
809
  onPress={() => toggleFolder(node.fullPath)}
809
810
  style={[bundleStyles.treeFolderRow, {paddingLeft: indentLeft}]}>
810
811
  <View style={bundleStyles.treeRowLeft}>
811
- <Text style={bundleStyles.treeChevron}>
812
- {isCollapsed ? '▸' : '▾'}
813
- </Text>
812
+ <View style={{marginRight: 4}}>
813
+ <ChevronIcon
814
+ direction={isCollapsed ? 'right' : 'down'}
815
+ size={12}
816
+ color={AppColors.grayTextWeak}
817
+ />
818
+ </View>
814
819
  {isCollapsed ? (
815
820
  <FolderIcon color={AppColors.amber500} size={15} />
816
821
  ) : (
@@ -1465,7 +1470,7 @@ const BundleTab = React.memo(() => {
1465
1470
  letterSpacing: 0.3,
1466
1471
  textTransform: 'uppercase',
1467
1472
  }}>
1468
- Live Metro URL
1473
+ Live Metro URL
1469
1474
  </Text>
1470
1475
  </View>
1471
1476
  <Text
@@ -31,6 +31,7 @@ import {
31
31
  ExternalLinkIcon,
32
32
  RepeatIcon,
33
33
  ShieldAlertIcon,
34
+ InfoCircleIcon,
34
35
  } from '../NetworkIcons';
35
36
 
36
37
  interface PortStatus {
@@ -137,14 +138,15 @@ export const DebuggingTab: React.FC = () => {
137
138
 
138
139
  // Construct target URLs for QR codes
139
140
  const metroBundleUrl = `http://${hostIp}:${selectedPort}/index.bundle?platform=android&dev=true`;
141
+ const directServerUrl = `http://${hostIp}:${selectedPort}`;
140
142
  const apkDownloadUrl = `http://${hostIp}:${selectedPort}/${apkPath}`;
141
143
  const devSettingsHost = `${hostIp}:${selectedPort}`;
142
144
 
143
145
  const currentQrValue = useMemo(() => {
144
146
  if (activeQrTab === 'apk') return apkDownloadUrl;
145
147
  if (activeQrTab === 'host') return devSettingsHost;
146
- return metroBundleUrl;
147
- }, [activeQrTab, apkDownloadUrl, devSettingsHost, metroBundleUrl]);
148
+ return directServerUrl;
149
+ }, [activeQrTab, apkDownloadUrl, devSettingsHost, directServerUrl]);
148
150
 
149
151
  const activePortInfo = portsStatus[selectedPort];
150
152
 
@@ -179,10 +181,10 @@ export const DebuggingTab: React.FC = () => {
179
181
  </View>
180
182
  <Text style={styles.serverSubtitle}>
181
183
  {activePortInfo?.status === 'active'
182
- ? `🟢 Active & Bundling (${activePortInfo.latencyMs}ms)`
184
+ ? `Active & Bundling (${activePortInfo.latencyMs}ms)`
183
185
  : activePortInfo?.status === 'checking'
184
- ? '🟡 Probing Metro Ports...'
185
- : '🔴 Standby / Select active port'}
186
+ ? 'Probing Metro Ports...'
187
+ : 'Standby / Select active port'}
186
188
  </Text>
187
189
  </View>
188
190
  </View>
@@ -272,9 +274,12 @@ export const DebuggingTab: React.FC = () => {
272
274
  />
273
275
  <Text style={styles.inputSuffix}>:{selectedPort}</Text>
274
276
  </View>
275
- <Text style={styles.inputHint}>
276
- 💡 Ensure your physical Android device and Mac are connected to the same Wi-Fi network.
277
- </Text>
277
+ <View style={{flexDirection: 'row', alignItems: 'center', gap: 6, marginTop: 6}}>
278
+ <InfoCircleIcon size={13} color={AppColors.grayTextWeak} />
279
+ <Text style={[styles.inputHint, {marginTop: 0, flex: 1}]}>
280
+ Ensure your physical Android device and Mac are connected to the same Wi-Fi network.
281
+ </Text>
282
+ </View>
278
283
  </View>
279
284
 
280
285
  {/* ─── Interactive QR Code Generator Card ──────────────────────── */}
@@ -352,13 +357,22 @@ export const DebuggingTab: React.FC = () => {
352
357
  />
353
358
 
354
359
  <View style={styles.qrInfoBox}>
355
- <Text style={styles.qrTargetTitle}>
356
- {activeQrTab === 'apk'
357
- ? '📦 Direct APK Download URL'
358
- : activeQrTab === 'host'
359
- ? '📱 Dev Settings Bundle Host'
360
- : '⚡ Live Metro Fast-Refresh Endpoint'}
361
- </Text>
360
+ <View style={{flexDirection: 'row', alignItems: 'center', gap: 5, marginBottom: 2}}>
361
+ {activeQrTab === 'apk' ? (
362
+ <PackageIcon size={14} color={AppColors.purple} />
363
+ ) : activeQrTab === 'host' ? (
364
+ <SmartphoneIcon size={14} color={AppColors.purple} />
365
+ ) : (
366
+ <BoltIcon size={14} color={AppColors.amber500} />
367
+ )}
368
+ <Text style={styles.qrTargetTitle}>
369
+ {activeQrTab === 'apk'
370
+ ? 'Direct APK Download URL'
371
+ : activeQrTab === 'host'
372
+ ? 'Dev Settings Bundle Host'
373
+ : 'Live Metro Fast-Refresh Endpoint'}
374
+ </Text>
375
+ </View>
362
376
  <Text style={styles.qrTargetUrl} numberOfLines={2} ellipsizeMode="middle">
363
377
  {currentQrValue}
364
378
  </Text>
@@ -388,7 +402,10 @@ export const DebuggingTab: React.FC = () => {
388
402
 
389
403
  {/* ─── Bare React Native Multi-Device Setup Guide ───────────────── */}
390
404
  <View style={styles.guideCard}>
391
- <Text style={styles.guideHeading}>📱 HOW TO CONNECT ANDROID DEVICE:</Text>
405
+ <View style={{flexDirection: 'row', alignItems: 'center', gap: 6, marginBottom: 12}}>
406
+ <SmartphoneIcon size={15} color={AppColors.primaryLight} />
407
+ <Text style={[styles.guideHeading, {marginBottom: 0}]}>HOW TO CONNECT ANDROID DEVICE:</Text>
408
+ </View>
392
409
 
393
410
  <View style={styles.stepRow}>
394
411
  <View style={styles.stepNumberBadge}>
@@ -419,7 +436,10 @@ export const DebuggingTab: React.FC = () => {
419
436
  <Text style={styles.stepNumberText}>3</Text>
420
437
  </View>
421
438
  <View style={{flex: 1}}>
422
- <Text style={styles.stepTitle}>Simultaneous Live Fast-Refresh ⚡</Text>
439
+ <View style={{flexDirection: 'row', alignItems: 'center', gap: 4}}>
440
+ <Text style={styles.stepTitle}>Simultaneous Live Fast-Refresh</Text>
441
+ <BoltIcon size={13} color={AppColors.amber500} />
442
+ </View>
423
443
  <Text style={styles.stepDesc}>
424
444
  Any changes saved in VS Code will immediately update <Text style={{fontWeight: '700'}}>both the iOS Simulator and physical Android phone</Text> in real-time!
425
445
  </Text>
@@ -432,7 +432,7 @@ const NetworkDetail = React.memo(() => {
432
432
  letterSpacing: 0.4,
433
433
  textTransform: 'uppercase',
434
434
  }}>
435
- ENDPOINT URL
435
+ ENDPOINT URL
436
436
  </Text>
437
437
  </View>
438
438
 
@@ -473,7 +473,7 @@ const NetworkDetail = React.memo(() => {
473
473
  fontSize: 9,
474
474
  color: AppColors.sky600,
475
475
  }}>
476
- Open
476
+ Open
477
477
  </Text>
478
478
  </View>
479
479
  </View>
@@ -27,6 +27,11 @@ import {
27
27
  BoltIcon,
28
28
  GlobeIcon,
29
29
  LockIcon,
30
+ UnlockIcon,
31
+ RocketIcon,
32
+ TurtleIcon,
33
+ HourglassIcon,
34
+ PackageIcon,
30
35
  } from '../NetworkIcons';
31
36
  import TouchableScale from '../TouchableScale';
32
37
 
@@ -405,12 +410,13 @@ export const NetworkFilterModal: React.FC<NetworkFilterModalProps> = ({
405
410
  <Text style={styles.sectionTitle}>LATENCY & SPEED</Text>
406
411
  <View style={styles.chipsWrap}>
407
412
  {[
408
- {key: 'all' as const, label: 'All Speeds', color: AppColors.purple},
409
- {key: 'fast' as const, label: 'Fast (<200ms)', color: AppColors.greenColor, count: counts.fastCount},
410
- {key: 'normal' as const, label: '⏱️ Normal (200-500ms)', color: AppColors.skyBlue, count: counts.normalCount},
411
- {key: 'slow' as const, label: '⚠️ Slow (>500ms)', color: AppColors.errorColor, count: counts.slowCount},
413
+ {key: 'all' as const, label: 'All Speeds', color: AppColors.purple, icon: LayersIcon},
414
+ {key: 'fast' as const, label: 'Fast (<200ms)', color: AppColors.greenColor, icon: BoltIcon, count: counts.fastCount},
415
+ {key: 'normal' as const, label: 'Normal (200-500ms)', color: AppColors.skyBlue, icon: ClockIcon, count: counts.normalCount},
416
+ {key: 'slow' as const, label: 'Slow (>500ms)', color: AppColors.errorColor, icon: CircleAlertIcon, count: counts.slowCount},
412
417
  ].map(item => {
413
418
  const isSelected = draft.latency === item.key;
419
+ const IconComp = item.icon;
414
420
  return (
415
421
  <TouchableOpacity
416
422
  key={item.key}
@@ -423,6 +429,10 @@ export const NetworkFilterModal: React.FC<NetworkFilterModalProps> = ({
423
429
  borderColor: item.color,
424
430
  },
425
431
  ]}>
432
+ <IconComp
433
+ size={13}
434
+ color={isSelected ? item.color : AppColors.grayTextWeak}
435
+ />
426
436
  <Text
427
437
  style={[
428
438
  styles.chipLabel,
@@ -448,11 +458,12 @@ export const NetworkFilterModal: React.FC<NetworkFilterModalProps> = ({
448
458
  <Text style={styles.sectionTitle}>PROTOCOL</Text>
449
459
  <View style={styles.chipsWrap}>
450
460
  {[
451
- {key: 'all' as const, label: 'All Protocols', color: AppColors.purple},
452
- {key: 'https' as const, label: '🔒 HTTPS Secure', color: AppColors.mintGreenText, count: counts.httpsCount},
453
- {key: 'http' as const, label: '🔓 HTTP Insecure', color: AppColors.amber700, count: counts.httpCount},
461
+ {key: 'all' as const, label: 'All Protocols', color: AppColors.purple, icon: GlobeIcon},
462
+ {key: 'https' as const, label: 'HTTPS Secure', color: AppColors.mintGreenText, icon: LockIcon, count: counts.httpsCount},
463
+ {key: 'http' as const, label: 'HTTP Insecure', color: AppColors.amber700, icon: UnlockIcon, count: counts.httpCount},
454
464
  ].map(item => {
455
465
  const isSelected = draft.protocol === item.key;
466
+ const IconComp = item.icon;
456
467
  return (
457
468
  <TouchableOpacity
458
469
  key={item.key}
@@ -465,6 +476,10 @@ export const NetworkFilterModal: React.FC<NetworkFilterModalProps> = ({
465
476
  borderColor: item.color,
466
477
  },
467
478
  ]}>
479
+ <IconComp
480
+ size={13}
481
+ color={isSelected ? item.color : AppColors.grayTextWeak}
482
+ />
468
483
  <Text
469
484
  style={[
470
485
  styles.chipLabel,
@@ -490,13 +505,14 @@ export const NetworkFilterModal: React.FC<NetworkFilterModalProps> = ({
490
505
  <Text style={styles.sectionTitle}>SORT BY</Text>
491
506
  <View style={styles.chipsWrap}>
492
507
  {[
493
- {key: 'time_desc' as const, label: '⏱️ Newest First (Default)'},
494
- {key: 'time_asc' as const, label: 'Oldest First'},
495
- {key: 'duration_desc' as const, label: '🐢 Slowest Response'},
496
- {key: 'duration_asc' as const, label: '🚀 Fastest Response'},
497
- {key: 'size_desc' as const, label: '📦 Largest Response Size'},
508
+ {key: 'time_desc' as const, label: 'Newest First (Default)', icon: ClockIcon},
509
+ {key: 'time_asc' as const, label: 'Oldest First', icon: HourglassIcon},
510
+ {key: 'duration_desc' as const, label: 'Slowest Response', icon: TurtleIcon},
511
+ {key: 'duration_asc' as const, label: 'Fastest Response', icon: RocketIcon},
512
+ {key: 'size_desc' as const, label: 'Largest Response Size', icon: PackageIcon},
498
513
  ].map(item => {
499
514
  const isSelected = draft.sortBy === item.key;
515
+ const IconComp = item.icon;
500
516
  return (
501
517
  <TouchableOpacity
502
518
  key={item.key}
@@ -509,6 +525,10 @@ export const NetworkFilterModal: React.FC<NetworkFilterModalProps> = ({
509
525
  borderColor: AppColors.purple,
510
526
  },
511
527
  ]}>
528
+ <IconComp
529
+ size={13}
530
+ color={isSelected ? AppColors.purple : AppColors.grayTextWeak}
531
+ />
512
532
  <Text
513
533
  style={[
514
534
  styles.chipLabel,
@@ -30,6 +30,7 @@ import {
30
30
  LayersIcon,
31
31
  ResetIcon,
32
32
  ChevronIcon,
33
+ WarningTriangleIcon,
33
34
  } from '../NetworkIcons';
34
35
  import {
35
36
  fetchStorageEntries,
@@ -162,9 +163,16 @@ const StorageEntryCard = React.memo(function StorageEntryCard({
162
163
  {displayValue}
163
164
  </Text>
164
165
  {entry.value.length > 80 && (
165
- <Text style={styles.expandHint}>
166
- {isExpanded ? '▲ Collapse' : '▼ Expand'}
167
- </Text>
166
+ <View style={{flexDirection: 'row', alignItems: 'center', gap: 4, marginTop: 4}}>
167
+ <Text style={styles.expandHint}>
168
+ {isExpanded ? 'Collapse' : 'Expand'}
169
+ </Text>
170
+ <ChevronIcon
171
+ direction={isExpanded ? 'up' : 'down'}
172
+ size={10}
173
+ color={AppColors.purple}
174
+ />
175
+ </View>
168
176
  )}
169
177
  </TouchableScale>
170
178
  </View>
@@ -710,7 +718,10 @@ export const StorageTab = React.memo(() => {
710
718
  autoCapitalize="none"
711
719
  />
712
720
  {jsonError && (
713
- <Text style={styles.errorText}>⚠️ {jsonError}</Text>
721
+ <View style={{flexDirection: 'row', alignItems: 'center', gap: 5, marginTop: 4}}>
722
+ <WarningTriangleIcon size={12} color={AppColors.errorColor} />
723
+ <Text style={styles.errorText}>{jsonError}</Text>
724
+ </View>
714
725
  )}
715
726
  </View>
716
727
 
@@ -7,7 +7,7 @@ import TreeNode from './TreeNode';
7
7
  import SegmentedTabs from './SegmentedTabs';
8
8
  import HighlightText from './HighlightText';
9
9
  import CopyButton from './CopyButton';
10
- import {PrettyIcon, RawIcon, TableIcon} from './NetworkIcons';
10
+ import {PrettyIcon, RawIcon, TableIcon, ChevronIcon} from './NetworkIcons';
11
11
 
12
12
  // Styles
13
13
  import {AppColors} from '../styles/AppColors';
@@ -77,10 +77,15 @@ const JsonTableRow = React.memo(({
77
77
  <TouchableOpacity
78
78
  onPress={() => setExpanded(prev => !prev)}
79
79
  hitSlop={6}
80
- style={localStyles.showMoreBtn}>
80
+ style={[localStyles.showMoreBtn, {flexDirection: 'row', alignItems: 'center', gap: 4}]}>
81
81
  <Text style={localStyles.showMoreText}>
82
- {expanded ? 'Show less' : 'Show more'}
82
+ {expanded ? 'Show less' : 'Show more'}
83
83
  </Text>
84
+ <ChevronIcon
85
+ direction={expanded ? 'up' : 'down'}
86
+ size={10}
87
+ color={AppColors.purple}
88
+ />
84
89
  </TouchableOpacity>
85
90
  )}
86
91
  </View>
@@ -570,11 +570,16 @@ export const SortArrowIcon = ({
570
570
  export const ChevronIcon = ({
571
571
  color = AppColors.grayTextWeak,
572
572
  size = 14,
573
+ direction = 'down',
573
574
  }: IconProps) => {
575
+ let path = 'M6 9l6 6 6-6';
576
+ if (direction === 'up') path = 'M18 15l-6-6-6 6';
577
+ else if (direction === 'right') path = 'M9 18l6-6-6-6';
578
+ else if (direction === 'left') path = 'M15 18l-6-6 6-6';
574
579
  return (
575
580
  <Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
576
581
  <Path
577
- d="M6 9l6 6 6-6"
582
+ d={path}
578
583
  stroke={color}
579
584
  strokeWidth="2.5"
580
585
  strokeLinecap="round"
@@ -2285,3 +2290,68 @@ export const QrCodeIcon = ({
2285
2290
  </Svg>
2286
2291
  );
2287
2292
 
2293
+ export const UnlockIcon = ({
2294
+ color = AppColors.grayTextWeak,
2295
+ size = 14,
2296
+ }: IconProps) => (
2297
+ <Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
2298
+ <Rect
2299
+ x="3"
2300
+ y="11"
2301
+ width="18"
2302
+ height="11"
2303
+ rx="2"
2304
+ ry="2"
2305
+ stroke={color}
2306
+ strokeWidth="2"
2307
+ strokeLinecap="round"
2308
+ strokeLinejoin="round"
2309
+ />
2310
+ <Path
2311
+ d="M7 11V7a5 5 0 0 1 9.9-1"
2312
+ stroke={color}
2313
+ strokeWidth="2"
2314
+ strokeLinecap="round"
2315
+ strokeLinejoin="round"
2316
+ />
2317
+ </Svg>
2318
+ );
2319
+
2320
+ export const RocketIcon = ({
2321
+ color = AppColors.grayTextWeak,
2322
+ size = 14,
2323
+ }: IconProps) => (
2324
+ <Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
2325
+ <Path
2326
+ d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09zM12 15l-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6.05 11a22.35 22.35 0 0 1-3.95 2z"
2327
+ stroke={color}
2328
+ strokeWidth="2"
2329
+ strokeLinecap="round"
2330
+ strokeLinejoin="round"
2331
+ />
2332
+ <Path
2333
+ d="M9 12l-4 4M15 6l-3 3"
2334
+ stroke={color}
2335
+ strokeWidth="2"
2336
+ strokeLinecap="round"
2337
+ strokeLinejoin="round"
2338
+ />
2339
+ </Svg>
2340
+ );
2341
+
2342
+ export const TurtleIcon = ({
2343
+ color = AppColors.grayTextWeak,
2344
+ size = 14,
2345
+ }: IconProps) => (
2346
+ <Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
2347
+ <Ellipse cx="12" cy="14" rx="7" ry="5" stroke={color} strokeWidth="2" />
2348
+ <Circle cx="12" cy="6" r="3" stroke={color} strokeWidth="2" />
2349
+ <Path
2350
+ d="M5 12l-2-2M19 12l2-2M6 18l-2 2M18 18l2 2"
2351
+ stroke={color}
2352
+ strokeWidth="2"
2353
+ strokeLinecap="round"
2354
+ />
2355
+ </Svg>
2356
+ );
2357
+