clawdex-mobile 3.0.0 → 4.0.0
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/.github/workflows/ci.yml +4 -3
- package/.github/workflows/npm-release.yml +62 -2
- package/.github/workflows/pages.yml +1 -1
- package/README.md +14 -3
- package/apps/mobile/app.json +1 -1
- package/apps/mobile/package.json +2 -1
- package/apps/mobile/src/api/__tests__/client.test.ts +13 -5
- package/apps/mobile/src/api/client.ts +25 -0
- package/apps/mobile/src/api/types.ts +1 -0
- package/apps/mobile/src/components/WorkspacePickerModal.tsx +555 -315
- package/apps/mobile/src/screens/MainScreen.tsx +0 -5
- package/apps/mobile/src/screens/OnboardingScreen.tsx +924 -312
- package/bin/clawdex.js +7 -6
- package/codex-rust-bridge +0 -0
- package/codex-rust-bridge.exe +0 -0
- package/docs/setup-and-operations.md +17 -12
- package/docs/troubleshooting.md +15 -19
- package/package.json +4 -3
- package/scripts/bridge-binary.js +194 -0
- package/scripts/setup-wizard.sh +17 -186
- package/scripts/start-bridge-secure.js +240 -0
- package/scripts/start-bridge-secure.sh +1 -40
- package/services/rust-bridge/Cargo.lock +1 -1
- package/services/rust-bridge/Cargo.toml +1 -1
- package/services/rust-bridge/package.json +1 -1
- package/services/rust-bridge/src/main.rs +11 -1
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { Ionicons } from '@expo/vector-icons';
|
|
2
2
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
3
|
+
import { BlurView } from 'expo-blur';
|
|
3
4
|
import { CameraView, type BarcodeScanningResult, useCameraPermissions } from 'expo-camera';
|
|
5
|
+
import * as Clipboard from 'expo-clipboard';
|
|
6
|
+
import { LinearGradient } from 'expo-linear-gradient';
|
|
4
7
|
import {
|
|
5
8
|
ActivityIndicator,
|
|
6
9
|
KeyboardAvoidingView,
|
|
@@ -42,10 +45,59 @@ type ConnectionCheck =
|
|
|
42
45
|
| { kind: 'error'; message: string };
|
|
43
46
|
type OnboardingStep = 'intro' | 'connect';
|
|
44
47
|
type PairingPayload = { bridgeToken: string; bridgeUrl?: string };
|
|
45
|
-
type
|
|
48
|
+
type IntroFeature = {
|
|
49
|
+
icon: keyof typeof Ionicons.glyphMap;
|
|
50
|
+
title: string;
|
|
51
|
+
description: string;
|
|
52
|
+
};
|
|
46
53
|
|
|
47
|
-
const
|
|
48
|
-
const
|
|
54
|
+
const BRIDGE_SETUP_COMMANDS = 'npm install -g clawdex-mobile@latest\nclawdex init';
|
|
55
|
+
const INTRO_FEATURES: IntroFeature[] = [
|
|
56
|
+
{
|
|
57
|
+
icon: 'pulse-outline',
|
|
58
|
+
title: 'Start and monitor runs',
|
|
59
|
+
description: 'Launch new Codex work from your phone and follow streaming updates as it runs.',
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
icon: 'chatbubble-ellipses-outline',
|
|
63
|
+
title: 'Resume threads',
|
|
64
|
+
description: 'Pick up existing chats and keep active work moving without going back to desktop.',
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
icon: 'shield-checkmark-outline',
|
|
68
|
+
title: 'Review approvals',
|
|
69
|
+
description: 'Handle clarification prompts and approve commands or file changes in-app.',
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
icon: 'folder-open-outline',
|
|
73
|
+
title: 'Browse workspaces',
|
|
74
|
+
description: 'Open recent folders or navigate server directories before starting work.',
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
icon: 'git-branch-outline',
|
|
78
|
+
title: 'Git actions',
|
|
79
|
+
description: 'Review diffs, commit, and push chat workspaces from the Git view.',
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
icon: 'mic-outline',
|
|
83
|
+
title: 'Voice, files, images',
|
|
84
|
+
description: 'Use push-to-talk and attach workspace files or phone images to prompts.',
|
|
85
|
+
},
|
|
86
|
+
];
|
|
87
|
+
const SETUP_STAGES = [
|
|
88
|
+
{
|
|
89
|
+
title: 'Start bridge',
|
|
90
|
+
description: 'Run the CLI on your server and wait for the pairing QR.',
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
title: 'Pair bridge',
|
|
94
|
+
description: 'Scan QR or paste the bridge URL and token.',
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
title: 'Verify auth',
|
|
98
|
+
description: 'Confirm health and authenticated RPC before continuing.',
|
|
99
|
+
},
|
|
100
|
+
] as const;
|
|
49
101
|
|
|
50
102
|
export function OnboardingScreen({
|
|
51
103
|
mode = 'initial',
|
|
@@ -83,7 +135,6 @@ export function OnboardingScreen({
|
|
|
83
135
|
}, [initialBridgeToken]);
|
|
84
136
|
|
|
85
137
|
const showIntroStep = mode === 'initial' && onboardingStep === 'intro';
|
|
86
|
-
|
|
87
138
|
const normalizedBridgeUrl = useMemo(
|
|
88
139
|
() => normalizeBridgeUrlInput(urlInput),
|
|
89
140
|
[urlInput]
|
|
@@ -98,11 +149,25 @@ export function OnboardingScreen({
|
|
|
98
149
|
: null;
|
|
99
150
|
}, [allowInsecureRemoteBridge, normalizedBridgeUrl]);
|
|
100
151
|
|
|
101
|
-
const modeTitle = mode === 'edit' ? 'Update Bridge URL' : '
|
|
152
|
+
const modeTitle = mode === 'edit' ? 'Update Bridge URL' : 'Pair Your Bridge';
|
|
102
153
|
const modeDescription =
|
|
103
154
|
mode === 'edit'
|
|
104
|
-
? 'Switch to another
|
|
105
|
-
: '
|
|
155
|
+
? 'Switch this phone to another bridge without rebuilding the app.'
|
|
156
|
+
: 'Connect this phone to your private bridge and verify that it responds.';
|
|
157
|
+
const normalizedTokenPreview = tokenInput.trim();
|
|
158
|
+
const showOnboardingDock = mode === 'initial';
|
|
159
|
+
const currentSetupStage = useMemo(() => {
|
|
160
|
+
if (showIntroStep) {
|
|
161
|
+
return 1;
|
|
162
|
+
}
|
|
163
|
+
if (connectionCheck.kind === 'success') {
|
|
164
|
+
return 3;
|
|
165
|
+
}
|
|
166
|
+
if (normalizedBridgeUrl || normalizedTokenPreview) {
|
|
167
|
+
return 2;
|
|
168
|
+
}
|
|
169
|
+
return 1;
|
|
170
|
+
}, [connectionCheck.kind, normalizedBridgeUrl, normalizedTokenPreview, showIntroStep]);
|
|
106
171
|
|
|
107
172
|
const validateInput = useCallback((): { bridgeUrl: string; bridgeToken: string } | null => {
|
|
108
173
|
const normalized = normalizeBridgeUrlInput(urlInput);
|
|
@@ -209,16 +274,6 @@ export function OnboardingScreen({
|
|
|
209
274
|
await runConnectionCheck(validated.bridgeUrl, normalizedToken);
|
|
210
275
|
}, [normalizeTokenInput, runConnectionCheck, validateInput]);
|
|
211
276
|
|
|
212
|
-
const applyPreset = useCallback((value: string) => {
|
|
213
|
-
setUrlInput(value);
|
|
214
|
-
setFormError(null);
|
|
215
|
-
setConnectionCheck({ kind: 'idle' });
|
|
216
|
-
}, []);
|
|
217
|
-
|
|
218
|
-
const applyModePreset = useCallback((preset: BridgeModePreset) => {
|
|
219
|
-
applyPreset(preset === 'local' ? LOCAL_EXAMPLE_URL : TAILSCALE_EXAMPLE_URL);
|
|
220
|
-
}, [applyPreset]);
|
|
221
|
-
|
|
222
277
|
const goToConnectStep = useCallback(() => {
|
|
223
278
|
setOnboardingStep('connect');
|
|
224
279
|
}, []);
|
|
@@ -281,6 +336,20 @@ export function OnboardingScreen({
|
|
|
281
336
|
|
|
282
337
|
return (
|
|
283
338
|
<View style={styles.container}>
|
|
339
|
+
<LinearGradient
|
|
340
|
+
colors={['#020304', '#05070C', '#0A0E16']}
|
|
341
|
+
style={StyleSheet.absoluteFill}
|
|
342
|
+
/>
|
|
343
|
+
<View pointerEvents="none" style={styles.ambientCanvas}>
|
|
344
|
+
<LinearGradient
|
|
345
|
+
colors={['rgba(181, 189, 204, 0.20)', 'rgba(181, 189, 204, 0.04)', 'transparent']}
|
|
346
|
+
style={styles.ambientOrbPrimary}
|
|
347
|
+
/>
|
|
348
|
+
<LinearGradient
|
|
349
|
+
colors={['rgba(255, 255, 255, 0.10)', 'rgba(255, 255, 255, 0.02)', 'transparent']}
|
|
350
|
+
style={styles.ambientOrbSecondary}
|
|
351
|
+
/>
|
|
352
|
+
</View>
|
|
284
353
|
<SafeAreaView style={styles.safeArea}>
|
|
285
354
|
<KeyboardAvoidingView
|
|
286
355
|
behavior={Platform.select({ ios: 'padding', default: undefined })}
|
|
@@ -288,64 +357,70 @@ export function OnboardingScreen({
|
|
|
288
357
|
>
|
|
289
358
|
{showIntroStep ? (
|
|
290
359
|
<View style={styles.introRoot}>
|
|
291
|
-
<View style={styles.
|
|
292
|
-
<
|
|
293
|
-
|
|
360
|
+
<View style={styles.introHeader}>
|
|
361
|
+
<View style={styles.introBrandRow}>
|
|
362
|
+
<BrandMark size={24} />
|
|
363
|
+
<Text style={styles.introBrandName}>Clawdex</Text>
|
|
364
|
+
</View>
|
|
365
|
+
<AmbientBadge icon="lock-closed-outline" label="Private bridge" />
|
|
294
366
|
</View>
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
367
|
+
|
|
368
|
+
<ScrollView
|
|
369
|
+
style={styles.introScroll}
|
|
370
|
+
contentContainerStyle={styles.introScrollContent}
|
|
371
|
+
showsVerticalScrollIndicator={false}
|
|
372
|
+
>
|
|
373
|
+
<LinearGradient
|
|
374
|
+
colors={[
|
|
375
|
+
'rgba(181, 189, 204, 0.22)',
|
|
376
|
+
'rgba(34, 39, 48, 0.75)',
|
|
377
|
+
'rgba(7, 9, 12, 0.96)',
|
|
378
|
+
]}
|
|
379
|
+
start={{ x: 0, y: 0 }}
|
|
380
|
+
end={{ x: 1, y: 1 }}
|
|
381
|
+
style={styles.introHero}
|
|
382
|
+
>
|
|
383
|
+
<Text style={styles.introHeroEyebrow}>Mobile control surface</Text>
|
|
384
|
+
<Text style={styles.introHeroTitle}>Run Codex away from your desk.</Text>
|
|
385
|
+
<Text style={styles.introHeroDescription}>
|
|
386
|
+
Continue threads, review work, and approve changes from a paired private
|
|
387
|
+
bridge.
|
|
388
|
+
</Text>
|
|
389
|
+
</LinearGradient>
|
|
390
|
+
|
|
391
|
+
<View style={styles.introSectionHeader}>
|
|
392
|
+
<Text style={styles.introSectionTitle}>What you can do from mobile</Text>
|
|
393
|
+
<Text style={styles.introSectionSubtitle}>
|
|
394
|
+
The phone UI should feel like a control surface, not a read-only mirror.
|
|
305
395
|
</Text>
|
|
306
396
|
</View>
|
|
307
397
|
|
|
308
|
-
<View style={
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
<IntroFeatureRow
|
|
316
|
-
icon="chatbubble-ellipses-outline"
|
|
317
|
-
title="Continue threads"
|
|
318
|
-
description="Follow active chats and start new runs from your phone."
|
|
319
|
-
/>
|
|
320
|
-
<IntroFeatureRow
|
|
321
|
-
icon="pulse-outline"
|
|
322
|
-
title="Track run progress"
|
|
323
|
-
description="See live status and streaming updates as Codex works."
|
|
324
|
-
/>
|
|
325
|
-
<IntroFeatureRow
|
|
326
|
-
icon="git-branch-outline"
|
|
327
|
-
title="Handle git tasks"
|
|
328
|
-
description="Review status, diffs, and commits for chat workspaces."
|
|
329
|
-
/>
|
|
330
|
-
<IntroFeatureRow
|
|
331
|
-
icon="mic-outline"
|
|
332
|
-
title="Talk to Codex"
|
|
333
|
-
description="Use voice input to speak your prompts directly from mobile."
|
|
334
|
-
/>
|
|
335
|
-
<IntroFeatureRow
|
|
336
|
-
icon="attach-outline"
|
|
337
|
-
title="Share files and images"
|
|
338
|
-
description="Attach workspace files and phone media to your prompts."
|
|
339
|
-
/>
|
|
340
|
-
<IntroFeatureRow
|
|
341
|
-
icon="shield-checkmark-outline"
|
|
342
|
-
title="Approve actions"
|
|
343
|
-
description="Review and approve command and file changes in-app."
|
|
398
|
+
<View style={styles.introFeatureGrid}>
|
|
399
|
+
{INTRO_FEATURES.map((feature) => (
|
|
400
|
+
<IntroFeatureCard
|
|
401
|
+
key={feature.title}
|
|
402
|
+
icon={feature.icon}
|
|
403
|
+
title={feature.title}
|
|
404
|
+
description={feature.description}
|
|
344
405
|
/>
|
|
345
|
-
|
|
406
|
+
))}
|
|
346
407
|
</View>
|
|
347
|
-
|
|
408
|
+
|
|
409
|
+
<BlurView intensity={40} tint="dark" style={styles.introContextCard}>
|
|
410
|
+
<Text style={styles.introContextTitle}>Best on trusted networks</Text>
|
|
411
|
+
<Text style={styles.introContextText}>
|
|
412
|
+
Built for your own machine and private network paths. Pair over LAN nearby, or
|
|
413
|
+
use Tailscale when the bridge lives on another device.
|
|
414
|
+
</Text>
|
|
415
|
+
<View style={styles.introContextPillRow}>
|
|
416
|
+
<AmbientBadge icon="git-network-outline" label="Private network only" />
|
|
417
|
+
<AmbientBadge icon="shield-checkmark-outline" label="Token auth required" />
|
|
418
|
+
</View>
|
|
419
|
+
</BlurView>
|
|
420
|
+
</ScrollView>
|
|
421
|
+
|
|
348
422
|
<View style={styles.introFooter}>
|
|
423
|
+
{showOnboardingDock ? <OnboardingStepDock currentStage={currentSetupStage} /> : null}
|
|
349
424
|
<Pressable
|
|
350
425
|
onPress={goToConnectStep}
|
|
351
426
|
style={({ pressed }) => [
|
|
@@ -353,18 +428,29 @@ export function OnboardingScreen({
|
|
|
353
428
|
pressed && styles.introNextButtonPressed,
|
|
354
429
|
]}
|
|
355
430
|
>
|
|
356
|
-
<Text style={styles.introNextButtonText}>
|
|
431
|
+
<Text style={styles.introNextButtonText}>Pair your bridge</Text>
|
|
357
432
|
<Ionicons name="arrow-forward" size={19} color={colors.black} />
|
|
358
433
|
</Pressable>
|
|
359
434
|
</View>
|
|
360
435
|
</View>
|
|
361
436
|
) : (
|
|
362
|
-
<
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
437
|
+
<View style={styles.connectRoot}>
|
|
438
|
+
<ScrollView
|
|
439
|
+
style={styles.scroll}
|
|
440
|
+
contentContainerStyle={styles.scrollContent}
|
|
441
|
+
keyboardShouldPersistTaps="handled"
|
|
442
|
+
showsVerticalScrollIndicator={false}
|
|
443
|
+
>
|
|
444
|
+
<LinearGradient
|
|
445
|
+
colors={[
|
|
446
|
+
'rgba(181, 189, 204, 0.18)',
|
|
447
|
+
'rgba(22, 25, 31, 0.82)',
|
|
448
|
+
'rgba(7, 9, 12, 0.98)',
|
|
449
|
+
]}
|
|
450
|
+
start={{ x: 0, y: 0 }}
|
|
451
|
+
end={{ x: 1, y: 1 }}
|
|
452
|
+
style={styles.connectHero}
|
|
453
|
+
>
|
|
368
454
|
<View style={styles.heroTopRow}>
|
|
369
455
|
<View style={styles.heroIconWrap}>
|
|
370
456
|
<Ionicons name="hardware-chip-outline" size={20} color={colors.textPrimary} />
|
|
@@ -379,99 +465,35 @@ export function OnboardingScreen({
|
|
|
379
465
|
</Pressable>
|
|
380
466
|
) : null}
|
|
381
467
|
</View>
|
|
468
|
+
<Text style={styles.connectEyebrow}>Bridge pairing</Text>
|
|
382
469
|
<Text style={styles.heroTitle}>{modeTitle}</Text>
|
|
383
470
|
<Text style={styles.heroDescription}>{modeDescription}</Text>
|
|
384
|
-
</
|
|
471
|
+
</LinearGradient>
|
|
385
472
|
|
|
386
|
-
<
|
|
387
|
-
<
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
onPress={() => applyModePreset('tailscale')}
|
|
401
|
-
style={({ pressed }) => [
|
|
402
|
-
styles.modeButton,
|
|
403
|
-
pressed && styles.modeButtonPressed,
|
|
404
|
-
]}
|
|
405
|
-
>
|
|
406
|
-
<Ionicons name="shield-outline" size={16} color={colors.textPrimary} />
|
|
407
|
-
<Text style={styles.modeButtonText}>Tailscale</Text>
|
|
408
|
-
</Pressable>
|
|
473
|
+
<BlurView intensity={55} tint="dark" style={styles.formCard}>
|
|
474
|
+
<View style={styles.commandPanel}>
|
|
475
|
+
<View style={styles.formSectionHeader}>
|
|
476
|
+
<Text style={styles.formSectionEyebrow}>1. Start the bridge</Text>
|
|
477
|
+
<Text style={styles.formSectionTitle}>
|
|
478
|
+
Run these on your server first. This installs the CLI, starts the bridge,
|
|
479
|
+
and prints the pairing QR used here.
|
|
480
|
+
</Text>
|
|
481
|
+
</View>
|
|
482
|
+
<CommandSnippet
|
|
483
|
+
label="Run on your server"
|
|
484
|
+
command={BRIDGE_SETUP_COMMANDS}
|
|
485
|
+
hint="After it starts, scan the pairing QR in this screen."
|
|
486
|
+
/>
|
|
409
487
|
</View>
|
|
410
|
-
<Text style={styles.helperText}>
|
|
411
|
-
Pick the same mode used while starting the bridge, then adjust the IP if needed.
|
|
412
|
-
</Text>
|
|
413
488
|
|
|
414
|
-
<
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
setConnectionCheck({ kind: 'idle' });
|
|
421
|
-
}}
|
|
422
|
-
keyboardAppearance="dark"
|
|
423
|
-
autoCapitalize="none"
|
|
424
|
-
autoCorrect={false}
|
|
425
|
-
keyboardType="url"
|
|
426
|
-
placeholder="http://100.101.102.103:8787"
|
|
427
|
-
placeholderTextColor={colors.textMuted}
|
|
428
|
-
style={styles.input}
|
|
429
|
-
returnKeyType="done"
|
|
430
|
-
onSubmitEditing={() => {
|
|
431
|
-
void handleSave();
|
|
432
|
-
}}
|
|
433
|
-
/>
|
|
434
|
-
<View style={styles.tokenHeaderRow}>
|
|
435
|
-
<Text style={styles.label}>Bridge Token</Text>
|
|
436
|
-
<Text style={styles.optionalLabel}>Required</Text>
|
|
437
|
-
</View>
|
|
438
|
-
<View style={styles.tokenInputWrap}>
|
|
439
|
-
<TextInput
|
|
440
|
-
value={tokenInput}
|
|
441
|
-
onChangeText={(value) => {
|
|
442
|
-
setTokenInput(value);
|
|
443
|
-
setConnectionCheck({ kind: 'idle' });
|
|
444
|
-
}}
|
|
445
|
-
keyboardAppearance="dark"
|
|
446
|
-
autoCapitalize="none"
|
|
447
|
-
autoCorrect={false}
|
|
448
|
-
keyboardType="default"
|
|
449
|
-
placeholder="Paste bridge token"
|
|
450
|
-
placeholderTextColor={colors.textMuted}
|
|
451
|
-
style={[styles.input, styles.tokenInput]}
|
|
452
|
-
secureTextEntry={tokenHidden}
|
|
453
|
-
returnKeyType="done"
|
|
454
|
-
onSubmitEditing={() => {
|
|
455
|
-
void handleSave();
|
|
456
|
-
}}
|
|
457
|
-
/>
|
|
458
|
-
<Pressable
|
|
459
|
-
onPress={() => setTokenHidden((prev) => !prev)}
|
|
460
|
-
style={({ pressed }) => [
|
|
461
|
-
styles.tokenRevealBtn,
|
|
462
|
-
pressed && styles.tokenRevealBtnPressed,
|
|
463
|
-
]}
|
|
464
|
-
>
|
|
465
|
-
<Ionicons
|
|
466
|
-
name={tokenHidden ? 'eye-outline' : 'eye-off-outline'}
|
|
467
|
-
size={16}
|
|
468
|
-
color={colors.textSecondary}
|
|
469
|
-
/>
|
|
470
|
-
<Text style={styles.tokenRevealBtnText}>
|
|
471
|
-
{tokenHidden ? 'Show' : 'Hide'}
|
|
472
|
-
</Text>
|
|
473
|
-
</Pressable>
|
|
489
|
+
<View style={styles.formSectionHeader}>
|
|
490
|
+
<Text style={styles.formSectionEyebrow}>2. Pair the bridge</Text>
|
|
491
|
+
<Text style={styles.formSectionTitle}>
|
|
492
|
+
Scan the bridge QR first. If that is not available, enter the URL and token
|
|
493
|
+
manually.
|
|
494
|
+
</Text>
|
|
474
495
|
</View>
|
|
496
|
+
|
|
475
497
|
<Pressable
|
|
476
498
|
onPress={() => {
|
|
477
499
|
void openScanner();
|
|
@@ -482,15 +504,96 @@ export function OnboardingScreen({
|
|
|
482
504
|
]}
|
|
483
505
|
>
|
|
484
506
|
<Ionicons name="qr-code-outline" size={16} color={colors.textPrimary} />
|
|
485
|
-
<Text style={styles.scanButtonText}>Scan
|
|
507
|
+
<Text style={styles.scanButtonText}>Scan bridge QR</Text>
|
|
486
508
|
</Pressable>
|
|
509
|
+
<Text style={styles.helperText}>
|
|
510
|
+
Recommended first. QR fills the bridge URL and token together.
|
|
511
|
+
</Text>
|
|
512
|
+
|
|
513
|
+
<View style={styles.fieldGroup}>
|
|
514
|
+
<Text style={styles.label}>Bridge URL</Text>
|
|
515
|
+
<View style={styles.inputRow}>
|
|
516
|
+
<View style={styles.inputIconWrap}>
|
|
517
|
+
<Ionicons name="globe-outline" size={16} color={colors.textSecondary} />
|
|
518
|
+
</View>
|
|
519
|
+
<TextInput
|
|
520
|
+
value={urlInput}
|
|
521
|
+
onChangeText={(value) => {
|
|
522
|
+
setUrlInput(value);
|
|
523
|
+
setFormError(null);
|
|
524
|
+
setConnectionCheck({ kind: 'idle' });
|
|
525
|
+
}}
|
|
526
|
+
keyboardAppearance="dark"
|
|
527
|
+
autoCapitalize="none"
|
|
528
|
+
autoCorrect={false}
|
|
529
|
+
keyboardType="url"
|
|
530
|
+
placeholder="http://100.101.102.103:8787"
|
|
531
|
+
placeholderTextColor={colors.textMuted}
|
|
532
|
+
style={styles.inputText}
|
|
533
|
+
returnKeyType="done"
|
|
534
|
+
onSubmitEditing={() => {
|
|
535
|
+
void handleSave();
|
|
536
|
+
}}
|
|
537
|
+
/>
|
|
538
|
+
</View>
|
|
539
|
+
</View>
|
|
540
|
+
|
|
541
|
+
<View style={styles.fieldGroup}>
|
|
542
|
+
<View style={styles.tokenHeaderRow}>
|
|
543
|
+
<Text style={styles.label}>Bridge Token</Text>
|
|
544
|
+
<Text style={styles.optionalLabel}>Required</Text>
|
|
545
|
+
</View>
|
|
546
|
+
<View style={styles.tokenInputWrap}>
|
|
547
|
+
<View style={styles.inputRow}>
|
|
548
|
+
<View style={styles.inputIconWrap}>
|
|
549
|
+
<Ionicons name="key-outline" size={16} color={colors.textSecondary} />
|
|
550
|
+
</View>
|
|
551
|
+
<TextInput
|
|
552
|
+
value={tokenInput}
|
|
553
|
+
onChangeText={(value) => {
|
|
554
|
+
setTokenInput(value);
|
|
555
|
+
setConnectionCheck({ kind: 'idle' });
|
|
556
|
+
}}
|
|
557
|
+
keyboardAppearance="dark"
|
|
558
|
+
autoCapitalize="none"
|
|
559
|
+
autoCorrect={false}
|
|
560
|
+
keyboardType="default"
|
|
561
|
+
placeholder="Paste bridge token"
|
|
562
|
+
placeholderTextColor={colors.textMuted}
|
|
563
|
+
style={styles.inputText}
|
|
564
|
+
secureTextEntry={tokenHidden}
|
|
565
|
+
returnKeyType="done"
|
|
566
|
+
onSubmitEditing={() => {
|
|
567
|
+
void handleSave();
|
|
568
|
+
}}
|
|
569
|
+
/>
|
|
570
|
+
</View>
|
|
571
|
+
<Pressable
|
|
572
|
+
onPress={() => setTokenHidden((prev) => !prev)}
|
|
573
|
+
style={({ pressed }) => [
|
|
574
|
+
styles.tokenRevealBtn,
|
|
575
|
+
pressed && styles.tokenRevealBtnPressed,
|
|
576
|
+
]}
|
|
577
|
+
>
|
|
578
|
+
<Ionicons
|
|
579
|
+
name={tokenHidden ? 'eye-outline' : 'eye-off-outline'}
|
|
580
|
+
size={16}
|
|
581
|
+
color={colors.textSecondary}
|
|
582
|
+
/>
|
|
583
|
+
<Text style={styles.tokenRevealBtnText}>
|
|
584
|
+
{tokenHidden ? 'Show' : 'Hide'}
|
|
585
|
+
</Text>
|
|
586
|
+
</Pressable>
|
|
587
|
+
</View>
|
|
588
|
+
</View>
|
|
589
|
+
|
|
487
590
|
<Text style={styles.helperText}>
|
|
488
591
|
URL supports `http`, `https`, `ws`, and `wss`. `/rpc` is added automatically.
|
|
489
592
|
</Text>
|
|
490
593
|
|
|
491
594
|
{normalizedBridgeUrl ? (
|
|
492
595
|
<View style={styles.previewWrap}>
|
|
493
|
-
<Text style={styles.previewLabel}>Normalized
|
|
596
|
+
<Text style={styles.previewLabel}>Normalized target</Text>
|
|
494
597
|
<Text selectable style={styles.previewValue}>
|
|
495
598
|
{normalizedBridgeUrl}
|
|
496
599
|
</Text>
|
|
@@ -498,17 +601,38 @@ export function OnboardingScreen({
|
|
|
498
601
|
) : null}
|
|
499
602
|
|
|
500
603
|
{insecureRemoteWarning ? (
|
|
501
|
-
<
|
|
604
|
+
<StatusBanner
|
|
605
|
+
tone="warning"
|
|
606
|
+
icon="warning-outline"
|
|
607
|
+
message={insecureRemoteWarning}
|
|
608
|
+
/>
|
|
502
609
|
) : null}
|
|
503
610
|
|
|
504
|
-
{formError ?
|
|
611
|
+
{formError ? (
|
|
612
|
+
<StatusBanner tone="error" icon="close-circle-outline" message={formError} />
|
|
613
|
+
) : null}
|
|
505
614
|
{connectionCheck.kind === 'success' ? (
|
|
506
|
-
<
|
|
615
|
+
<StatusBanner
|
|
616
|
+
tone="success"
|
|
617
|
+
icon="checkmark-circle-outline"
|
|
618
|
+
message={connectionCheck.message}
|
|
619
|
+
/>
|
|
507
620
|
) : null}
|
|
508
621
|
{connectionCheck.kind === 'error' ? (
|
|
509
|
-
<
|
|
622
|
+
<StatusBanner
|
|
623
|
+
tone="error"
|
|
624
|
+
icon="alert-circle-outline"
|
|
625
|
+
message={connectionCheck.message}
|
|
626
|
+
/>
|
|
510
627
|
) : null}
|
|
511
628
|
|
|
629
|
+
<View style={styles.formSectionHeader}>
|
|
630
|
+
<Text style={styles.formSectionEyebrow}>3. Verify and continue</Text>
|
|
631
|
+
<Text style={styles.formSectionTitle}>
|
|
632
|
+
Confirm the bridge responds before saving it into the app.
|
|
633
|
+
</Text>
|
|
634
|
+
</View>
|
|
635
|
+
|
|
512
636
|
<View style={styles.actionRow}>
|
|
513
637
|
<Pressable
|
|
514
638
|
onPress={() => {
|
|
@@ -549,15 +673,14 @@ export function OnboardingScreen({
|
|
|
549
673
|
</Text>
|
|
550
674
|
</Pressable>
|
|
551
675
|
</View>
|
|
676
|
+
</BlurView>
|
|
677
|
+
</ScrollView>
|
|
678
|
+
{showOnboardingDock ? (
|
|
679
|
+
<View style={styles.connectFooter}>
|
|
680
|
+
<OnboardingStepDock currentStage={currentSetupStage} />
|
|
552
681
|
</View>
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
<Text style={styles.hintTitle}>Quick Setup</Text>
|
|
556
|
-
<Text style={styles.hintText}>1. Start the bridge in Local (LAN) or Tailscale mode.</Text>
|
|
557
|
-
<Text style={styles.hintText}>2. Pick Local or Tailscale above, then confirm bridge URL.</Text>
|
|
558
|
-
<Text style={styles.hintText}>3. Scan bridge QR, then test connection and continue.</Text>
|
|
559
|
-
</View>
|
|
560
|
-
</ScrollView>
|
|
682
|
+
) : null}
|
|
683
|
+
</View>
|
|
561
684
|
)}
|
|
562
685
|
<Modal
|
|
563
686
|
animationType="slide"
|
|
@@ -608,7 +731,7 @@ export function OnboardingScreen({
|
|
|
608
731
|
);
|
|
609
732
|
}
|
|
610
733
|
|
|
611
|
-
function
|
|
734
|
+
function IntroFeatureCard({
|
|
612
735
|
icon,
|
|
613
736
|
title,
|
|
614
737
|
description,
|
|
@@ -618,7 +741,7 @@ function IntroFeatureRow({
|
|
|
618
741
|
description: string;
|
|
619
742
|
}) {
|
|
620
743
|
return (
|
|
621
|
-
<View style={styles.
|
|
744
|
+
<View style={styles.introFeatureCard}>
|
|
622
745
|
<View style={styles.introFeatureIconWrap}>
|
|
623
746
|
<Ionicons name={icon} size={16} color={colors.textPrimary} />
|
|
624
747
|
</View>
|
|
@@ -630,6 +753,177 @@ function IntroFeatureRow({
|
|
|
630
753
|
);
|
|
631
754
|
}
|
|
632
755
|
|
|
756
|
+
function AmbientBadge({
|
|
757
|
+
icon,
|
|
758
|
+
label,
|
|
759
|
+
}: {
|
|
760
|
+
icon: keyof typeof Ionicons.glyphMap;
|
|
761
|
+
label: string;
|
|
762
|
+
}) {
|
|
763
|
+
return (
|
|
764
|
+
<View style={styles.ambientBadge}>
|
|
765
|
+
<Ionicons name={icon} size={13} color={colors.textSecondary} />
|
|
766
|
+
<Text style={styles.ambientBadgeText}>{label}</Text>
|
|
767
|
+
</View>
|
|
768
|
+
);
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
function OnboardingStepDock({ currentStage }: { currentStage: number }) {
|
|
772
|
+
return (
|
|
773
|
+
<BlurView intensity={45} tint="dark" style={styles.stepperDock}>
|
|
774
|
+
<View style={styles.stepperDockRow}>
|
|
775
|
+
{SETUP_STAGES.map((stage, index) => {
|
|
776
|
+
const stepNumber = index + 1;
|
|
777
|
+
const isActive = stepNumber === currentStage;
|
|
778
|
+
const isComplete = stepNumber < currentStage;
|
|
779
|
+
return (
|
|
780
|
+
<View
|
|
781
|
+
key={stage.title}
|
|
782
|
+
style={[
|
|
783
|
+
styles.stepperPill,
|
|
784
|
+
isActive && styles.stepperPillActive,
|
|
785
|
+
isComplete && styles.stepperPillComplete,
|
|
786
|
+
]}
|
|
787
|
+
>
|
|
788
|
+
<View
|
|
789
|
+
style={[
|
|
790
|
+
styles.stepperPillIndex,
|
|
791
|
+
isActive && styles.stepperPillIndexActive,
|
|
792
|
+
isComplete && styles.stepperPillIndexComplete,
|
|
793
|
+
]}
|
|
794
|
+
>
|
|
795
|
+
<Text
|
|
796
|
+
style={[
|
|
797
|
+
styles.stepperPillIndexText,
|
|
798
|
+
(isActive || isComplete) && styles.stepperPillIndexTextActive,
|
|
799
|
+
]}
|
|
800
|
+
>
|
|
801
|
+
{isComplete ? '✓' : String(stepNumber)}
|
|
802
|
+
</Text>
|
|
803
|
+
</View>
|
|
804
|
+
<Text
|
|
805
|
+
numberOfLines={1}
|
|
806
|
+
style={[
|
|
807
|
+
styles.stepperPillTitle,
|
|
808
|
+
isActive && styles.stepperPillTitleActive,
|
|
809
|
+
isComplete && styles.stepperPillTitleComplete,
|
|
810
|
+
]}
|
|
811
|
+
>
|
|
812
|
+
{stage.title}
|
|
813
|
+
</Text>
|
|
814
|
+
</View>
|
|
815
|
+
);
|
|
816
|
+
})}
|
|
817
|
+
</View>
|
|
818
|
+
</BlurView>
|
|
819
|
+
);
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
function CommandSnippet({
|
|
823
|
+
label,
|
|
824
|
+
command,
|
|
825
|
+
hint,
|
|
826
|
+
}: {
|
|
827
|
+
label: string;
|
|
828
|
+
command: string;
|
|
829
|
+
hint: string;
|
|
830
|
+
}) {
|
|
831
|
+
const [copied, setCopied] = useState(false);
|
|
832
|
+
|
|
833
|
+
const handleCopy = useCallback(async () => {
|
|
834
|
+
await Clipboard.setStringAsync(command);
|
|
835
|
+
setCopied(true);
|
|
836
|
+
setTimeout(() => {
|
|
837
|
+
setCopied(false);
|
|
838
|
+
}, 1400);
|
|
839
|
+
}, [command]);
|
|
840
|
+
|
|
841
|
+
return (
|
|
842
|
+
<View style={styles.commandCard}>
|
|
843
|
+
<View style={styles.commandCardHeader}>
|
|
844
|
+
<View style={styles.commandCardHeaderLeft}>
|
|
845
|
+
<Ionicons name="terminal-outline" size={14} color={colors.textSecondary} />
|
|
846
|
+
<Text style={styles.commandCardLabel}>{label}</Text>
|
|
847
|
+
</View>
|
|
848
|
+
<Pressable
|
|
849
|
+
onPress={() => {
|
|
850
|
+
void handleCopy();
|
|
851
|
+
}}
|
|
852
|
+
style={({ pressed }) => [
|
|
853
|
+
styles.commandCopyButton,
|
|
854
|
+
copied && styles.commandCopyButtonCopied,
|
|
855
|
+
pressed && styles.commandCopyButtonPressed,
|
|
856
|
+
]}
|
|
857
|
+
>
|
|
858
|
+
<Ionicons
|
|
859
|
+
name={copied ? 'checkmark-outline' : 'copy-outline'}
|
|
860
|
+
size={14}
|
|
861
|
+
color={copied ? colors.black : colors.textPrimary}
|
|
862
|
+
/>
|
|
863
|
+
<Text
|
|
864
|
+
style={[
|
|
865
|
+
styles.commandCopyButtonText,
|
|
866
|
+
copied && styles.commandCopyButtonTextCopied,
|
|
867
|
+
]}
|
|
868
|
+
>
|
|
869
|
+
{copied ? 'Copied' : 'Copy'}
|
|
870
|
+
</Text>
|
|
871
|
+
</Pressable>
|
|
872
|
+
</View>
|
|
873
|
+
<View style={styles.commandCodeWrap}>
|
|
874
|
+
<Text selectable style={styles.commandCodeText}>
|
|
875
|
+
{command}
|
|
876
|
+
</Text>
|
|
877
|
+
</View>
|
|
878
|
+
<Text style={styles.commandCardHint}>{hint}</Text>
|
|
879
|
+
</View>
|
|
880
|
+
);
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
function StatusBanner({
|
|
884
|
+
tone,
|
|
885
|
+
icon,
|
|
886
|
+
message,
|
|
887
|
+
}: {
|
|
888
|
+
tone: 'warning' | 'error' | 'success';
|
|
889
|
+
icon: keyof typeof Ionicons.glyphMap;
|
|
890
|
+
message: string;
|
|
891
|
+
}) {
|
|
892
|
+
const iconColor =
|
|
893
|
+
tone === 'warning' ? '#F7D27E' : tone === 'success' ? colors.statusComplete : colors.error;
|
|
894
|
+
|
|
895
|
+
return (
|
|
896
|
+
<View
|
|
897
|
+
style={[
|
|
898
|
+
styles.statusBanner,
|
|
899
|
+
tone === 'warning'
|
|
900
|
+
? styles.statusBannerWarning
|
|
901
|
+
: tone === 'success'
|
|
902
|
+
? styles.statusBannerSuccess
|
|
903
|
+
: styles.statusBannerError,
|
|
904
|
+
]}
|
|
905
|
+
>
|
|
906
|
+
<Ionicons
|
|
907
|
+
name={icon}
|
|
908
|
+
size={16}
|
|
909
|
+
color={iconColor}
|
|
910
|
+
/>
|
|
911
|
+
<Text
|
|
912
|
+
style={[
|
|
913
|
+
styles.statusBannerText,
|
|
914
|
+
tone === 'warning'
|
|
915
|
+
? styles.warningText
|
|
916
|
+
: tone === 'success'
|
|
917
|
+
? styles.successText
|
|
918
|
+
: styles.errorText,
|
|
919
|
+
]}
|
|
920
|
+
>
|
|
921
|
+
{message}
|
|
922
|
+
</Text>
|
|
923
|
+
</View>
|
|
924
|
+
);
|
|
925
|
+
}
|
|
926
|
+
|
|
633
927
|
function parsePairingPayload(rawValue: string): PairingPayload | null {
|
|
634
928
|
const raw = rawValue.trim();
|
|
635
929
|
if (!raw) {
|
|
@@ -707,6 +1001,26 @@ const styles = StyleSheet.create({
|
|
|
707
1001
|
keyboardAvoiding: {
|
|
708
1002
|
flex: 1,
|
|
709
1003
|
},
|
|
1004
|
+
ambientCanvas: {
|
|
1005
|
+
...StyleSheet.absoluteFillObject,
|
|
1006
|
+
overflow: 'hidden',
|
|
1007
|
+
},
|
|
1008
|
+
ambientOrbPrimary: {
|
|
1009
|
+
position: 'absolute',
|
|
1010
|
+
top: -110,
|
|
1011
|
+
right: -70,
|
|
1012
|
+
width: 280,
|
|
1013
|
+
height: 280,
|
|
1014
|
+
borderRadius: 140,
|
|
1015
|
+
},
|
|
1016
|
+
ambientOrbSecondary: {
|
|
1017
|
+
position: 'absolute',
|
|
1018
|
+
bottom: 110,
|
|
1019
|
+
left: -90,
|
|
1020
|
+
width: 220,
|
|
1021
|
+
height: 220,
|
|
1022
|
+
borderRadius: 110,
|
|
1023
|
+
},
|
|
710
1024
|
introRoot: {
|
|
711
1025
|
flex: 1,
|
|
712
1026
|
paddingHorizontal: spacing.lg,
|
|
@@ -714,11 +1028,16 @@ const styles = StyleSheet.create({
|
|
|
714
1028
|
paddingBottom: spacing.xxl,
|
|
715
1029
|
gap: spacing.md,
|
|
716
1030
|
},
|
|
1031
|
+
introHeader: {
|
|
1032
|
+
flexDirection: 'row',
|
|
1033
|
+
alignItems: 'center',
|
|
1034
|
+
justifyContent: 'space-between',
|
|
1035
|
+
gap: spacing.md,
|
|
1036
|
+
},
|
|
717
1037
|
introBrandRow: {
|
|
718
1038
|
flexDirection: 'row',
|
|
719
1039
|
alignItems: 'center',
|
|
720
1040
|
gap: spacing.sm,
|
|
721
|
-
paddingHorizontal: spacing.xs,
|
|
722
1041
|
},
|
|
723
1042
|
introBrandName: {
|
|
724
1043
|
...typography.headline,
|
|
@@ -726,23 +1045,223 @@ const styles = StyleSheet.create({
|
|
|
726
1045
|
fontSize: 18,
|
|
727
1046
|
letterSpacing: -0.2,
|
|
728
1047
|
},
|
|
729
|
-
|
|
1048
|
+
introScroll: {
|
|
730
1049
|
flex: 1,
|
|
1050
|
+
},
|
|
1051
|
+
introScrollContent: {
|
|
1052
|
+
paddingBottom: spacing.lg,
|
|
731
1053
|
gap: spacing.md,
|
|
732
1054
|
},
|
|
733
|
-
|
|
1055
|
+
introHero: {
|
|
1056
|
+
borderRadius: 24,
|
|
1057
|
+
borderWidth: StyleSheet.hairlineWidth,
|
|
1058
|
+
borderColor: colors.borderHighlight,
|
|
1059
|
+
padding: spacing.lg,
|
|
1060
|
+
gap: spacing.sm,
|
|
1061
|
+
overflow: 'hidden',
|
|
1062
|
+
},
|
|
1063
|
+
introHeroEyebrow: {
|
|
1064
|
+
...typography.caption,
|
|
1065
|
+
color: colors.textSecondary,
|
|
1066
|
+
textTransform: 'uppercase',
|
|
1067
|
+
letterSpacing: 0.8,
|
|
1068
|
+
},
|
|
1069
|
+
introHeroTitle: {
|
|
1070
|
+
...typography.largeTitle,
|
|
1071
|
+
fontSize: 22,
|
|
1072
|
+
lineHeight: 26,
|
|
1073
|
+
letterSpacing: -0.4,
|
|
1074
|
+
},
|
|
1075
|
+
introHeroDescription: {
|
|
1076
|
+
...typography.body,
|
|
1077
|
+
color: colors.textSecondary,
|
|
1078
|
+
fontSize: 14,
|
|
1079
|
+
lineHeight: 19,
|
|
1080
|
+
},
|
|
1081
|
+
ambientBadge: {
|
|
1082
|
+
flexDirection: 'row',
|
|
1083
|
+
alignItems: 'center',
|
|
1084
|
+
gap: 6,
|
|
1085
|
+
minHeight: 30,
|
|
1086
|
+
paddingHorizontal: spacing.sm,
|
|
1087
|
+
borderRadius: radius.full,
|
|
1088
|
+
backgroundColor: 'rgba(255,255,255,0.06)',
|
|
1089
|
+
borderWidth: StyleSheet.hairlineWidth,
|
|
1090
|
+
borderColor: colors.borderLight,
|
|
1091
|
+
},
|
|
1092
|
+
ambientBadgeText: {
|
|
1093
|
+
...typography.caption,
|
|
1094
|
+
color: colors.textSecondary,
|
|
1095
|
+
fontWeight: '600',
|
|
1096
|
+
},
|
|
1097
|
+
stepperDock: {
|
|
1098
|
+
borderRadius: 16,
|
|
1099
|
+
borderWidth: StyleSheet.hairlineWidth,
|
|
1100
|
+
borderColor: colors.borderHighlight,
|
|
1101
|
+
backgroundColor: 'rgba(12, 14, 18, 0.76)',
|
|
1102
|
+
paddingHorizontal: spacing.sm,
|
|
1103
|
+
paddingVertical: spacing.xs,
|
|
1104
|
+
overflow: 'hidden',
|
|
1105
|
+
},
|
|
1106
|
+
stepperDockRow: {
|
|
1107
|
+
flexDirection: 'row',
|
|
1108
|
+
gap: spacing.xs,
|
|
1109
|
+
},
|
|
1110
|
+
stepperPill: {
|
|
734
1111
|
flex: 1,
|
|
1112
|
+
minHeight: 36,
|
|
1113
|
+
borderRadius: radius.full,
|
|
1114
|
+
flexDirection: 'row',
|
|
1115
|
+
alignItems: 'center',
|
|
1116
|
+
justifyContent: 'center',
|
|
1117
|
+
paddingHorizontal: spacing.sm,
|
|
1118
|
+
paddingVertical: 6,
|
|
1119
|
+
gap: 6,
|
|
1120
|
+
backgroundColor: 'rgba(255,255,255,0.03)',
|
|
1121
|
+
borderWidth: StyleSheet.hairlineWidth,
|
|
1122
|
+
borderColor: colors.borderLight,
|
|
1123
|
+
},
|
|
1124
|
+
stepperPillActive: {
|
|
1125
|
+
backgroundColor: 'rgba(181, 189, 204, 0.10)',
|
|
1126
|
+
borderColor: colors.borderHighlight,
|
|
1127
|
+
},
|
|
1128
|
+
stepperPillComplete: {
|
|
1129
|
+
backgroundColor: 'rgba(198, 205, 217, 0.08)',
|
|
1130
|
+
borderColor: 'rgba(198, 205, 217, 0.22)',
|
|
1131
|
+
},
|
|
1132
|
+
stepperPillIndex: {
|
|
1133
|
+
width: 18,
|
|
1134
|
+
height: 18,
|
|
1135
|
+
borderRadius: 9,
|
|
1136
|
+
alignItems: 'center',
|
|
1137
|
+
justifyContent: 'center',
|
|
1138
|
+
backgroundColor: colors.bgInput,
|
|
1139
|
+
borderWidth: StyleSheet.hairlineWidth,
|
|
1140
|
+
borderColor: colors.border,
|
|
1141
|
+
},
|
|
1142
|
+
stepperPillIndexActive: {
|
|
1143
|
+
backgroundColor: colors.accent,
|
|
1144
|
+
borderColor: colors.accent,
|
|
1145
|
+
},
|
|
1146
|
+
stepperPillIndexComplete: {
|
|
1147
|
+
backgroundColor: colors.statusComplete,
|
|
1148
|
+
borderColor: colors.statusComplete,
|
|
1149
|
+
},
|
|
1150
|
+
stepperPillIndexText: {
|
|
1151
|
+
...typography.caption,
|
|
1152
|
+
color: colors.textPrimary,
|
|
1153
|
+
fontWeight: '700',
|
|
1154
|
+
fontSize: 10,
|
|
1155
|
+
lineHeight: 12,
|
|
1156
|
+
},
|
|
1157
|
+
stepperPillIndexTextActive: {
|
|
1158
|
+
color: colors.black,
|
|
1159
|
+
},
|
|
1160
|
+
stepperPillTitle: {
|
|
1161
|
+
...typography.caption,
|
|
1162
|
+
color: colors.textSecondary,
|
|
1163
|
+
fontWeight: '600',
|
|
1164
|
+
fontSize: 10,
|
|
1165
|
+
lineHeight: 12,
|
|
1166
|
+
},
|
|
1167
|
+
stepperPillTitleActive: {
|
|
1168
|
+
color: colors.textPrimary,
|
|
1169
|
+
},
|
|
1170
|
+
stepperPillTitleComplete: {
|
|
1171
|
+
color: colors.textPrimary,
|
|
1172
|
+
},
|
|
1173
|
+
introSectionHeader: {
|
|
1174
|
+
gap: spacing.xs,
|
|
1175
|
+
paddingHorizontal: spacing.xs,
|
|
1176
|
+
},
|
|
1177
|
+
introSectionTitle: {
|
|
1178
|
+
...typography.caption,
|
|
1179
|
+
color: colors.textMuted,
|
|
1180
|
+
textTransform: 'uppercase',
|
|
1181
|
+
letterSpacing: 0.9,
|
|
1182
|
+
},
|
|
1183
|
+
introSectionSubtitle: {
|
|
1184
|
+
...typography.body,
|
|
1185
|
+
color: colors.textSecondary,
|
|
1186
|
+
},
|
|
1187
|
+
introFeatureGrid: {
|
|
1188
|
+
gap: spacing.sm,
|
|
1189
|
+
},
|
|
1190
|
+
introFeatureCard: {
|
|
1191
|
+
flexDirection: 'row',
|
|
1192
|
+
gap: spacing.md,
|
|
1193
|
+
borderRadius: 18,
|
|
1194
|
+
borderWidth: StyleSheet.hairlineWidth,
|
|
1195
|
+
borderColor: colors.borderLight,
|
|
1196
|
+
backgroundColor: 'rgba(7, 9, 12, 0.72)',
|
|
1197
|
+
paddingHorizontal: spacing.md,
|
|
735
1198
|
paddingVertical: spacing.md,
|
|
736
1199
|
},
|
|
737
|
-
|
|
1200
|
+
introFeatureIconWrap: {
|
|
1201
|
+
width: 36,
|
|
1202
|
+
height: 36,
|
|
1203
|
+
borderRadius: 14,
|
|
1204
|
+
alignItems: 'center',
|
|
1205
|
+
justifyContent: 'center',
|
|
1206
|
+
backgroundColor: 'rgba(255,255,255,0.07)',
|
|
1207
|
+
borderWidth: StyleSheet.hairlineWidth,
|
|
1208
|
+
borderColor: colors.borderLight,
|
|
1209
|
+
},
|
|
1210
|
+
introFeatureTextWrap: {
|
|
738
1211
|
flex: 1,
|
|
1212
|
+
gap: 2,
|
|
739
1213
|
},
|
|
740
|
-
|
|
1214
|
+
introFeatureTitle: {
|
|
1215
|
+
...typography.headline,
|
|
1216
|
+
fontSize: 14,
|
|
1217
|
+
},
|
|
1218
|
+
introFeatureDescription: {
|
|
1219
|
+
...typography.caption,
|
|
1220
|
+
color: colors.textSecondary,
|
|
1221
|
+
lineHeight: 18,
|
|
1222
|
+
},
|
|
1223
|
+
introContextCard: {
|
|
1224
|
+
borderRadius: 20,
|
|
1225
|
+
borderWidth: StyleSheet.hairlineWidth,
|
|
1226
|
+
borderColor: colors.borderHighlight,
|
|
1227
|
+
backgroundColor: 'rgba(12, 14, 18, 0.76)',
|
|
1228
|
+
padding: spacing.lg,
|
|
1229
|
+
gap: spacing.sm,
|
|
1230
|
+
overflow: 'hidden',
|
|
1231
|
+
},
|
|
1232
|
+
introContextTitle: {
|
|
1233
|
+
...typography.headline,
|
|
1234
|
+
},
|
|
1235
|
+
introContextText: {
|
|
1236
|
+
...typography.body,
|
|
1237
|
+
color: colors.textSecondary,
|
|
1238
|
+
},
|
|
1239
|
+
introContextPillRow: {
|
|
1240
|
+
flexDirection: 'row',
|
|
1241
|
+
flexWrap: 'wrap',
|
|
741
1242
|
gap: spacing.sm,
|
|
742
|
-
|
|
1243
|
+
marginTop: spacing.xs,
|
|
743
1244
|
},
|
|
744
1245
|
introFooter: {
|
|
745
|
-
|
|
1246
|
+
gap: spacing.sm,
|
|
1247
|
+
},
|
|
1248
|
+
introNextButton: {
|
|
1249
|
+
borderRadius: 18,
|
|
1250
|
+
backgroundColor: colors.accent,
|
|
1251
|
+
minHeight: 58,
|
|
1252
|
+
flexDirection: 'row',
|
|
1253
|
+
alignItems: 'center',
|
|
1254
|
+
justifyContent: 'center',
|
|
1255
|
+
gap: spacing.sm,
|
|
1256
|
+
},
|
|
1257
|
+
introNextButtonPressed: {
|
|
1258
|
+
backgroundColor: colors.accentPressed,
|
|
1259
|
+
},
|
|
1260
|
+
introNextButtonText: {
|
|
1261
|
+
...typography.headline,
|
|
1262
|
+
color: colors.black,
|
|
1263
|
+
fontSize: 17,
|
|
1264
|
+
fontWeight: '700',
|
|
746
1265
|
},
|
|
747
1266
|
scroll: {
|
|
748
1267
|
flex: 1,
|
|
@@ -753,13 +1272,20 @@ const styles = StyleSheet.create({
|
|
|
753
1272
|
paddingBottom: spacing.xxl,
|
|
754
1273
|
gap: spacing.md,
|
|
755
1274
|
},
|
|
756
|
-
|
|
757
|
-
|
|
1275
|
+
connectRoot: {
|
|
1276
|
+
flex: 1,
|
|
1277
|
+
},
|
|
1278
|
+
connectFooter: {
|
|
1279
|
+
paddingHorizontal: spacing.lg,
|
|
1280
|
+
paddingTop: spacing.sm,
|
|
1281
|
+
paddingBottom: spacing.md,
|
|
1282
|
+
},
|
|
1283
|
+
connectHero: {
|
|
1284
|
+
borderRadius: 22,
|
|
758
1285
|
borderWidth: StyleSheet.hairlineWidth,
|
|
759
1286
|
borderColor: colors.borderHighlight,
|
|
760
|
-
backgroundColor: colors.black,
|
|
761
1287
|
padding: spacing.lg,
|
|
762
|
-
gap: spacing.
|
|
1288
|
+
gap: spacing.xs,
|
|
763
1289
|
overflow: 'hidden',
|
|
764
1290
|
},
|
|
765
1291
|
heroTopRow: {
|
|
@@ -768,9 +1294,9 @@ const styles = StyleSheet.create({
|
|
|
768
1294
|
alignItems: 'center',
|
|
769
1295
|
},
|
|
770
1296
|
heroIconWrap: {
|
|
771
|
-
width:
|
|
772
|
-
height:
|
|
773
|
-
borderRadius:
|
|
1297
|
+
width: 32,
|
|
1298
|
+
height: 32,
|
|
1299
|
+
borderRadius: 16,
|
|
774
1300
|
backgroundColor: 'rgba(255, 255, 255, 0.08)',
|
|
775
1301
|
alignItems: 'center',
|
|
776
1302
|
justifyContent: 'center',
|
|
@@ -788,91 +1314,185 @@ const styles = StyleSheet.create({
|
|
|
788
1314
|
cancelBtnPressed: {
|
|
789
1315
|
opacity: 0.75,
|
|
790
1316
|
},
|
|
1317
|
+
connectEyebrow: {
|
|
1318
|
+
...typography.caption,
|
|
1319
|
+
color: colors.textSecondary,
|
|
1320
|
+
textTransform: 'uppercase',
|
|
1321
|
+
letterSpacing: 0.8,
|
|
1322
|
+
},
|
|
791
1323
|
heroTitle: {
|
|
792
1324
|
...typography.largeTitle,
|
|
793
|
-
fontSize:
|
|
794
|
-
|
|
1325
|
+
fontSize: 24,
|
|
1326
|
+
lineHeight: 28,
|
|
1327
|
+
letterSpacing: -0.45,
|
|
795
1328
|
},
|
|
796
1329
|
heroDescription: {
|
|
797
1330
|
...typography.body,
|
|
798
1331
|
color: colors.textSecondary,
|
|
1332
|
+
fontSize: 14,
|
|
1333
|
+
lineHeight: 19,
|
|
799
1334
|
},
|
|
800
|
-
|
|
1335
|
+
formCard: {
|
|
1336
|
+
borderRadius: 20,
|
|
1337
|
+
borderWidth: StyleSheet.hairlineWidth,
|
|
1338
|
+
borderColor: colors.borderHighlight,
|
|
1339
|
+
backgroundColor: 'rgba(12, 14, 18, 0.76)',
|
|
1340
|
+
padding: spacing.lg,
|
|
1341
|
+
gap: spacing.md,
|
|
1342
|
+
overflow: 'hidden',
|
|
1343
|
+
},
|
|
1344
|
+
formSectionHeader: {
|
|
1345
|
+
gap: spacing.xs,
|
|
1346
|
+
},
|
|
1347
|
+
formSectionEyebrow: {
|
|
801
1348
|
...typography.caption,
|
|
802
|
-
textTransform: 'uppercase',
|
|
803
|
-
letterSpacing: 0.9,
|
|
804
1349
|
color: colors.textMuted,
|
|
1350
|
+
textTransform: 'uppercase',
|
|
1351
|
+
letterSpacing: 0.85,
|
|
805
1352
|
},
|
|
806
|
-
|
|
1353
|
+
formSectionTitle: {
|
|
1354
|
+
...typography.headline,
|
|
1355
|
+
fontSize: 15,
|
|
1356
|
+
lineHeight: 21,
|
|
1357
|
+
},
|
|
1358
|
+
modeCardGrid: {
|
|
807
1359
|
flexDirection: 'row',
|
|
808
1360
|
gap: spacing.sm,
|
|
809
|
-
|
|
1361
|
+
},
|
|
1362
|
+
modePresetCard: {
|
|
1363
|
+
flex: 1,
|
|
1364
|
+
minHeight: 146,
|
|
1365
|
+
borderRadius: 18,
|
|
810
1366
|
borderWidth: StyleSheet.hairlineWidth,
|
|
811
1367
|
borderColor: colors.borderLight,
|
|
812
|
-
backgroundColor:
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
1368
|
+
backgroundColor: 'rgba(255,255,255,0.03)',
|
|
1369
|
+
padding: spacing.md,
|
|
1370
|
+
gap: spacing.sm,
|
|
1371
|
+
justifyContent: 'space-between',
|
|
816
1372
|
},
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
1373
|
+
modePresetCardSelected: {
|
|
1374
|
+
backgroundColor: 'rgba(181, 189, 204, 0.10)',
|
|
1375
|
+
borderColor: colors.borderHighlight,
|
|
1376
|
+
},
|
|
1377
|
+
modePresetCardPressed: {
|
|
1378
|
+
opacity: 0.84,
|
|
1379
|
+
},
|
|
1380
|
+
modePresetIconWrap: {
|
|
1381
|
+
width: 38,
|
|
1382
|
+
height: 38,
|
|
1383
|
+
borderRadius: 14,
|
|
821
1384
|
alignItems: 'center',
|
|
822
1385
|
justifyContent: 'center',
|
|
823
|
-
backgroundColor:
|
|
824
|
-
|
|
1386
|
+
backgroundColor: colors.bgInput,
|
|
1387
|
+
borderWidth: StyleSheet.hairlineWidth,
|
|
1388
|
+
borderColor: colors.borderLight,
|
|
825
1389
|
},
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
1390
|
+
modePresetIconWrapSelected: {
|
|
1391
|
+
backgroundColor: 'rgba(181, 189, 204, 0.16)',
|
|
1392
|
+
borderColor: colors.borderHighlight,
|
|
829
1393
|
},
|
|
830
|
-
|
|
1394
|
+
modePresetTitle: {
|
|
831
1395
|
...typography.headline,
|
|
832
|
-
|
|
833
|
-
fontSize: 14,
|
|
1396
|
+
fontSize: 15,
|
|
834
1397
|
},
|
|
835
|
-
|
|
1398
|
+
modePresetDescription: {
|
|
836
1399
|
...typography.caption,
|
|
837
1400
|
color: colors.textSecondary,
|
|
838
1401
|
lineHeight: 18,
|
|
839
1402
|
},
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
1403
|
+
helperText: {
|
|
1404
|
+
...typography.caption,
|
|
1405
|
+
color: colors.textMuted,
|
|
1406
|
+
lineHeight: 18,
|
|
1407
|
+
},
|
|
1408
|
+
commandPanel: {
|
|
1409
|
+
gap: spacing.sm,
|
|
1410
|
+
paddingTop: spacing.xs,
|
|
1411
|
+
},
|
|
1412
|
+
commandCard: {
|
|
1413
|
+
gap: spacing.xs,
|
|
1414
|
+
borderRadius: 16,
|
|
1415
|
+
borderWidth: StyleSheet.hairlineWidth,
|
|
1416
|
+
borderColor: colors.borderLight,
|
|
1417
|
+
backgroundColor: 'rgba(255,255,255,0.03)',
|
|
1418
|
+
padding: spacing.md,
|
|
1419
|
+
},
|
|
1420
|
+
commandCardHeader: {
|
|
844
1421
|
flexDirection: 'row',
|
|
845
1422
|
alignItems: 'center',
|
|
846
|
-
justifyContent: '
|
|
1423
|
+
justifyContent: 'space-between',
|
|
847
1424
|
gap: spacing.sm,
|
|
848
|
-
width: '100%',
|
|
849
1425
|
},
|
|
850
|
-
|
|
851
|
-
|
|
1426
|
+
commandCardHeaderLeft: {
|
|
1427
|
+
flexDirection: 'row',
|
|
1428
|
+
alignItems: 'center',
|
|
1429
|
+
gap: 6,
|
|
1430
|
+
flex: 1,
|
|
1431
|
+
minWidth: 0,
|
|
852
1432
|
},
|
|
853
|
-
|
|
854
|
-
...typography.
|
|
1433
|
+
commandCardLabel: {
|
|
1434
|
+
...typography.caption,
|
|
1435
|
+
color: colors.textSecondary,
|
|
1436
|
+
fontWeight: '600',
|
|
1437
|
+
letterSpacing: 0.2,
|
|
1438
|
+
},
|
|
1439
|
+
commandCopyButton: {
|
|
1440
|
+
minHeight: 30,
|
|
1441
|
+
paddingHorizontal: spacing.sm,
|
|
1442
|
+
borderRadius: radius.full,
|
|
1443
|
+
borderWidth: StyleSheet.hairlineWidth,
|
|
1444
|
+
borderColor: colors.border,
|
|
1445
|
+
backgroundColor: colors.bgInput,
|
|
1446
|
+
flexDirection: 'row',
|
|
1447
|
+
alignItems: 'center',
|
|
1448
|
+
justifyContent: 'center',
|
|
1449
|
+
gap: 4,
|
|
1450
|
+
flexShrink: 0,
|
|
1451
|
+
},
|
|
1452
|
+
commandCopyButtonCopied: {
|
|
1453
|
+
backgroundColor: colors.accent,
|
|
1454
|
+
borderColor: colors.accent,
|
|
1455
|
+
},
|
|
1456
|
+
commandCopyButtonPressed: {
|
|
1457
|
+
opacity: 0.84,
|
|
1458
|
+
},
|
|
1459
|
+
commandCopyButtonText: {
|
|
1460
|
+
...typography.caption,
|
|
1461
|
+
color: colors.textPrimary,
|
|
1462
|
+
fontWeight: '600',
|
|
1463
|
+
},
|
|
1464
|
+
commandCopyButtonTextCopied: {
|
|
855
1465
|
color: colors.black,
|
|
856
|
-
fontSize: 18,
|
|
857
|
-
fontWeight: '700',
|
|
858
1466
|
},
|
|
859
|
-
|
|
860
|
-
borderRadius:
|
|
1467
|
+
commandCodeWrap: {
|
|
1468
|
+
borderRadius: 12,
|
|
861
1469
|
borderWidth: StyleSheet.hairlineWidth,
|
|
862
|
-
borderColor: colors.
|
|
863
|
-
backgroundColor: colors.
|
|
864
|
-
|
|
1470
|
+
borderColor: colors.border,
|
|
1471
|
+
backgroundColor: colors.bgInput,
|
|
1472
|
+
paddingHorizontal: spacing.sm,
|
|
1473
|
+
paddingVertical: spacing.sm,
|
|
1474
|
+
},
|
|
1475
|
+
commandCodeText: {
|
|
1476
|
+
...typography.mono,
|
|
1477
|
+
color: colors.textPrimary,
|
|
1478
|
+
fontSize: 12,
|
|
1479
|
+
lineHeight: 18,
|
|
1480
|
+
},
|
|
1481
|
+
commandCardHint: {
|
|
1482
|
+
...typography.caption,
|
|
1483
|
+
color: colors.textMuted,
|
|
1484
|
+
lineHeight: 16,
|
|
1485
|
+
},
|
|
1486
|
+
fieldGroup: {
|
|
865
1487
|
gap: spacing.sm,
|
|
866
|
-
overflow: 'hidden',
|
|
867
1488
|
},
|
|
868
1489
|
label: {
|
|
869
1490
|
...typography.caption,
|
|
870
1491
|
textTransform: 'uppercase',
|
|
871
|
-
letterSpacing: 0.
|
|
1492
|
+
letterSpacing: 0.85,
|
|
872
1493
|
color: colors.textMuted,
|
|
873
1494
|
},
|
|
874
1495
|
tokenHeaderRow: {
|
|
875
|
-
marginTop: spacing.xs,
|
|
876
1496
|
flexDirection: 'row',
|
|
877
1497
|
justifyContent: 'space-between',
|
|
878
1498
|
alignItems: 'center',
|
|
@@ -883,32 +1503,46 @@ const styles = StyleSheet.create({
|
|
|
883
1503
|
color: colors.textMuted,
|
|
884
1504
|
fontSize: 11,
|
|
885
1505
|
},
|
|
886
|
-
|
|
1506
|
+
inputRow: {
|
|
1507
|
+
flex: 1,
|
|
1508
|
+
minWidth: 0,
|
|
887
1509
|
flexDirection: 'row',
|
|
888
1510
|
alignItems: 'center',
|
|
889
|
-
gap: spacing.xs,
|
|
890
|
-
},
|
|
891
|
-
input: {
|
|
892
|
-
...typography.body,
|
|
893
|
-
color: colors.textPrimary,
|
|
894
1511
|
borderWidth: 1,
|
|
895
1512
|
borderColor: colors.border,
|
|
896
|
-
borderRadius:
|
|
897
|
-
backgroundColor:
|
|
1513
|
+
borderRadius: 16,
|
|
1514
|
+
backgroundColor: 'rgba(255,255,255,0.03)',
|
|
1515
|
+
minHeight: 54,
|
|
898
1516
|
paddingHorizontal: spacing.md,
|
|
899
|
-
|
|
1517
|
+
gap: spacing.sm,
|
|
900
1518
|
},
|
|
901
|
-
|
|
1519
|
+
inputIconWrap: {
|
|
1520
|
+
width: 24,
|
|
1521
|
+
alignItems: 'center',
|
|
1522
|
+
justifyContent: 'center',
|
|
1523
|
+
},
|
|
1524
|
+
inputText: {
|
|
902
1525
|
flex: 1,
|
|
1526
|
+
minWidth: 0,
|
|
1527
|
+
...typography.body,
|
|
1528
|
+
color: colors.textPrimary,
|
|
1529
|
+
paddingVertical: spacing.md,
|
|
1530
|
+
},
|
|
1531
|
+
tokenInputWrap: {
|
|
1532
|
+
flexDirection: 'row',
|
|
1533
|
+
alignItems: 'center',
|
|
1534
|
+
gap: spacing.xs,
|
|
1535
|
+
minWidth: 0,
|
|
903
1536
|
},
|
|
904
1537
|
tokenRevealBtn: {
|
|
905
1538
|
minWidth: 74,
|
|
906
|
-
|
|
1539
|
+
minHeight: 54,
|
|
1540
|
+
flexShrink: 0,
|
|
1541
|
+
borderRadius: 16,
|
|
907
1542
|
borderWidth: 1,
|
|
908
1543
|
borderColor: colors.border,
|
|
909
|
-
backgroundColor: colors.
|
|
1544
|
+
backgroundColor: colors.bgInput,
|
|
910
1545
|
paddingHorizontal: spacing.sm,
|
|
911
|
-
paddingVertical: spacing.sm,
|
|
912
1546
|
flexDirection: 'row',
|
|
913
1547
|
alignItems: 'center',
|
|
914
1548
|
justifyContent: 'center',
|
|
@@ -922,39 +1556,12 @@ const styles = StyleSheet.create({
|
|
|
922
1556
|
color: colors.textSecondary,
|
|
923
1557
|
fontWeight: '600',
|
|
924
1558
|
},
|
|
925
|
-
modeRow: {
|
|
926
|
-
flexDirection: 'row',
|
|
927
|
-
gap: spacing.sm,
|
|
928
|
-
marginBottom: spacing.xs,
|
|
929
|
-
},
|
|
930
|
-
modeButton: {
|
|
931
|
-
flex: 1,
|
|
932
|
-
minHeight: 44,
|
|
933
|
-
borderRadius: radius.md,
|
|
934
|
-
borderWidth: 1,
|
|
935
|
-
borderColor: colors.border,
|
|
936
|
-
backgroundColor: colors.bgMain,
|
|
937
|
-
paddingHorizontal: spacing.md,
|
|
938
|
-
flexDirection: 'row',
|
|
939
|
-
alignItems: 'center',
|
|
940
|
-
justifyContent: 'center',
|
|
941
|
-
gap: spacing.xs,
|
|
942
|
-
},
|
|
943
|
-
modeButtonPressed: {
|
|
944
|
-
opacity: 0.82,
|
|
945
|
-
},
|
|
946
|
-
modeButtonText: {
|
|
947
|
-
...typography.caption,
|
|
948
|
-
color: colors.textPrimary,
|
|
949
|
-
fontWeight: '700',
|
|
950
|
-
},
|
|
951
1559
|
scanButton: {
|
|
952
|
-
|
|
953
|
-
borderRadius: radius.md,
|
|
1560
|
+
borderRadius: 16,
|
|
954
1561
|
borderWidth: 1,
|
|
955
1562
|
borderColor: colors.border,
|
|
956
|
-
backgroundColor: colors.
|
|
957
|
-
minHeight:
|
|
1563
|
+
backgroundColor: colors.bgInput,
|
|
1564
|
+
minHeight: 50,
|
|
958
1565
|
paddingHorizontal: spacing.md,
|
|
959
1566
|
flexDirection: 'row',
|
|
960
1567
|
alignItems: 'center',
|
|
@@ -965,19 +1572,15 @@ const styles = StyleSheet.create({
|
|
|
965
1572
|
opacity: 0.82,
|
|
966
1573
|
},
|
|
967
1574
|
scanButtonText: {
|
|
968
|
-
...typography.
|
|
1575
|
+
...typography.headline,
|
|
969
1576
|
color: colors.textPrimary,
|
|
970
1577
|
fontWeight: '700',
|
|
971
1578
|
},
|
|
972
|
-
helperText: {
|
|
973
|
-
...typography.caption,
|
|
974
|
-
color: colors.textMuted,
|
|
975
|
-
},
|
|
976
1579
|
previewWrap: {
|
|
977
|
-
borderRadius:
|
|
1580
|
+
borderRadius: 16,
|
|
978
1581
|
borderWidth: StyleSheet.hairlineWidth,
|
|
979
1582
|
borderColor: colors.borderLight,
|
|
980
|
-
backgroundColor:
|
|
1583
|
+
backgroundColor: 'rgba(255,255,255,0.03)',
|
|
981
1584
|
paddingHorizontal: spacing.md,
|
|
982
1585
|
paddingVertical: spacing.sm,
|
|
983
1586
|
gap: spacing.xs,
|
|
@@ -991,6 +1594,32 @@ const styles = StyleSheet.create({
|
|
|
991
1594
|
color: colors.textPrimary,
|
|
992
1595
|
fontSize: 13,
|
|
993
1596
|
},
|
|
1597
|
+
statusBanner: {
|
|
1598
|
+
flexDirection: 'row',
|
|
1599
|
+
alignItems: 'flex-start',
|
|
1600
|
+
gap: spacing.sm,
|
|
1601
|
+
borderRadius: 16,
|
|
1602
|
+
borderWidth: StyleSheet.hairlineWidth,
|
|
1603
|
+
paddingHorizontal: spacing.md,
|
|
1604
|
+
paddingVertical: spacing.sm,
|
|
1605
|
+
},
|
|
1606
|
+
statusBannerWarning: {
|
|
1607
|
+
backgroundColor: 'rgba(247, 210, 126, 0.08)',
|
|
1608
|
+
borderColor: 'rgba(247, 210, 126, 0.22)',
|
|
1609
|
+
},
|
|
1610
|
+
statusBannerSuccess: {
|
|
1611
|
+
backgroundColor: 'rgba(198, 205, 217, 0.10)',
|
|
1612
|
+
borderColor: 'rgba(198, 205, 217, 0.22)',
|
|
1613
|
+
},
|
|
1614
|
+
statusBannerError: {
|
|
1615
|
+
backgroundColor: colors.errorBg,
|
|
1616
|
+
borderColor: 'rgba(239, 68, 68, 0.28)',
|
|
1617
|
+
},
|
|
1618
|
+
statusBannerText: {
|
|
1619
|
+
flex: 1,
|
|
1620
|
+
...typography.caption,
|
|
1621
|
+
lineHeight: 18,
|
|
1622
|
+
},
|
|
994
1623
|
warningText: {
|
|
995
1624
|
...typography.caption,
|
|
996
1625
|
color: '#F7D27E',
|
|
@@ -1006,7 +1635,6 @@ const styles = StyleSheet.create({
|
|
|
1006
1635
|
actionRow: {
|
|
1007
1636
|
flexDirection: 'row',
|
|
1008
1637
|
gap: spacing.sm,
|
|
1009
|
-
marginTop: spacing.sm,
|
|
1010
1638
|
},
|
|
1011
1639
|
secondaryButton: {
|
|
1012
1640
|
flex: 1,
|
|
@@ -1016,9 +1644,9 @@ const styles = StyleSheet.create({
|
|
|
1016
1644
|
justifyContent: 'center',
|
|
1017
1645
|
borderWidth: 1,
|
|
1018
1646
|
borderColor: colors.border,
|
|
1019
|
-
backgroundColor: colors.
|
|
1020
|
-
borderRadius:
|
|
1021
|
-
|
|
1647
|
+
backgroundColor: colors.bgInput,
|
|
1648
|
+
borderRadius: 16,
|
|
1649
|
+
minHeight: 54,
|
|
1022
1650
|
},
|
|
1023
1651
|
secondaryButtonPressed: {
|
|
1024
1652
|
opacity: 0.8,
|
|
@@ -1038,8 +1666,8 @@ const styles = StyleSheet.create({
|
|
|
1038
1666
|
alignItems: 'center',
|
|
1039
1667
|
justifyContent: 'center',
|
|
1040
1668
|
backgroundColor: colors.accent,
|
|
1041
|
-
borderRadius:
|
|
1042
|
-
|
|
1669
|
+
borderRadius: 16,
|
|
1670
|
+
minHeight: 54,
|
|
1043
1671
|
},
|
|
1044
1672
|
primaryButtonPressed: {
|
|
1045
1673
|
backgroundColor: colors.accentPressed,
|
|
@@ -1059,10 +1687,10 @@ const styles = StyleSheet.create({
|
|
|
1059
1687
|
paddingHorizontal: spacing.lg,
|
|
1060
1688
|
},
|
|
1061
1689
|
scannerSheet: {
|
|
1062
|
-
borderRadius:
|
|
1690
|
+
borderRadius: 22,
|
|
1063
1691
|
borderWidth: StyleSheet.hairlineWidth,
|
|
1064
1692
|
borderColor: colors.borderHighlight,
|
|
1065
|
-
backgroundColor:
|
|
1693
|
+
backgroundColor: '#07090C',
|
|
1066
1694
|
padding: spacing.lg,
|
|
1067
1695
|
gap: spacing.md,
|
|
1068
1696
|
},
|
|
@@ -1091,11 +1719,11 @@ const styles = StyleSheet.create({
|
|
|
1091
1719
|
scannerCameraFrame: {
|
|
1092
1720
|
width: '100%',
|
|
1093
1721
|
aspectRatio: 1,
|
|
1094
|
-
borderRadius:
|
|
1722
|
+
borderRadius: 18,
|
|
1095
1723
|
overflow: 'hidden',
|
|
1096
1724
|
borderWidth: StyleSheet.hairlineWidth,
|
|
1097
1725
|
borderColor: colors.borderLight,
|
|
1098
|
-
backgroundColor: colors.
|
|
1726
|
+
backgroundColor: colors.bgItem,
|
|
1099
1727
|
},
|
|
1100
1728
|
scannerCamera: {
|
|
1101
1729
|
flex: 1,
|
|
@@ -1115,20 +1743,4 @@ const styles = StyleSheet.create({
|
|
|
1115
1743
|
...typography.caption,
|
|
1116
1744
|
color: colors.textMuted,
|
|
1117
1745
|
},
|
|
1118
|
-
hintCard: {
|
|
1119
|
-
borderRadius: radius.md,
|
|
1120
|
-
borderWidth: StyleSheet.hairlineWidth,
|
|
1121
|
-
borderColor: colors.borderLight,
|
|
1122
|
-
backgroundColor: colors.black,
|
|
1123
|
-
padding: spacing.lg,
|
|
1124
|
-
gap: spacing.sm,
|
|
1125
|
-
},
|
|
1126
|
-
hintTitle: {
|
|
1127
|
-
...typography.headline,
|
|
1128
|
-
color: colors.textPrimary,
|
|
1129
|
-
},
|
|
1130
|
-
hintText: {
|
|
1131
|
-
...typography.caption,
|
|
1132
|
-
color: colors.textSecondary,
|
|
1133
|
-
},
|
|
1134
1746
|
});
|