clawdex-mobile 2.0.1 → 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 +41 -0
- package/AGENTS.md +263 -110
- package/README.md +15 -4
- package/apps/mobile/.env.example +2 -2
- package/apps/mobile/App.tsx +175 -14
- package/apps/mobile/app.json +27 -9
- package/apps/mobile/eas.json +14 -4
- package/apps/mobile/package.json +14 -13
- package/apps/mobile/src/api/__tests__/chatMapping.test.ts +219 -0
- package/apps/mobile/src/api/__tests__/client.test.ts +587 -6
- package/apps/mobile/src/api/__tests__/ws.test.ts +27 -0
- package/apps/mobile/src/api/account.ts +47 -0
- package/apps/mobile/src/api/chatMapping.ts +435 -18
- package/apps/mobile/src/api/client.ts +321 -36
- package/apps/mobile/src/api/rateLimits.ts +143 -0
- package/apps/mobile/src/api/types.ts +107 -0
- package/apps/mobile/src/api/ws.ts +10 -1
- package/apps/mobile/src/components/ChatHeader.tsx +12 -12
- package/apps/mobile/src/components/ChatInput.tsx +154 -88
- package/apps/mobile/src/components/ChatMessage.tsx +548 -93
- package/apps/mobile/src/components/ComposerUsageLimits.tsx +167 -0
- package/apps/mobile/src/components/SelectionSheet.tsx +466 -0
- package/apps/mobile/src/components/ToolBlock.tsx +17 -15
- package/apps/mobile/src/components/VoiceRecordingWaveform.tsx +181 -0
- package/apps/mobile/src/components/WorkspacePickerModal.tsx +812 -0
- package/apps/mobile/src/components/__tests__/chat-input-layout.test.ts +35 -0
- package/apps/mobile/src/components/__tests__/chatImageSource.test.ts +44 -0
- package/apps/mobile/src/components/__tests__/composerUsageLimits.test.ts +138 -0
- package/apps/mobile/src/components/__tests__/voiceWaveform.test.ts +31 -0
- package/apps/mobile/src/components/chat-input-layout.ts +59 -0
- package/apps/mobile/src/components/chatImageSource.ts +86 -0
- package/apps/mobile/src/components/usageLimitBadges.ts +109 -0
- package/apps/mobile/src/components/voiceWaveform.ts +46 -0
- package/apps/mobile/src/config.ts +9 -2
- package/apps/mobile/src/hooks/useVoiceRecorder.ts +8 -1
- package/apps/mobile/src/navigation/DrawerContent.tsx +607 -457
- package/apps/mobile/src/navigation/__tests__/chatThreadTree.test.ts +89 -0
- package/apps/mobile/src/navigation/__tests__/drawerChats.test.ts +65 -0
- package/apps/mobile/src/navigation/chatThreadTree.ts +191 -0
- package/apps/mobile/src/navigation/drawerChats.ts +9 -0
- package/apps/mobile/src/screens/GitScreen.tsx +2 -0
- package/apps/mobile/src/screens/MainScreen.tsx +4239 -1237
- package/apps/mobile/src/screens/OnboardingScreen.tsx +924 -310
- package/apps/mobile/src/screens/SettingsScreen.tsx +256 -226
- package/apps/mobile/src/screens/TerminalScreen.tsx +2 -5
- package/apps/mobile/src/screens/__tests__/agentThreadDisplay.test.ts +80 -0
- package/apps/mobile/src/screens/__tests__/agentThreads.test.ts +170 -0
- package/apps/mobile/src/screens/__tests__/planCardState.test.ts +88 -0
- package/apps/mobile/src/screens/__tests__/subAgentTranscript.test.ts +102 -0
- package/apps/mobile/src/screens/__tests__/transcriptMessages.test.ts +97 -0
- package/apps/mobile/src/screens/agentThreadDisplay.ts +261 -0
- package/apps/mobile/src/screens/agentThreads.ts +167 -0
- package/apps/mobile/src/screens/planCardState.ts +40 -0
- package/apps/mobile/src/screens/subAgentTranscript.ts +149 -0
- package/apps/mobile/src/screens/transcriptMessages.ts +102 -0
- package/apps/mobile/src/theme.ts +6 -12
- package/bin/clawdex.js +7 -6
- package/codex-rust-bridge +0 -0
- package/codex-rust-bridge.exe +0 -0
- package/docs/codex-app-server-cli-gap-tracker.md +14 -5
- package/docs/privacy-policy.md +54 -0
- package/docs/setup-and-operations.md +21 -15
- package/docs/terms-of-service.md +33 -0
- package/docs/troubleshooting.md +15 -19
- package/package.json +6 -5
- 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/mac-bridge/package.json +6 -6
- package/services/rust-bridge/Cargo.lock +56 -47
- package/services/rust-bridge/Cargo.toml +1 -1
- package/services/rust-bridge/package.json +1 -1
- package/services/rust-bridge/src/main.rs +517 -9
- package/site/index.html +54 -0
- package/site/privacy/index.html +80 -0
- package/site/styles.css +135 -0
- package/site/support/index.html +51 -0
- package/site/terms/index.html +68 -0
|
@@ -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,97 +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
|
-
autoCapitalize="none"
|
|
423
|
-
autoCorrect={false}
|
|
424
|
-
keyboardType="url"
|
|
425
|
-
placeholder="http://100.101.102.103:8787"
|
|
426
|
-
placeholderTextColor={colors.textMuted}
|
|
427
|
-
style={styles.input}
|
|
428
|
-
returnKeyType="done"
|
|
429
|
-
onSubmitEditing={() => {
|
|
430
|
-
void handleSave();
|
|
431
|
-
}}
|
|
432
|
-
/>
|
|
433
|
-
<View style={styles.tokenHeaderRow}>
|
|
434
|
-
<Text style={styles.label}>Bridge Token</Text>
|
|
435
|
-
<Text style={styles.optionalLabel}>Required</Text>
|
|
436
|
-
</View>
|
|
437
|
-
<View style={styles.tokenInputWrap}>
|
|
438
|
-
<TextInput
|
|
439
|
-
value={tokenInput}
|
|
440
|
-
onChangeText={(value) => {
|
|
441
|
-
setTokenInput(value);
|
|
442
|
-
setConnectionCheck({ kind: 'idle' });
|
|
443
|
-
}}
|
|
444
|
-
autoCapitalize="none"
|
|
445
|
-
autoCorrect={false}
|
|
446
|
-
keyboardType="default"
|
|
447
|
-
placeholder="Paste bridge token"
|
|
448
|
-
placeholderTextColor={colors.textMuted}
|
|
449
|
-
style={[styles.input, styles.tokenInput]}
|
|
450
|
-
secureTextEntry={tokenHidden}
|
|
451
|
-
returnKeyType="done"
|
|
452
|
-
onSubmitEditing={() => {
|
|
453
|
-
void handleSave();
|
|
454
|
-
}}
|
|
455
|
-
/>
|
|
456
|
-
<Pressable
|
|
457
|
-
onPress={() => setTokenHidden((prev) => !prev)}
|
|
458
|
-
style={({ pressed }) => [
|
|
459
|
-
styles.tokenRevealBtn,
|
|
460
|
-
pressed && styles.tokenRevealBtnPressed,
|
|
461
|
-
]}
|
|
462
|
-
>
|
|
463
|
-
<Ionicons
|
|
464
|
-
name={tokenHidden ? 'eye-outline' : 'eye-off-outline'}
|
|
465
|
-
size={16}
|
|
466
|
-
color={colors.textSecondary}
|
|
467
|
-
/>
|
|
468
|
-
<Text style={styles.tokenRevealBtnText}>
|
|
469
|
-
{tokenHidden ? 'Show' : 'Hide'}
|
|
470
|
-
</Text>
|
|
471
|
-
</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>
|
|
472
495
|
</View>
|
|
496
|
+
|
|
473
497
|
<Pressable
|
|
474
498
|
onPress={() => {
|
|
475
499
|
void openScanner();
|
|
@@ -480,15 +504,96 @@ export function OnboardingScreen({
|
|
|
480
504
|
]}
|
|
481
505
|
>
|
|
482
506
|
<Ionicons name="qr-code-outline" size={16} color={colors.textPrimary} />
|
|
483
|
-
<Text style={styles.scanButtonText}>Scan
|
|
507
|
+
<Text style={styles.scanButtonText}>Scan bridge QR</Text>
|
|
484
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
|
+
|
|
485
590
|
<Text style={styles.helperText}>
|
|
486
591
|
URL supports `http`, `https`, `ws`, and `wss`. `/rpc` is added automatically.
|
|
487
592
|
</Text>
|
|
488
593
|
|
|
489
594
|
{normalizedBridgeUrl ? (
|
|
490
595
|
<View style={styles.previewWrap}>
|
|
491
|
-
<Text style={styles.previewLabel}>Normalized
|
|
596
|
+
<Text style={styles.previewLabel}>Normalized target</Text>
|
|
492
597
|
<Text selectable style={styles.previewValue}>
|
|
493
598
|
{normalizedBridgeUrl}
|
|
494
599
|
</Text>
|
|
@@ -496,17 +601,38 @@ export function OnboardingScreen({
|
|
|
496
601
|
) : null}
|
|
497
602
|
|
|
498
603
|
{insecureRemoteWarning ? (
|
|
499
|
-
<
|
|
604
|
+
<StatusBanner
|
|
605
|
+
tone="warning"
|
|
606
|
+
icon="warning-outline"
|
|
607
|
+
message={insecureRemoteWarning}
|
|
608
|
+
/>
|
|
500
609
|
) : null}
|
|
501
610
|
|
|
502
|
-
{formError ?
|
|
611
|
+
{formError ? (
|
|
612
|
+
<StatusBanner tone="error" icon="close-circle-outline" message={formError} />
|
|
613
|
+
) : null}
|
|
503
614
|
{connectionCheck.kind === 'success' ? (
|
|
504
|
-
<
|
|
615
|
+
<StatusBanner
|
|
616
|
+
tone="success"
|
|
617
|
+
icon="checkmark-circle-outline"
|
|
618
|
+
message={connectionCheck.message}
|
|
619
|
+
/>
|
|
505
620
|
) : null}
|
|
506
621
|
{connectionCheck.kind === 'error' ? (
|
|
507
|
-
<
|
|
622
|
+
<StatusBanner
|
|
623
|
+
tone="error"
|
|
624
|
+
icon="alert-circle-outline"
|
|
625
|
+
message={connectionCheck.message}
|
|
626
|
+
/>
|
|
508
627
|
) : null}
|
|
509
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
|
+
|
|
510
636
|
<View style={styles.actionRow}>
|
|
511
637
|
<Pressable
|
|
512
638
|
onPress={() => {
|
|
@@ -547,15 +673,14 @@ export function OnboardingScreen({
|
|
|
547
673
|
</Text>
|
|
548
674
|
</Pressable>
|
|
549
675
|
</View>
|
|
676
|
+
</BlurView>
|
|
677
|
+
</ScrollView>
|
|
678
|
+
{showOnboardingDock ? (
|
|
679
|
+
<View style={styles.connectFooter}>
|
|
680
|
+
<OnboardingStepDock currentStage={currentSetupStage} />
|
|
550
681
|
</View>
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
<Text style={styles.hintTitle}>Quick Setup</Text>
|
|
554
|
-
<Text style={styles.hintText}>1. Start the bridge in Local (LAN) or Tailscale mode.</Text>
|
|
555
|
-
<Text style={styles.hintText}>2. Pick Local or Tailscale above, then confirm bridge URL.</Text>
|
|
556
|
-
<Text style={styles.hintText}>3. Scan bridge QR, then test connection and continue.</Text>
|
|
557
|
-
</View>
|
|
558
|
-
</ScrollView>
|
|
682
|
+
) : null}
|
|
683
|
+
</View>
|
|
559
684
|
)}
|
|
560
685
|
<Modal
|
|
561
686
|
animationType="slide"
|
|
@@ -606,7 +731,7 @@ export function OnboardingScreen({
|
|
|
606
731
|
);
|
|
607
732
|
}
|
|
608
733
|
|
|
609
|
-
function
|
|
734
|
+
function IntroFeatureCard({
|
|
610
735
|
icon,
|
|
611
736
|
title,
|
|
612
737
|
description,
|
|
@@ -616,7 +741,7 @@ function IntroFeatureRow({
|
|
|
616
741
|
description: string;
|
|
617
742
|
}) {
|
|
618
743
|
return (
|
|
619
|
-
<View style={styles.
|
|
744
|
+
<View style={styles.introFeatureCard}>
|
|
620
745
|
<View style={styles.introFeatureIconWrap}>
|
|
621
746
|
<Ionicons name={icon} size={16} color={colors.textPrimary} />
|
|
622
747
|
</View>
|
|
@@ -628,6 +753,177 @@ function IntroFeatureRow({
|
|
|
628
753
|
);
|
|
629
754
|
}
|
|
630
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
|
+
|
|
631
927
|
function parsePairingPayload(rawValue: string): PairingPayload | null {
|
|
632
928
|
const raw = rawValue.trim();
|
|
633
929
|
if (!raw) {
|
|
@@ -705,6 +1001,26 @@ const styles = StyleSheet.create({
|
|
|
705
1001
|
keyboardAvoiding: {
|
|
706
1002
|
flex: 1,
|
|
707
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
|
+
},
|
|
708
1024
|
introRoot: {
|
|
709
1025
|
flex: 1,
|
|
710
1026
|
paddingHorizontal: spacing.lg,
|
|
@@ -712,11 +1028,16 @@ const styles = StyleSheet.create({
|
|
|
712
1028
|
paddingBottom: spacing.xxl,
|
|
713
1029
|
gap: spacing.md,
|
|
714
1030
|
},
|
|
1031
|
+
introHeader: {
|
|
1032
|
+
flexDirection: 'row',
|
|
1033
|
+
alignItems: 'center',
|
|
1034
|
+
justifyContent: 'space-between',
|
|
1035
|
+
gap: spacing.md,
|
|
1036
|
+
},
|
|
715
1037
|
introBrandRow: {
|
|
716
1038
|
flexDirection: 'row',
|
|
717
1039
|
alignItems: 'center',
|
|
718
1040
|
gap: spacing.sm,
|
|
719
|
-
paddingHorizontal: spacing.xs,
|
|
720
1041
|
},
|
|
721
1042
|
introBrandName: {
|
|
722
1043
|
...typography.headline,
|
|
@@ -724,23 +1045,223 @@ const styles = StyleSheet.create({
|
|
|
724
1045
|
fontSize: 18,
|
|
725
1046
|
letterSpacing: -0.2,
|
|
726
1047
|
},
|
|
727
|
-
|
|
1048
|
+
introScroll: {
|
|
728
1049
|
flex: 1,
|
|
1050
|
+
},
|
|
1051
|
+
introScrollContent: {
|
|
1052
|
+
paddingBottom: spacing.lg,
|
|
729
1053
|
gap: spacing.md,
|
|
730
1054
|
},
|
|
731
|
-
|
|
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: {
|
|
732
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,
|
|
733
1198
|
paddingVertical: spacing.md,
|
|
734
1199
|
},
|
|
735
|
-
|
|
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: {
|
|
736
1211
|
flex: 1,
|
|
1212
|
+
gap: 2,
|
|
737
1213
|
},
|
|
738
|
-
|
|
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',
|
|
739
1242
|
gap: spacing.sm,
|
|
740
|
-
|
|
1243
|
+
marginTop: spacing.xs,
|
|
741
1244
|
},
|
|
742
1245
|
introFooter: {
|
|
743
|
-
|
|
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',
|
|
744
1265
|
},
|
|
745
1266
|
scroll: {
|
|
746
1267
|
flex: 1,
|
|
@@ -751,13 +1272,20 @@ const styles = StyleSheet.create({
|
|
|
751
1272
|
paddingBottom: spacing.xxl,
|
|
752
1273
|
gap: spacing.md,
|
|
753
1274
|
},
|
|
754
|
-
|
|
755
|
-
|
|
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,
|
|
756
1285
|
borderWidth: StyleSheet.hairlineWidth,
|
|
757
1286
|
borderColor: colors.borderHighlight,
|
|
758
|
-
backgroundColor: colors.black,
|
|
759
1287
|
padding: spacing.lg,
|
|
760
|
-
gap: spacing.
|
|
1288
|
+
gap: spacing.xs,
|
|
761
1289
|
overflow: 'hidden',
|
|
762
1290
|
},
|
|
763
1291
|
heroTopRow: {
|
|
@@ -766,9 +1294,9 @@ const styles = StyleSheet.create({
|
|
|
766
1294
|
alignItems: 'center',
|
|
767
1295
|
},
|
|
768
1296
|
heroIconWrap: {
|
|
769
|
-
width:
|
|
770
|
-
height:
|
|
771
|
-
borderRadius:
|
|
1297
|
+
width: 32,
|
|
1298
|
+
height: 32,
|
|
1299
|
+
borderRadius: 16,
|
|
772
1300
|
backgroundColor: 'rgba(255, 255, 255, 0.08)',
|
|
773
1301
|
alignItems: 'center',
|
|
774
1302
|
justifyContent: 'center',
|
|
@@ -786,91 +1314,185 @@ const styles = StyleSheet.create({
|
|
|
786
1314
|
cancelBtnPressed: {
|
|
787
1315
|
opacity: 0.75,
|
|
788
1316
|
},
|
|
1317
|
+
connectEyebrow: {
|
|
1318
|
+
...typography.caption,
|
|
1319
|
+
color: colors.textSecondary,
|
|
1320
|
+
textTransform: 'uppercase',
|
|
1321
|
+
letterSpacing: 0.8,
|
|
1322
|
+
},
|
|
789
1323
|
heroTitle: {
|
|
790
1324
|
...typography.largeTitle,
|
|
791
|
-
fontSize:
|
|
792
|
-
|
|
1325
|
+
fontSize: 24,
|
|
1326
|
+
lineHeight: 28,
|
|
1327
|
+
letterSpacing: -0.45,
|
|
793
1328
|
},
|
|
794
1329
|
heroDescription: {
|
|
795
1330
|
...typography.body,
|
|
796
1331
|
color: colors.textSecondary,
|
|
1332
|
+
fontSize: 14,
|
|
1333
|
+
lineHeight: 19,
|
|
797
1334
|
},
|
|
798
|
-
|
|
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: {
|
|
799
1348
|
...typography.caption,
|
|
800
|
-
textTransform: 'uppercase',
|
|
801
|
-
letterSpacing: 0.9,
|
|
802
1349
|
color: colors.textMuted,
|
|
1350
|
+
textTransform: 'uppercase',
|
|
1351
|
+
letterSpacing: 0.85,
|
|
803
1352
|
},
|
|
804
|
-
|
|
1353
|
+
formSectionTitle: {
|
|
1354
|
+
...typography.headline,
|
|
1355
|
+
fontSize: 15,
|
|
1356
|
+
lineHeight: 21,
|
|
1357
|
+
},
|
|
1358
|
+
modeCardGrid: {
|
|
805
1359
|
flexDirection: 'row',
|
|
806
1360
|
gap: spacing.sm,
|
|
807
|
-
|
|
1361
|
+
},
|
|
1362
|
+
modePresetCard: {
|
|
1363
|
+
flex: 1,
|
|
1364
|
+
minHeight: 146,
|
|
1365
|
+
borderRadius: 18,
|
|
808
1366
|
borderWidth: StyleSheet.hairlineWidth,
|
|
809
1367
|
borderColor: colors.borderLight,
|
|
810
|
-
backgroundColor:
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
1368
|
+
backgroundColor: 'rgba(255,255,255,0.03)',
|
|
1369
|
+
padding: spacing.md,
|
|
1370
|
+
gap: spacing.sm,
|
|
1371
|
+
justifyContent: 'space-between',
|
|
814
1372
|
},
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
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,
|
|
819
1384
|
alignItems: 'center',
|
|
820
1385
|
justifyContent: 'center',
|
|
821
|
-
backgroundColor:
|
|
822
|
-
|
|
1386
|
+
backgroundColor: colors.bgInput,
|
|
1387
|
+
borderWidth: StyleSheet.hairlineWidth,
|
|
1388
|
+
borderColor: colors.borderLight,
|
|
823
1389
|
},
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
1390
|
+
modePresetIconWrapSelected: {
|
|
1391
|
+
backgroundColor: 'rgba(181, 189, 204, 0.16)',
|
|
1392
|
+
borderColor: colors.borderHighlight,
|
|
827
1393
|
},
|
|
828
|
-
|
|
1394
|
+
modePresetTitle: {
|
|
829
1395
|
...typography.headline,
|
|
830
|
-
|
|
831
|
-
fontSize: 14,
|
|
1396
|
+
fontSize: 15,
|
|
832
1397
|
},
|
|
833
|
-
|
|
1398
|
+
modePresetDescription: {
|
|
834
1399
|
...typography.caption,
|
|
835
1400
|
color: colors.textSecondary,
|
|
836
1401
|
lineHeight: 18,
|
|
837
1402
|
},
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
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: {
|
|
842
1421
|
flexDirection: 'row',
|
|
843
1422
|
alignItems: 'center',
|
|
844
|
-
justifyContent: '
|
|
1423
|
+
justifyContent: 'space-between',
|
|
845
1424
|
gap: spacing.sm,
|
|
846
|
-
width: '100%',
|
|
847
1425
|
},
|
|
848
|
-
|
|
849
|
-
|
|
1426
|
+
commandCardHeaderLeft: {
|
|
1427
|
+
flexDirection: 'row',
|
|
1428
|
+
alignItems: 'center',
|
|
1429
|
+
gap: 6,
|
|
1430
|
+
flex: 1,
|
|
1431
|
+
minWidth: 0,
|
|
850
1432
|
},
|
|
851
|
-
|
|
852
|
-
...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: {
|
|
853
1465
|
color: colors.black,
|
|
854
|
-
fontSize: 18,
|
|
855
|
-
fontWeight: '700',
|
|
856
1466
|
},
|
|
857
|
-
|
|
858
|
-
borderRadius:
|
|
1467
|
+
commandCodeWrap: {
|
|
1468
|
+
borderRadius: 12,
|
|
859
1469
|
borderWidth: StyleSheet.hairlineWidth,
|
|
860
|
-
borderColor: colors.
|
|
861
|
-
backgroundColor: colors.
|
|
862
|
-
|
|
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: {
|
|
863
1487
|
gap: spacing.sm,
|
|
864
|
-
overflow: 'hidden',
|
|
865
1488
|
},
|
|
866
1489
|
label: {
|
|
867
1490
|
...typography.caption,
|
|
868
1491
|
textTransform: 'uppercase',
|
|
869
|
-
letterSpacing: 0.
|
|
1492
|
+
letterSpacing: 0.85,
|
|
870
1493
|
color: colors.textMuted,
|
|
871
1494
|
},
|
|
872
1495
|
tokenHeaderRow: {
|
|
873
|
-
marginTop: spacing.xs,
|
|
874
1496
|
flexDirection: 'row',
|
|
875
1497
|
justifyContent: 'space-between',
|
|
876
1498
|
alignItems: 'center',
|
|
@@ -881,32 +1503,46 @@ const styles = StyleSheet.create({
|
|
|
881
1503
|
color: colors.textMuted,
|
|
882
1504
|
fontSize: 11,
|
|
883
1505
|
},
|
|
884
|
-
|
|
1506
|
+
inputRow: {
|
|
1507
|
+
flex: 1,
|
|
1508
|
+
minWidth: 0,
|
|
885
1509
|
flexDirection: 'row',
|
|
886
1510
|
alignItems: 'center',
|
|
887
|
-
gap: spacing.xs,
|
|
888
|
-
},
|
|
889
|
-
input: {
|
|
890
|
-
...typography.body,
|
|
891
|
-
color: colors.textPrimary,
|
|
892
1511
|
borderWidth: 1,
|
|
893
1512
|
borderColor: colors.border,
|
|
894
|
-
borderRadius:
|
|
895
|
-
backgroundColor:
|
|
1513
|
+
borderRadius: 16,
|
|
1514
|
+
backgroundColor: 'rgba(255,255,255,0.03)',
|
|
1515
|
+
minHeight: 54,
|
|
896
1516
|
paddingHorizontal: spacing.md,
|
|
897
|
-
|
|
1517
|
+
gap: spacing.sm,
|
|
898
1518
|
},
|
|
899
|
-
|
|
1519
|
+
inputIconWrap: {
|
|
1520
|
+
width: 24,
|
|
1521
|
+
alignItems: 'center',
|
|
1522
|
+
justifyContent: 'center',
|
|
1523
|
+
},
|
|
1524
|
+
inputText: {
|
|
900
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,
|
|
901
1536
|
},
|
|
902
1537
|
tokenRevealBtn: {
|
|
903
1538
|
minWidth: 74,
|
|
904
|
-
|
|
1539
|
+
minHeight: 54,
|
|
1540
|
+
flexShrink: 0,
|
|
1541
|
+
borderRadius: 16,
|
|
905
1542
|
borderWidth: 1,
|
|
906
1543
|
borderColor: colors.border,
|
|
907
|
-
backgroundColor: colors.
|
|
1544
|
+
backgroundColor: colors.bgInput,
|
|
908
1545
|
paddingHorizontal: spacing.sm,
|
|
909
|
-
paddingVertical: spacing.sm,
|
|
910
1546
|
flexDirection: 'row',
|
|
911
1547
|
alignItems: 'center',
|
|
912
1548
|
justifyContent: 'center',
|
|
@@ -920,39 +1556,12 @@ const styles = StyleSheet.create({
|
|
|
920
1556
|
color: colors.textSecondary,
|
|
921
1557
|
fontWeight: '600',
|
|
922
1558
|
},
|
|
923
|
-
modeRow: {
|
|
924
|
-
flexDirection: 'row',
|
|
925
|
-
gap: spacing.sm,
|
|
926
|
-
marginBottom: spacing.xs,
|
|
927
|
-
},
|
|
928
|
-
modeButton: {
|
|
929
|
-
flex: 1,
|
|
930
|
-
minHeight: 44,
|
|
931
|
-
borderRadius: radius.md,
|
|
932
|
-
borderWidth: 1,
|
|
933
|
-
borderColor: colors.border,
|
|
934
|
-
backgroundColor: colors.bgMain,
|
|
935
|
-
paddingHorizontal: spacing.md,
|
|
936
|
-
flexDirection: 'row',
|
|
937
|
-
alignItems: 'center',
|
|
938
|
-
justifyContent: 'center',
|
|
939
|
-
gap: spacing.xs,
|
|
940
|
-
},
|
|
941
|
-
modeButtonPressed: {
|
|
942
|
-
opacity: 0.82,
|
|
943
|
-
},
|
|
944
|
-
modeButtonText: {
|
|
945
|
-
...typography.caption,
|
|
946
|
-
color: colors.textPrimary,
|
|
947
|
-
fontWeight: '700',
|
|
948
|
-
},
|
|
949
1559
|
scanButton: {
|
|
950
|
-
|
|
951
|
-
borderRadius: radius.md,
|
|
1560
|
+
borderRadius: 16,
|
|
952
1561
|
borderWidth: 1,
|
|
953
1562
|
borderColor: colors.border,
|
|
954
|
-
backgroundColor: colors.
|
|
955
|
-
minHeight:
|
|
1563
|
+
backgroundColor: colors.bgInput,
|
|
1564
|
+
minHeight: 50,
|
|
956
1565
|
paddingHorizontal: spacing.md,
|
|
957
1566
|
flexDirection: 'row',
|
|
958
1567
|
alignItems: 'center',
|
|
@@ -963,19 +1572,15 @@ const styles = StyleSheet.create({
|
|
|
963
1572
|
opacity: 0.82,
|
|
964
1573
|
},
|
|
965
1574
|
scanButtonText: {
|
|
966
|
-
...typography.
|
|
1575
|
+
...typography.headline,
|
|
967
1576
|
color: colors.textPrimary,
|
|
968
1577
|
fontWeight: '700',
|
|
969
1578
|
},
|
|
970
|
-
helperText: {
|
|
971
|
-
...typography.caption,
|
|
972
|
-
color: colors.textMuted,
|
|
973
|
-
},
|
|
974
1579
|
previewWrap: {
|
|
975
|
-
borderRadius:
|
|
1580
|
+
borderRadius: 16,
|
|
976
1581
|
borderWidth: StyleSheet.hairlineWidth,
|
|
977
1582
|
borderColor: colors.borderLight,
|
|
978
|
-
backgroundColor:
|
|
1583
|
+
backgroundColor: 'rgba(255,255,255,0.03)',
|
|
979
1584
|
paddingHorizontal: spacing.md,
|
|
980
1585
|
paddingVertical: spacing.sm,
|
|
981
1586
|
gap: spacing.xs,
|
|
@@ -989,6 +1594,32 @@ const styles = StyleSheet.create({
|
|
|
989
1594
|
color: colors.textPrimary,
|
|
990
1595
|
fontSize: 13,
|
|
991
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
|
+
},
|
|
992
1623
|
warningText: {
|
|
993
1624
|
...typography.caption,
|
|
994
1625
|
color: '#F7D27E',
|
|
@@ -1004,7 +1635,6 @@ const styles = StyleSheet.create({
|
|
|
1004
1635
|
actionRow: {
|
|
1005
1636
|
flexDirection: 'row',
|
|
1006
1637
|
gap: spacing.sm,
|
|
1007
|
-
marginTop: spacing.sm,
|
|
1008
1638
|
},
|
|
1009
1639
|
secondaryButton: {
|
|
1010
1640
|
flex: 1,
|
|
@@ -1014,9 +1644,9 @@ const styles = StyleSheet.create({
|
|
|
1014
1644
|
justifyContent: 'center',
|
|
1015
1645
|
borderWidth: 1,
|
|
1016
1646
|
borderColor: colors.border,
|
|
1017
|
-
backgroundColor: colors.
|
|
1018
|
-
borderRadius:
|
|
1019
|
-
|
|
1647
|
+
backgroundColor: colors.bgInput,
|
|
1648
|
+
borderRadius: 16,
|
|
1649
|
+
minHeight: 54,
|
|
1020
1650
|
},
|
|
1021
1651
|
secondaryButtonPressed: {
|
|
1022
1652
|
opacity: 0.8,
|
|
@@ -1036,8 +1666,8 @@ const styles = StyleSheet.create({
|
|
|
1036
1666
|
alignItems: 'center',
|
|
1037
1667
|
justifyContent: 'center',
|
|
1038
1668
|
backgroundColor: colors.accent,
|
|
1039
|
-
borderRadius:
|
|
1040
|
-
|
|
1669
|
+
borderRadius: 16,
|
|
1670
|
+
minHeight: 54,
|
|
1041
1671
|
},
|
|
1042
1672
|
primaryButtonPressed: {
|
|
1043
1673
|
backgroundColor: colors.accentPressed,
|
|
@@ -1057,10 +1687,10 @@ const styles = StyleSheet.create({
|
|
|
1057
1687
|
paddingHorizontal: spacing.lg,
|
|
1058
1688
|
},
|
|
1059
1689
|
scannerSheet: {
|
|
1060
|
-
borderRadius:
|
|
1690
|
+
borderRadius: 22,
|
|
1061
1691
|
borderWidth: StyleSheet.hairlineWidth,
|
|
1062
1692
|
borderColor: colors.borderHighlight,
|
|
1063
|
-
backgroundColor:
|
|
1693
|
+
backgroundColor: '#07090C',
|
|
1064
1694
|
padding: spacing.lg,
|
|
1065
1695
|
gap: spacing.md,
|
|
1066
1696
|
},
|
|
@@ -1089,11 +1719,11 @@ const styles = StyleSheet.create({
|
|
|
1089
1719
|
scannerCameraFrame: {
|
|
1090
1720
|
width: '100%',
|
|
1091
1721
|
aspectRatio: 1,
|
|
1092
|
-
borderRadius:
|
|
1722
|
+
borderRadius: 18,
|
|
1093
1723
|
overflow: 'hidden',
|
|
1094
1724
|
borderWidth: StyleSheet.hairlineWidth,
|
|
1095
1725
|
borderColor: colors.borderLight,
|
|
1096
|
-
backgroundColor: colors.
|
|
1726
|
+
backgroundColor: colors.bgItem,
|
|
1097
1727
|
},
|
|
1098
1728
|
scannerCamera: {
|
|
1099
1729
|
flex: 1,
|
|
@@ -1113,20 +1743,4 @@ const styles = StyleSheet.create({
|
|
|
1113
1743
|
...typography.caption,
|
|
1114
1744
|
color: colors.textMuted,
|
|
1115
1745
|
},
|
|
1116
|
-
hintCard: {
|
|
1117
|
-
borderRadius: radius.md,
|
|
1118
|
-
borderWidth: StyleSheet.hairlineWidth,
|
|
1119
|
-
borderColor: colors.borderLight,
|
|
1120
|
-
backgroundColor: colors.black,
|
|
1121
|
-
padding: spacing.lg,
|
|
1122
|
-
gap: spacing.sm,
|
|
1123
|
-
},
|
|
1124
|
-
hintTitle: {
|
|
1125
|
-
...typography.headline,
|
|
1126
|
-
color: colors.textPrimary,
|
|
1127
|
-
},
|
|
1128
|
-
hintText: {
|
|
1129
|
-
...typography.caption,
|
|
1130
|
-
color: colors.textSecondary,
|
|
1131
|
-
},
|
|
1132
1746
|
});
|