react-native-inapp-inspector 2.3.10 → 2.3.12

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 (43) hide show
  1. package/dist/commonjs/components/Inspector/DebuggingTab.d.ts +3 -0
  2. package/dist/commonjs/components/Inspector/DebuggingTab.js +683 -0
  3. package/dist/commonjs/components/Inspector/MainScreen.js +2 -0
  4. package/dist/commonjs/components/Inspector/SettingsPanel.js +12 -0
  5. package/dist/commonjs/components/Inspector/TabBar.js +9 -0
  6. package/dist/commonjs/components/NetworkIcons.d.ts +1 -0
  7. package/dist/commonjs/components/NetworkIcons.js +8 -1
  8. package/dist/commonjs/components/QRCodeView.d.ts +9 -0
  9. package/dist/commonjs/components/QRCodeView.js +82 -0
  10. package/dist/commonjs/constants/version.d.ts +1 -1
  11. package/dist/commonjs/constants/version.js +1 -1
  12. package/dist/commonjs/helpers/qrGenerator.d.ts +2 -0
  13. package/dist/commonjs/helpers/qrGenerator.js +44 -0
  14. package/dist/commonjs/index.js +2 -0
  15. package/dist/commonjs/types/enums.d.ts +2 -0
  16. package/dist/commonjs/types/enums.js +2 -0
  17. package/dist/esm/components/Inspector/DebuggingTab.d.ts +3 -0
  18. package/dist/esm/components/Inspector/DebuggingTab.js +643 -0
  19. package/dist/esm/components/Inspector/MainScreen.js +2 -0
  20. package/dist/esm/components/Inspector/SettingsPanel.js +13 -1
  21. package/dist/esm/components/Inspector/TabBar.js +10 -1
  22. package/dist/esm/components/NetworkIcons.d.ts +1 -0
  23. package/dist/esm/components/NetworkIcons.js +6 -0
  24. package/dist/esm/components/QRCodeView.d.ts +9 -0
  25. package/dist/esm/components/QRCodeView.js +45 -0
  26. package/dist/esm/constants/version.d.ts +1 -1
  27. package/dist/esm/constants/version.js +1 -1
  28. package/dist/esm/helpers/qrGenerator.d.ts +2 -0
  29. package/dist/esm/helpers/qrGenerator.js +38 -0
  30. package/dist/esm/index.js +2 -0
  31. package/dist/esm/types/enums.d.ts +2 -0
  32. package/dist/esm/types/enums.js +2 -0
  33. package/package.json +4 -2
  34. package/src/components/Inspector/DebuggingTab.tsx +756 -0
  35. package/src/components/Inspector/MainScreen.tsx +2 -0
  36. package/src/components/Inspector/SettingsPanel.tsx +20 -0
  37. package/src/components/Inspector/TabBar.tsx +11 -0
  38. package/src/components/NetworkIcons.tsx +39 -0
  39. package/src/components/QRCodeView.tsx +75 -0
  40. package/src/constants/version.ts +1 -1
  41. package/src/helpers/qrGenerator.ts +49 -0
  42. package/src/index.tsx +2 -0
  43. package/src/types/enums.ts +2 -0
@@ -0,0 +1,756 @@
1
+ import React, {useState, useEffect, useMemo, useCallback} from 'react';
2
+ import {
3
+ View,
4
+ Text,
5
+ ScrollView,
6
+ StyleSheet,
7
+ TextInput,
8
+ TouchableOpacity,
9
+ NativeModules,
10
+ Platform,
11
+ Share,
12
+ } from 'react-native';
13
+ import {useInspector} from './InspectorContext';
14
+ import {AppColors} from '../../styles/AppColors';
15
+ import {AppFonts} from '../../styles/AppFonts';
16
+ import QRCodeView from '../QRCodeView';
17
+ import TouchableScale from '../TouchableScale';
18
+ import CopyButton from '../CopyButton';
19
+ import {copyToClipboard} from '../../helpers';
20
+ import {showToast} from '../../helpers/toast';
21
+ import {useTranslation} from '../../i18n';
22
+ import {
23
+ BoltIcon,
24
+ GlobeIcon,
25
+ ClockIcon,
26
+ CircleCheckIcon,
27
+ CircleXIcon,
28
+ SmartphoneIcon,
29
+ PackageIcon,
30
+ TerminalIcon,
31
+ ExternalLinkIcon,
32
+ RepeatIcon,
33
+ ShieldAlertIcon,
34
+ } from '../NetworkIcons';
35
+
36
+ interface PortStatus {
37
+ port: number;
38
+ status: 'checking' | 'active' | 'inactive';
39
+ latencyMs?: number;
40
+ }
41
+
42
+ const COMMON_PORTS = [8081, 8082, 8083, 19000];
43
+
44
+ export const DebuggingTab: React.FC = () => {
45
+ const {t} = useTranslation();
46
+
47
+ // Auto-detect host IP and initial port from NativeModules.SourceCode.scriptURL
48
+ const detectedConfig = useMemo(() => {
49
+ const scriptURL = NativeModules.SourceCode?.scriptURL || '';
50
+ let host = 'localhost';
51
+ let port = 8081;
52
+
53
+ try {
54
+ if (scriptURL.includes('://')) {
55
+ const urlWithoutScheme = scriptURL.split('://')[1];
56
+ const hostAndPort = urlWithoutScheme.split('/')[0];
57
+ if (hostAndPort.includes(':')) {
58
+ const parts = hostAndPort.split(':');
59
+ host = parts[0];
60
+ const parsedPort = parseInt(parts[1], 10);
61
+ if (!isNaN(parsedPort)) port = parsedPort;
62
+ } else {
63
+ host = hostAndPort;
64
+ }
65
+ }
66
+ } catch {}
67
+
68
+ return {host, port, scriptURL};
69
+ }, []);
70
+
71
+ const [hostIp, setHostIp] = useState<string>(() => {
72
+ // If localhost or 127.0.0.1 or 10.0.2.2, default to 192.168.1.1 or detected host
73
+ if (
74
+ detectedConfig.host === 'localhost' ||
75
+ detectedConfig.host === '127.0.0.1' ||
76
+ detectedConfig.host === '10.0.2.2'
77
+ ) {
78
+ return '192.168.1.15';
79
+ }
80
+ return detectedConfig.host;
81
+ });
82
+
83
+ const [selectedPort, setSelectedPort] = useState<number>(detectedConfig.port);
84
+ const [apkPath, setApkPath] = useState<string>('app-debug.apk');
85
+ const [activeQrTab, setActiveQrTab] = useState<'metro' | 'apk' | 'host'>('metro');
86
+ const [isAutoDetecting, setIsAutoDetecting] = useState<boolean>(false);
87
+ const [portsStatus, setPortsStatus] = useState<Record<number, PortStatus>>({});
88
+
89
+ // Auto-probe ports on mount
90
+ const probePorts = useCallback(async () => {
91
+ setIsAutoDetecting(true);
92
+ const results: Record<number, PortStatus> = {};
93
+
94
+ for (const p of COMMON_PORTS) {
95
+ results[p] = {port: p, status: 'checking'};
96
+ }
97
+ setPortsStatus({...results});
98
+
99
+ let foundActivePort: number | null = null;
100
+
101
+ for (const p of COMMON_PORTS) {
102
+ const startTime = Date.now();
103
+ try {
104
+ const controller = new AbortController();
105
+ const timeoutId = setTimeout(() => controller.abort(), 1500);
106
+ // Ping /status endpoint of Metro Bundler
107
+ const res = await fetch(`http://localhost:${p}/status`, {
108
+ signal: controller.signal,
109
+ });
110
+ clearTimeout(timeoutId);
111
+ const text = await res.text();
112
+ const latency = Date.now() - startTime;
113
+
114
+ if (res.ok && text.includes('packager-status:running')) {
115
+ results[p] = {port: p, status: 'active', latencyMs: latency};
116
+ if (!foundActivePort) foundActivePort = p;
117
+ } else {
118
+ results[p] = {port: p, status: 'inactive'};
119
+ }
120
+ } catch {
121
+ results[p] = {port: p, status: 'inactive'};
122
+ }
123
+ setPortsStatus({...results});
124
+ }
125
+
126
+ if (foundActivePort) {
127
+ setSelectedPort(foundActivePort);
128
+ showToast(`Detected Metro running on Port ${foundActivePort}!`);
129
+ }
130
+
131
+ setIsAutoDetecting(false);
132
+ }, []);
133
+
134
+ useEffect(() => {
135
+ probePorts();
136
+ }, [probePorts]);
137
+
138
+ // Construct target URLs for QR codes
139
+ const metroBundleUrl = `http://${hostIp}:${selectedPort}/index.bundle?platform=android&dev=true`;
140
+ const directServerUrl = `http://${hostIp}:${selectedPort}`;
141
+ const apkDownloadUrl = `http://${hostIp}:${selectedPort}/${apkPath}`;
142
+ const devSettingsHost = `${hostIp}:${selectedPort}`;
143
+
144
+ const currentQrValue = useMemo(() => {
145
+ if (activeQrTab === 'apk') return apkDownloadUrl;
146
+ if (activeQrTab === 'host') return devSettingsHost;
147
+ return directServerUrl;
148
+ }, [activeQrTab, apkDownloadUrl, devSettingsHost, directServerUrl]);
149
+
150
+ const activePortInfo = portsStatus[selectedPort];
151
+
152
+ return (
153
+ <ScrollView
154
+ style={styles.container}
155
+ contentContainerStyle={styles.content}
156
+ showsVerticalScrollIndicator={false}>
157
+ {/* ─── Top Live Server Status Banner ────────────────────────────── */}
158
+ <View style={styles.serverCard}>
159
+ <View style={styles.serverCardTop}>
160
+ <View style={styles.serverStatusLeft}>
161
+ <View
162
+ style={[
163
+ styles.statusDot,
164
+ {
165
+ backgroundColor:
166
+ activePortInfo?.status === 'active'
167
+ ? AppColors.greenColor
168
+ : activePortInfo?.status === 'checking'
169
+ ? AppColors.warningIconGold
170
+ : AppColors.errorColor,
171
+ },
172
+ ]}
173
+ />
174
+ <View>
175
+ <View style={{flexDirection: 'row', alignItems: 'center', gap: 6}}>
176
+ <Text style={styles.serverTitle}>Metro Dev Server</Text>
177
+ <View style={styles.portPill}>
178
+ <Text style={styles.portPillText}>Port {selectedPort}</Text>
179
+ </View>
180
+ </View>
181
+ <Text style={styles.serverSubtitle}>
182
+ {activePortInfo?.status === 'active'
183
+ ? `🟢 Active & Bundling (${activePortInfo.latencyMs}ms)`
184
+ : activePortInfo?.status === 'checking'
185
+ ? '🟡 Probing Metro Ports...'
186
+ : '🔴 Standby / Select active port'}
187
+ </Text>
188
+ </View>
189
+ </View>
190
+
191
+ <TouchableScale
192
+ onPress={probePorts}
193
+ style={styles.scanBtn}
194
+ hitSlop={8}>
195
+ <RepeatIcon size={12} color={AppColors.purple} />
196
+ <Text style={styles.scanBtnText}>Re-scan</Text>
197
+ </TouchableScale>
198
+ </View>
199
+
200
+ {/* Port Selector Chips */}
201
+ <View style={styles.portsRow}>
202
+ {COMMON_PORTS.map(p => {
203
+ const isSelected = selectedPort === p;
204
+ const pInfo = portsStatus[p];
205
+ const isActive = pInfo?.status === 'active';
206
+ return (
207
+ <TouchableOpacity
208
+ key={p}
209
+ activeOpacity={0.7}
210
+ onPress={() => setSelectedPort(p)}
211
+ style={[
212
+ styles.portChip,
213
+ isSelected && styles.portChipSelected,
214
+ isActive && {borderColor: AppColors.greenColor},
215
+ ]}>
216
+ <View
217
+ style={[
218
+ styles.portMiniDot,
219
+ {
220
+ backgroundColor: isActive
221
+ ? AppColors.greenColor
222
+ : AppColors.grayTextWeak,
223
+ },
224
+ ]}
225
+ />
226
+ <Text
227
+ style={[
228
+ styles.portChipText,
229
+ isSelected && styles.portChipTextSelected,
230
+ ]}>
231
+ {p}
232
+ </Text>
233
+ {isActive && (
234
+ <View style={styles.liveBadge}>
235
+ <Text style={styles.liveBadgeText}>LIVE</Text>
236
+ </View>
237
+ )}
238
+ </TouchableOpacity>
239
+ );
240
+ })}
241
+ </View>
242
+ </View>
243
+
244
+ {/* ─── Host IP Configuration Card ───────────────────────────────── */}
245
+ <View style={styles.card}>
246
+ <View style={styles.cardHeader}>
247
+ <Text style={styles.cardTitle}>HOST IP & BUNDLE LOCATION</Text>
248
+ <TouchableOpacity
249
+ onPress={() => {
250
+ if (
251
+ detectedConfig.host !== 'localhost' &&
252
+ detectedConfig.host !== '127.0.0.1' &&
253
+ detectedConfig.host !== '10.0.2.2'
254
+ ) {
255
+ setHostIp(detectedConfig.host);
256
+ showToast(`Reset to detected IP: ${detectedConfig.host}`);
257
+ }
258
+ }}>
259
+ <Text style={styles.headerActionText}>Reset to Detected</Text>
260
+ </TouchableOpacity>
261
+ </View>
262
+
263
+ <View style={styles.inputContainer}>
264
+ <Text style={styles.inputPrefix}>http://</Text>
265
+ <TextInput
266
+ style={styles.input}
267
+ value={hostIp}
268
+ onChangeText={setHostIp}
269
+ placeholder="192.168.1.15"
270
+ placeholderTextColor={AppColors.grayTextWeak}
271
+ autoCapitalize="none"
272
+ autoCorrect={false}
273
+ />
274
+ <Text style={styles.inputSuffix}>:{selectedPort}</Text>
275
+ </View>
276
+ <Text style={styles.inputHint}>
277
+ 💡 Ensure your physical Android device and Mac are connected to the same Wi-Fi network.
278
+ </Text>
279
+ </View>
280
+
281
+ {/* ─── Interactive QR Code Generator Card ──────────────────────── */}
282
+ <View style={styles.qrCard}>
283
+ {/* Mode Selector Tabs */}
284
+ <View style={styles.qrTabsRow}>
285
+ <TouchableOpacity
286
+ activeOpacity={0.7}
287
+ onPress={() => setActiveQrTab('metro')}
288
+ style={[
289
+ styles.qrTab,
290
+ activeQrTab === 'metro' && styles.qrTabActive,
291
+ ]}>
292
+ <BoltIcon
293
+ size={13}
294
+ color={activeQrTab === 'metro' ? AppColors.white : AppColors.grayText}
295
+ />
296
+ <Text
297
+ style={[
298
+ styles.qrTabText,
299
+ activeQrTab === 'metro' && styles.qrTabTextActive,
300
+ ]}>
301
+ Metro Live Reload
302
+ </Text>
303
+ </TouchableOpacity>
304
+
305
+ <TouchableOpacity
306
+ activeOpacity={0.7}
307
+ onPress={() => setActiveQrTab('apk')}
308
+ style={[
309
+ styles.qrTab,
310
+ activeQrTab === 'apk' && styles.qrTabActive,
311
+ ]}>
312
+ <PackageIcon
313
+ size={13}
314
+ color={activeQrTab === 'apk' ? AppColors.white : AppColors.grayText}
315
+ />
316
+ <Text
317
+ style={[
318
+ styles.qrTabText,
319
+ activeQrTab === 'apk' && styles.qrTabTextActive,
320
+ ]}>
321
+ Install Debug APK
322
+ </Text>
323
+ </TouchableOpacity>
324
+
325
+ <TouchableOpacity
326
+ activeOpacity={0.7}
327
+ onPress={() => setActiveQrTab('host')}
328
+ style={[
329
+ styles.qrTab,
330
+ activeQrTab === 'host' && styles.qrTabActive,
331
+ ]}>
332
+ <SmartphoneIcon
333
+ size={13}
334
+ color={activeQrTab === 'host' ? AppColors.white : AppColors.grayText}
335
+ />
336
+ <Text
337
+ style={[
338
+ styles.qrTabText,
339
+ activeQrTab === 'host' && styles.qrTabTextActive,
340
+ ]}>
341
+ Dev Host Info
342
+ </Text>
343
+ </TouchableOpacity>
344
+ </View>
345
+
346
+ {/* QR Code Container */}
347
+ <View style={styles.qrWrapper}>
348
+ <QRCodeView
349
+ value={currentQrValue}
350
+ size={190}
351
+ color={AppColors.primaryBlack}
352
+ backgroundColor={AppColors.white}
353
+ />
354
+
355
+ <View style={styles.qrInfoBox}>
356
+ <Text style={styles.qrTargetTitle}>
357
+ {activeQrTab === 'apk'
358
+ ? '📦 Direct APK Download URL'
359
+ : activeQrTab === 'host'
360
+ ? '📱 Dev Settings Bundle Host'
361
+ : '⚡ Live Metro Fast-Refresh Endpoint'}
362
+ </Text>
363
+ <Text style={styles.qrTargetUrl} numberOfLines={2} ellipsizeMode="middle">
364
+ {currentQrValue}
365
+ </Text>
366
+ </View>
367
+ </View>
368
+
369
+ {/* Quick Action Row */}
370
+ <View style={styles.qrActionsRow}>
371
+ <TouchableScale
372
+ onPress={() => copyToClipboard(currentQrValue, 'QR Link')}
373
+ style={styles.actionBtn}>
374
+ <Text style={styles.actionBtnText}>Copy Link</Text>
375
+ </TouchableScale>
376
+
377
+ <TouchableScale
378
+ onPress={() => {
379
+ Share.share({
380
+ message: currentQrValue,
381
+ title: 'Metro Debug Link',
382
+ });
383
+ }}
384
+ style={[styles.actionBtn, styles.actionBtnPrimary]}>
385
+ <Text style={styles.actionBtnPrimaryText}>Share to Device</Text>
386
+ </TouchableScale>
387
+ </View>
388
+ </View>
389
+
390
+ {/* ─── Bare React Native Multi-Device Setup Guide ───────────────── */}
391
+ <View style={styles.guideCard}>
392
+ <Text style={styles.guideHeading}>📱 HOW TO CONNECT ANDROID DEVICE:</Text>
393
+
394
+ <View style={styles.stepRow}>
395
+ <View style={styles.stepNumberBadge}>
396
+ <Text style={styles.stepNumberText}>1</Text>
397
+ </View>
398
+ <View style={{flex: 1}}>
399
+ <Text style={styles.stepTitle}>Connect to Same Wi-Fi</Text>
400
+ <Text style={styles.stepDesc}>
401
+ Make sure your Android phone is connected to the same Wi-Fi router or Mac Mobile Hotspot.
402
+ </Text>
403
+ </View>
404
+ </View>
405
+
406
+ <View style={styles.stepRow}>
407
+ <View style={styles.stepNumberBadge}>
408
+ <Text style={styles.stepNumberText}>2</Text>
409
+ </View>
410
+ <View style={{flex: 1}}>
411
+ <Text style={styles.stepTitle}>Scan to Install / Connect</Text>
412
+ <Text style={styles.stepDesc}>
413
+ Open Android Camera or browser, scan the QR code above to download the APK or connect Metro.
414
+ </Text>
415
+ </View>
416
+ </View>
417
+
418
+ <View style={styles.stepRow}>
419
+ <View style={styles.stepNumberBadge}>
420
+ <Text style={styles.stepNumberText}>3</Text>
421
+ </View>
422
+ <View style={{flex: 1}}>
423
+ <Text style={styles.stepTitle}>Simultaneous Live Fast-Refresh ⚡</Text>
424
+ <Text style={styles.stepDesc}>
425
+ 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!
426
+ </Text>
427
+ </View>
428
+ </View>
429
+ </View>
430
+ </ScrollView>
431
+ );
432
+ };
433
+
434
+ const styles = StyleSheet.create({
435
+ container: {
436
+ flex: 1,
437
+ backgroundColor: AppColors.contentBg,
438
+ },
439
+ content: {
440
+ padding: 12,
441
+ gap: 12,
442
+ paddingBottom: 40,
443
+ },
444
+ serverCard: {
445
+ backgroundColor: AppColors.primaryLight,
446
+ borderRadius: 14,
447
+ padding: 14,
448
+ borderWidth: 1,
449
+ borderColor: AppColors.grayBorderSecondary,
450
+ shadowColor: AppColors.black,
451
+ shadowOffset: {width: 0, height: 2},
452
+ shadowOpacity: 0.05,
453
+ shadowRadius: 4,
454
+ elevation: 2,
455
+ gap: 12,
456
+ },
457
+ serverCardTop: {
458
+ flexDirection: 'row',
459
+ alignItems: 'center',
460
+ justifyContent: 'space-between',
461
+ },
462
+ serverStatusLeft: {
463
+ flexDirection: 'row',
464
+ alignItems: 'center',
465
+ gap: 10,
466
+ flex: 1,
467
+ },
468
+ statusDot: {
469
+ width: 12,
470
+ height: 12,
471
+ borderRadius: 6,
472
+ },
473
+ serverTitle: {
474
+ fontFamily: AppFonts.interBold,
475
+ fontSize: 15,
476
+ color: AppColors.primaryBlack,
477
+ },
478
+ serverSubtitle: {
479
+ fontFamily: AppFonts.interMedium,
480
+ fontSize: 11,
481
+ color: AppColors.grayText,
482
+ marginTop: 2,
483
+ },
484
+ portPill: {
485
+ backgroundColor: `${AppColors.purple}18`,
486
+ paddingHorizontal: 6,
487
+ paddingVertical: 2,
488
+ borderRadius: 6,
489
+ },
490
+ portPillText: {
491
+ fontFamily: AppFonts.interBold,
492
+ fontSize: 10,
493
+ color: AppColors.purple,
494
+ },
495
+ scanBtn: {
496
+ flexDirection: 'row',
497
+ alignItems: 'center',
498
+ gap: 4,
499
+ paddingHorizontal: 8,
500
+ paddingVertical: 4,
501
+ borderRadius: 6,
502
+ backgroundColor: `${AppColors.purple}14`,
503
+ borderWidth: 1,
504
+ borderColor: `${AppColors.purple}30`,
505
+ },
506
+ scanBtnText: {
507
+ fontFamily: AppFonts.interBold,
508
+ fontSize: 11,
509
+ color: AppColors.purple,
510
+ },
511
+ portsRow: {
512
+ flexDirection: 'row',
513
+ gap: 8,
514
+ },
515
+ portChip: {
516
+ flex: 1,
517
+ flexDirection: 'row',
518
+ alignItems: 'center',
519
+ justifyContent: 'center',
520
+ gap: 4,
521
+ paddingVertical: 7,
522
+ borderRadius: 8,
523
+ backgroundColor: AppColors.grayBackground,
524
+ borderWidth: 1,
525
+ borderColor: AppColors.grayBorderSecondary,
526
+ },
527
+ portChipSelected: {
528
+ backgroundColor: `${AppColors.purple}18`,
529
+ borderColor: AppColors.purple,
530
+ },
531
+ portMiniDot: {
532
+ width: 5,
533
+ height: 5,
534
+ borderRadius: 2.5,
535
+ },
536
+ portChipText: {
537
+ fontFamily: AppFonts.interBold,
538
+ fontSize: 11,
539
+ color: AppColors.grayTextStrong,
540
+ },
541
+ portChipTextSelected: {
542
+ color: AppColors.purple,
543
+ },
544
+ liveBadge: {
545
+ backgroundColor: AppColors.greenColor,
546
+ paddingHorizontal: 3,
547
+ paddingVertical: 0.5,
548
+ borderRadius: 3,
549
+ },
550
+ liveBadgeText: {
551
+ fontFamily: AppFonts.interBold,
552
+ fontSize: 7.5,
553
+ color: AppColors.white,
554
+ },
555
+ card: {
556
+ backgroundColor: AppColors.primaryLight,
557
+ borderRadius: 14,
558
+ padding: 14,
559
+ borderWidth: 1,
560
+ borderColor: AppColors.grayBorderSecondary,
561
+ shadowColor: AppColors.black,
562
+ shadowOffset: {width: 0, height: 2},
563
+ shadowOpacity: 0.05,
564
+ shadowRadius: 4,
565
+ elevation: 2,
566
+ gap: 8,
567
+ },
568
+ cardHeader: {
569
+ flexDirection: 'row',
570
+ alignItems: 'center',
571
+ justifyContent: 'space-between',
572
+ },
573
+ cardTitle: {
574
+ fontFamily: AppFonts.interBold,
575
+ fontSize: 11,
576
+ color: AppColors.grayTextWeak,
577
+ letterSpacing: 0.6,
578
+ },
579
+ headerActionText: {
580
+ fontFamily: AppFonts.interBold,
581
+ fontSize: 11,
582
+ color: AppColors.purple,
583
+ },
584
+ inputContainer: {
585
+ flexDirection: 'row',
586
+ alignItems: 'center',
587
+ backgroundColor: AppColors.grayBackground,
588
+ borderRadius: 10,
589
+ borderWidth: 1,
590
+ borderColor: AppColors.grayBorderSecondary,
591
+ paddingHorizontal: 10,
592
+ height: 40,
593
+ },
594
+ inputPrefix: {
595
+ fontFamily: AppFonts.interMedium,
596
+ fontSize: 12.5,
597
+ color: AppColors.grayTextWeak,
598
+ },
599
+ input: {
600
+ flex: 1,
601
+ fontFamily: AppFonts.interBold,
602
+ fontSize: 13,
603
+ color: AppColors.primaryBlack,
604
+ paddingVertical: 0,
605
+ },
606
+ inputSuffix: {
607
+ fontFamily: AppFonts.interBold,
608
+ fontSize: 12.5,
609
+ color: AppColors.purple,
610
+ },
611
+ inputHint: {
612
+ fontFamily: AppFonts.interRegular,
613
+ fontSize: 11,
614
+ color: AppColors.grayTextWeak,
615
+ lineHeight: 15,
616
+ },
617
+ qrCard: {
618
+ backgroundColor: AppColors.primaryLight,
619
+ borderRadius: 14,
620
+ padding: 14,
621
+ borderWidth: 1,
622
+ borderColor: AppColors.grayBorderSecondary,
623
+ shadowColor: AppColors.black,
624
+ shadowOffset: {width: 0, height: 2},
625
+ shadowOpacity: 0.05,
626
+ shadowRadius: 4,
627
+ elevation: 2,
628
+ alignItems: 'center',
629
+ gap: 14,
630
+ },
631
+ qrTabsRow: {
632
+ flexDirection: 'row',
633
+ backgroundColor: AppColors.grayBackground,
634
+ borderRadius: 10,
635
+ padding: 3,
636
+ width: '100%',
637
+ gap: 4,
638
+ },
639
+ qrTab: {
640
+ flex: 1,
641
+ flexDirection: 'row',
642
+ alignItems: 'center',
643
+ justifyContent: 'center',
644
+ gap: 4,
645
+ paddingVertical: 7,
646
+ borderRadius: 8,
647
+ },
648
+ qrTabActive: {
649
+ backgroundColor: AppColors.purple,
650
+ },
651
+ qrTabText: {
652
+ fontFamily: AppFonts.interMedium,
653
+ fontSize: 11,
654
+ color: AppColors.grayText,
655
+ },
656
+ qrTabTextActive: {
657
+ color: AppColors.white,
658
+ fontFamily: AppFonts.interBold,
659
+ },
660
+ qrWrapper: {
661
+ alignItems: 'center',
662
+ gap: 12,
663
+ },
664
+ qrInfoBox: {
665
+ alignItems: 'center',
666
+ maxWidth: 280,
667
+ gap: 2,
668
+ },
669
+ qrTargetTitle: {
670
+ fontFamily: AppFonts.interBold,
671
+ fontSize: 11.5,
672
+ color: AppColors.primaryBlack,
673
+ },
674
+ qrTargetUrl: {
675
+ fontFamily: AppFonts.interRegular,
676
+ fontSize: 10.5,
677
+ color: AppColors.skyBlue,
678
+ textAlign: 'center',
679
+ },
680
+ qrActionsRow: {
681
+ flexDirection: 'row',
682
+ gap: 10,
683
+ width: '100%',
684
+ },
685
+ actionBtn: {
686
+ flex: 1,
687
+ paddingVertical: 10,
688
+ borderRadius: 10,
689
+ borderWidth: 1,
690
+ borderColor: AppColors.grayBorderSecondary,
691
+ backgroundColor: AppColors.grayBackground,
692
+ alignItems: 'center',
693
+ justifyContent: 'center',
694
+ },
695
+ actionBtnText: {
696
+ fontFamily: AppFonts.interBold,
697
+ fontSize: 12,
698
+ color: AppColors.grayTextStrong,
699
+ },
700
+ actionBtnPrimary: {
701
+ backgroundColor: AppColors.purple,
702
+ borderColor: AppColors.purple,
703
+ },
704
+ actionBtnPrimaryText: {
705
+ fontFamily: AppFonts.interBold,
706
+ fontSize: 12,
707
+ color: AppColors.white,
708
+ },
709
+ guideCard: {
710
+ backgroundColor: AppColors.primaryLight,
711
+ borderRadius: 14,
712
+ padding: 14,
713
+ borderWidth: 1,
714
+ borderColor: AppColors.grayBorderSecondary,
715
+ gap: 10,
716
+ },
717
+ guideHeading: {
718
+ fontFamily: AppFonts.interBold,
719
+ fontSize: 11,
720
+ color: AppColors.grayTextWeak,
721
+ letterSpacing: 0.6,
722
+ },
723
+ stepRow: {
724
+ flexDirection: 'row',
725
+ alignItems: 'flex-start',
726
+ gap: 10,
727
+ },
728
+ stepNumberBadge: {
729
+ width: 22,
730
+ height: 22,
731
+ borderRadius: 11,
732
+ backgroundColor: `${AppColors.purple}18`,
733
+ alignItems: 'center',
734
+ justifyContent: 'center',
735
+ marginTop: 1,
736
+ },
737
+ stepNumberText: {
738
+ fontFamily: AppFonts.interBold,
739
+ fontSize: 11,
740
+ color: AppColors.purple,
741
+ },
742
+ stepTitle: {
743
+ fontFamily: AppFonts.interBold,
744
+ fontSize: 12.5,
745
+ color: AppColors.primaryBlack,
746
+ },
747
+ stepDesc: {
748
+ fontFamily: AppFonts.interRegular,
749
+ fontSize: 11,
750
+ color: AppColors.grayText,
751
+ lineHeight: 16,
752
+ marginTop: 1,
753
+ },
754
+ });
755
+
756
+ export default DebuggingTab;