clawdex-mobile 1.3.2 → 2.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 +1 -1
- package/.github/workflows/npm-release.yml +18 -0
- package/AGENTS.md +3 -3
- package/README.md +101 -541
- package/apps/mobile/.env.example +1 -2
- package/apps/mobile/App.tsx +261 -68
- package/apps/mobile/app.json +31 -5
- package/apps/mobile/assets/brand/splash-icon-white.png +0 -0
- package/apps/mobile/eas.json +30 -0
- package/apps/mobile/package.json +22 -21
- package/apps/mobile/plugins/withAndroidCleartextTraffic.js +14 -0
- package/apps/mobile/src/api/__tests__/ws.test.ts +44 -6
- package/apps/mobile/src/api/chatMapping.ts +48 -8
- package/apps/mobile/src/api/client.ts +6 -0
- package/apps/mobile/src/api/types.ts +11 -0
- package/apps/mobile/src/api/ws.ts +52 -10
- package/apps/mobile/src/bridgeUrl.ts +105 -0
- package/apps/mobile/src/components/ActivityBar.tsx +32 -13
- package/apps/mobile/src/components/ChatHeader.tsx +3 -2
- package/apps/mobile/src/components/ChatInput.tsx +246 -91
- package/apps/mobile/src/components/ChatMessage.tsx +108 -4
- package/apps/mobile/src/config.ts +11 -29
- package/apps/mobile/src/hooks/useVoiceRecorder.ts +264 -0
- package/apps/mobile/src/navigation/DrawerContent.tsx +18 -8
- package/apps/mobile/src/screens/GitScreen.tsx +1 -1
- package/apps/mobile/src/screens/MainScreen.tsx +906 -268
- package/apps/mobile/src/screens/OnboardingScreen.tsx +1132 -0
- package/apps/mobile/src/screens/PrivacyScreen.tsx +1 -1
- package/apps/mobile/src/screens/SettingsScreen.tsx +65 -1
- package/apps/mobile/src/screens/TerminalScreen.tsx +1 -1
- package/apps/mobile/src/screens/TermsScreen.tsx +1 -1
- package/docs/app-review-notes.md +7 -2
- package/docs/eas-builds.md +91 -0
- package/docs/realtime-streaming-limitations.md +84 -0
- package/docs/setup-and-operations.md +239 -0
- package/docs/troubleshooting.md +121 -0
- package/docs/voice-transcription.md +87 -0
- package/package.json +8 -16
- package/scripts/setup-secure-dev.sh +122 -8
- package/scripts/setup-wizard.sh +342 -122
- package/scripts/start-bridge-secure.sh +7 -1
- package/scripts/sync-versions.js +63 -0
- package/services/rust-bridge/.env.example +1 -1
- package/services/rust-bridge/Cargo.lock +1104 -23
- package/services/rust-bridge/Cargo.toml +3 -1
- package/services/rust-bridge/package.json +1 -1
- package/services/rust-bridge/src/main.rs +587 -12
- package/apps/mobile/metro.config.js +0 -3
package/apps/mobile/.env.example
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
EXPO_PUBLIC_HOST_BRIDGE_URL=http://127.0.0.1:8787
|
|
2
1
|
EXPO_PUBLIC_HOST_BRIDGE_TOKEN=change-me
|
|
3
|
-
EXPO_PUBLIC_ALLOW_QUERY_TOKEN_AUTH=
|
|
2
|
+
EXPO_PUBLIC_ALLOW_QUERY_TOKEN_AUTH=true
|
|
4
3
|
EXPO_PUBLIC_ALLOW_INSECURE_REMOTE_BRIDGE=false
|
|
5
4
|
EXPO_PUBLIC_EXTERNAL_STATUS_FULL_SYNC_DEBOUNCE_MS=450
|
|
6
5
|
EXPO_PUBLIC_PRIVACY_POLICY_URL=https://example.com/privacy
|
package/apps/mobile/App.tsx
CHANGED
|
@@ -3,6 +3,7 @@ import 'react-native-gesture-handler';
|
|
|
3
3
|
import * as FileSystem from 'expo-file-system/legacy';
|
|
4
4
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
5
5
|
import {
|
|
6
|
+
ActivityIndicator,
|
|
6
7
|
Animated,
|
|
7
8
|
Keyboard,
|
|
8
9
|
PanResponder,
|
|
@@ -11,21 +12,25 @@ import {
|
|
|
11
12
|
useWindowDimensions,
|
|
12
13
|
View,
|
|
13
14
|
} from 'react-native';
|
|
15
|
+
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
|
14
16
|
|
|
15
17
|
import { HostBridgeApiClient } from './src/api/client';
|
|
16
18
|
import type { ApprovalMode, Chat, ReasoningEffort } from './src/api/types';
|
|
17
19
|
import { HostBridgeWsClient } from './src/api/ws';
|
|
20
|
+
import { normalizeBridgeUrlInput } from './src/bridgeUrl';
|
|
18
21
|
import { env } from './src/config';
|
|
19
22
|
import { DrawerContent } from './src/navigation/DrawerContent';
|
|
20
23
|
import { GitScreen } from './src/screens/GitScreen';
|
|
21
24
|
import { MainScreen, type MainScreenHandle } from './src/screens/MainScreen';
|
|
25
|
+
import { OnboardingScreen } from './src/screens/OnboardingScreen';
|
|
22
26
|
import { PrivacyScreen } from './src/screens/PrivacyScreen';
|
|
23
27
|
import { SettingsScreen } from './src/screens/SettingsScreen';
|
|
24
|
-
import { TerminalScreen } from './src/screens/TerminalScreen';
|
|
25
28
|
import { TermsScreen } from './src/screens/TermsScreen';
|
|
26
29
|
import { colors } from './src/theme';
|
|
27
30
|
|
|
28
|
-
type
|
|
31
|
+
type AppScreen = 'Main' | 'ChatGit' | 'Settings' | 'Privacy' | 'Terms';
|
|
32
|
+
type Screen = AppScreen | 'Onboarding';
|
|
33
|
+
type OnboardingMode = 'initial' | 'edit';
|
|
29
34
|
|
|
30
35
|
const DRAWER_WIDTH = 280;
|
|
31
36
|
const EDGE_SWIPE_WIDTH = 24;
|
|
@@ -34,22 +39,32 @@ const SWIPE_CLOSE_DISTANCE = 56;
|
|
|
34
39
|
const SWIPE_OPEN_VELOCITY = 0.4;
|
|
35
40
|
const SWIPE_CLOSE_VELOCITY = -0.4;
|
|
36
41
|
const APP_SETTINGS_FILE = 'clawdex-app-settings.json';
|
|
37
|
-
const APP_SETTINGS_VERSION =
|
|
42
|
+
const APP_SETTINGS_VERSION = 3;
|
|
38
43
|
|
|
39
44
|
export default function App() {
|
|
45
|
+
const [settingsLoaded, setSettingsLoaded] = useState(false);
|
|
46
|
+
const [bridgeUrl, setBridgeUrl] = useState<string | null>(null);
|
|
47
|
+
const [bridgeToken, setBridgeToken] = useState<string | null>(env.hostBridgeToken);
|
|
48
|
+
const [onboardingMode, setOnboardingMode] = useState<OnboardingMode>('initial');
|
|
49
|
+
const [onboardingReturnScreen, setOnboardingReturnScreen] =
|
|
50
|
+
useState<AppScreen>('Settings');
|
|
40
51
|
const ws = useMemo(
|
|
41
52
|
() =>
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
53
|
+
bridgeUrl
|
|
54
|
+
? new HostBridgeWsClient(bridgeUrl, {
|
|
55
|
+
authToken: bridgeToken ?? env.hostBridgeToken,
|
|
56
|
+
allowQueryTokenAuth: env.allowWsQueryTokenAuth
|
|
57
|
+
})
|
|
58
|
+
: null,
|
|
59
|
+
[bridgeToken, bridgeUrl]
|
|
47
60
|
);
|
|
48
61
|
const api = useMemo(
|
|
49
62
|
() =>
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
63
|
+
ws
|
|
64
|
+
? new HostBridgeApiClient({
|
|
65
|
+
ws,
|
|
66
|
+
})
|
|
67
|
+
: null,
|
|
53
68
|
[ws]
|
|
54
69
|
);
|
|
55
70
|
const mainRef = useRef<MainScreenHandle>(null);
|
|
@@ -70,12 +85,18 @@ export default function App() {
|
|
|
70
85
|
const { width: screenWidth } = useWindowDimensions();
|
|
71
86
|
|
|
72
87
|
useEffect(() => {
|
|
88
|
+
if (!ws) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
73
92
|
ws.connect();
|
|
74
93
|
return () => ws.disconnect();
|
|
75
94
|
}, [ws]);
|
|
76
95
|
|
|
77
96
|
const saveAppSettings = useCallback(
|
|
78
97
|
async (
|
|
98
|
+
nextBridgeUrl: string | null,
|
|
99
|
+
nextBridgeToken: string | null,
|
|
79
100
|
nextModelId: string | null,
|
|
80
101
|
nextEffort: ReasoningEffort | null,
|
|
81
102
|
nextApprovalMode: ApprovalMode
|
|
@@ -87,6 +108,8 @@ export default function App() {
|
|
|
87
108
|
|
|
88
109
|
const payload = JSON.stringify({
|
|
89
110
|
version: APP_SETTINGS_VERSION,
|
|
111
|
+
bridgeUrl: nextBridgeUrl,
|
|
112
|
+
bridgeToken: nextBridgeToken,
|
|
90
113
|
defaultModelId: nextModelId,
|
|
91
114
|
defaultReasoningEffort: nextEffort,
|
|
92
115
|
approvalMode: nextApprovalMode,
|
|
@@ -104,9 +127,21 @@ export default function App() {
|
|
|
104
127
|
useEffect(() => {
|
|
105
128
|
let cancelled = false;
|
|
106
129
|
|
|
130
|
+
const resetToDefaults = () => {
|
|
131
|
+
setDefaultModelId(null);
|
|
132
|
+
setDefaultReasoningEffort(null);
|
|
133
|
+
setApprovalMode('normal');
|
|
134
|
+
};
|
|
135
|
+
|
|
107
136
|
const loadSettings = async () => {
|
|
108
137
|
const settingsPath = getAppSettingsPath();
|
|
109
138
|
if (!settingsPath) {
|
|
139
|
+
if (!cancelled) {
|
|
140
|
+
resetToDefaults();
|
|
141
|
+
setBridgeUrl(null);
|
|
142
|
+
setBridgeToken(env.hostBridgeToken);
|
|
143
|
+
setSettingsLoaded(true);
|
|
144
|
+
}
|
|
110
145
|
return;
|
|
111
146
|
}
|
|
112
147
|
|
|
@@ -116,14 +151,21 @@ export default function App() {
|
|
|
116
151
|
return;
|
|
117
152
|
}
|
|
118
153
|
const parsed = parseAppSettings(raw);
|
|
154
|
+
const resolvedBridgeUrl = parsed.bridgeUrl ?? null;
|
|
155
|
+
setBridgeUrl(resolvedBridgeUrl);
|
|
156
|
+
setBridgeToken(parsed.bridgeToken ?? env.hostBridgeToken);
|
|
119
157
|
setDefaultModelId(parsed.defaultModelId);
|
|
120
158
|
setDefaultReasoningEffort(parsed.defaultReasoningEffort);
|
|
121
159
|
setApprovalMode(parsed.approvalMode);
|
|
122
160
|
} catch {
|
|
123
161
|
if (!cancelled) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
162
|
+
resetToDefaults();
|
|
163
|
+
setBridgeUrl(null);
|
|
164
|
+
setBridgeToken(env.hostBridgeToken);
|
|
165
|
+
}
|
|
166
|
+
} finally {
|
|
167
|
+
if (!cancelled) {
|
|
168
|
+
setSettingsLoaded(true);
|
|
127
169
|
}
|
|
128
170
|
}
|
|
129
171
|
};
|
|
@@ -280,20 +322,100 @@ export default function App() {
|
|
|
280
322
|
const normalizedEffort = normalizeReasoningEffort(effort);
|
|
281
323
|
setDefaultModelId(normalizedModelId);
|
|
282
324
|
setDefaultReasoningEffort(normalizedEffort);
|
|
283
|
-
void saveAppSettings(
|
|
325
|
+
void saveAppSettings(
|
|
326
|
+
bridgeUrl,
|
|
327
|
+
bridgeToken,
|
|
328
|
+
normalizedModelId,
|
|
329
|
+
normalizedEffort,
|
|
330
|
+
approvalMode
|
|
331
|
+
);
|
|
284
332
|
},
|
|
285
|
-
[approvalMode, saveAppSettings]
|
|
333
|
+
[approvalMode, bridgeToken, bridgeUrl, saveAppSettings]
|
|
286
334
|
);
|
|
287
335
|
|
|
288
336
|
const handleApprovalModeChange = useCallback(
|
|
289
337
|
(nextMode: ApprovalMode) => {
|
|
290
338
|
const normalizedMode = normalizeApprovalMode(nextMode);
|
|
291
339
|
setApprovalMode(normalizedMode);
|
|
292
|
-
void saveAppSettings(
|
|
340
|
+
void saveAppSettings(
|
|
341
|
+
bridgeUrl,
|
|
342
|
+
bridgeToken,
|
|
343
|
+
defaultModelId,
|
|
344
|
+
defaultReasoningEffort,
|
|
345
|
+
normalizedMode
|
|
346
|
+
);
|
|
347
|
+
},
|
|
348
|
+
[bridgeToken, bridgeUrl, defaultModelId, defaultReasoningEffort, saveAppSettings]
|
|
349
|
+
);
|
|
350
|
+
|
|
351
|
+
const handleBridgeUrlSaved = useCallback(
|
|
352
|
+
(nextBridgeUrl: string, nextBridgeToken: string | null) => {
|
|
353
|
+
const normalized = normalizeBridgeUrlInput(nextBridgeUrl);
|
|
354
|
+
if (!normalized) {
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
setBridgeUrl(normalized);
|
|
359
|
+
setBridgeToken(normalizeBridgeToken(nextBridgeToken));
|
|
360
|
+
setSelectedChatId(null);
|
|
361
|
+
setActiveChat(null);
|
|
362
|
+
setGitChat(null);
|
|
363
|
+
setPendingMainChatId(null);
|
|
364
|
+
setPendingMainChatSnapshot(null);
|
|
365
|
+
void saveAppSettings(
|
|
366
|
+
normalized,
|
|
367
|
+
normalizeBridgeToken(nextBridgeToken),
|
|
368
|
+
defaultModelId,
|
|
369
|
+
defaultReasoningEffort,
|
|
370
|
+
approvalMode
|
|
371
|
+
);
|
|
372
|
+
setCurrentScreen(onboardingMode === 'edit' ? onboardingReturnScreen : 'Main');
|
|
373
|
+
setOnboardingMode('edit');
|
|
374
|
+
closeDrawer();
|
|
293
375
|
},
|
|
294
|
-
[
|
|
376
|
+
[
|
|
377
|
+
approvalMode,
|
|
378
|
+
closeDrawer,
|
|
379
|
+
defaultModelId,
|
|
380
|
+
defaultReasoningEffort,
|
|
381
|
+
onboardingMode,
|
|
382
|
+
onboardingReturnScreen,
|
|
383
|
+
saveAppSettings,
|
|
384
|
+
]
|
|
295
385
|
);
|
|
296
386
|
|
|
387
|
+
const handleOpenBridgeUrlSettings = useCallback(() => {
|
|
388
|
+
setOnboardingMode(bridgeUrl ? 'edit' : 'initial');
|
|
389
|
+
setOnboardingReturnScreen(currentScreen === 'Onboarding' ? 'Settings' : currentScreen);
|
|
390
|
+
setCurrentScreen('Onboarding');
|
|
391
|
+
closeDrawer();
|
|
392
|
+
}, [bridgeUrl, closeDrawer, currentScreen]);
|
|
393
|
+
|
|
394
|
+
const handleResetOnboarding = useCallback(() => {
|
|
395
|
+
setBridgeUrl(null);
|
|
396
|
+
setBridgeToken(null);
|
|
397
|
+
setSelectedChatId(null);
|
|
398
|
+
setActiveChat(null);
|
|
399
|
+
setGitChat(null);
|
|
400
|
+
setPendingMainChatId(null);
|
|
401
|
+
setPendingMainChatSnapshot(null);
|
|
402
|
+
setOnboardingMode('initial');
|
|
403
|
+
setOnboardingReturnScreen('Main');
|
|
404
|
+
setCurrentScreen('Onboarding');
|
|
405
|
+
void saveAppSettings(null, null, defaultModelId, defaultReasoningEffort, approvalMode);
|
|
406
|
+
closeDrawer();
|
|
407
|
+
}, [
|
|
408
|
+
approvalMode,
|
|
409
|
+
closeDrawer,
|
|
410
|
+
defaultModelId,
|
|
411
|
+
defaultReasoningEffort,
|
|
412
|
+
saveAppSettings,
|
|
413
|
+
]);
|
|
414
|
+
|
|
415
|
+
const handleCancelOnboarding = useCallback(() => {
|
|
416
|
+
setCurrentScreen(onboardingReturnScreen);
|
|
417
|
+
}, [onboardingReturnScreen]);
|
|
418
|
+
|
|
297
419
|
const handleOpenChatGit = useCallback((chat: Chat) => {
|
|
298
420
|
setGitChat(chat);
|
|
299
421
|
setSelectedChatId(chat.id);
|
|
@@ -335,14 +457,45 @@ export default function App() {
|
|
|
335
457
|
setCurrentScreen('Terms');
|
|
336
458
|
}, []);
|
|
337
459
|
|
|
460
|
+
if (!settingsLoaded) {
|
|
461
|
+
return (
|
|
462
|
+
<SafeAreaProvider>
|
|
463
|
+
<View style={styles.loadingRoot}>
|
|
464
|
+
<ActivityIndicator size="large" color={colors.textMuted} />
|
|
465
|
+
</View>
|
|
466
|
+
</SafeAreaProvider>
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
if (!bridgeUrl || !api || !ws || currentScreen === 'Onboarding') {
|
|
471
|
+
const initialUrl = bridgeUrl ?? env.legacyHostBridgeUrl ?? '';
|
|
472
|
+
const initialToken = bridgeToken ?? env.hostBridgeToken ?? '';
|
|
473
|
+
const mode: OnboardingMode = bridgeUrl ? onboardingMode : 'initial';
|
|
474
|
+
const canCancel = mode === 'edit' && Boolean(bridgeUrl);
|
|
475
|
+
return (
|
|
476
|
+
<SafeAreaProvider>
|
|
477
|
+
<OnboardingScreen
|
|
478
|
+
mode={mode}
|
|
479
|
+
initialBridgeUrl={initialUrl}
|
|
480
|
+
initialBridgeToken={initialToken}
|
|
481
|
+
allowInsecureRemoteBridge={env.allowInsecureRemoteBridge}
|
|
482
|
+
allowQueryTokenAuth={env.allowWsQueryTokenAuth}
|
|
483
|
+
onSave={handleBridgeUrlSaved}
|
|
484
|
+
onCancel={canCancel ? handleCancelOnboarding : undefined}
|
|
485
|
+
/>
|
|
486
|
+
</SafeAreaProvider>
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
const activeApi = api;
|
|
491
|
+
const activeWs = ws;
|
|
492
|
+
|
|
338
493
|
const renderScreen = () => {
|
|
339
494
|
switch (currentScreen) {
|
|
340
|
-
case 'Terminal':
|
|
341
|
-
return <TerminalScreen api={api} ws={ws} onOpenDrawer={openDrawer} />;
|
|
342
495
|
case 'ChatGit':
|
|
343
496
|
return gitChat ? (
|
|
344
497
|
<GitScreen
|
|
345
|
-
api={
|
|
498
|
+
api={activeApi}
|
|
346
499
|
chat={gitChat}
|
|
347
500
|
onBack={handleCloseGit}
|
|
348
501
|
onChatUpdated={handleGitChatUpdated}
|
|
@@ -350,8 +503,8 @@ export default function App() {
|
|
|
350
503
|
) : (
|
|
351
504
|
<MainScreen
|
|
352
505
|
ref={mainRef}
|
|
353
|
-
api={
|
|
354
|
-
ws={
|
|
506
|
+
api={activeApi}
|
|
507
|
+
ws={activeWs}
|
|
355
508
|
onOpenDrawer={openDrawer}
|
|
356
509
|
onOpenGit={handleOpenChatGit}
|
|
357
510
|
defaultStartCwd={defaultStartCwd}
|
|
@@ -371,14 +524,16 @@ export default function App() {
|
|
|
371
524
|
case 'Settings':
|
|
372
525
|
return (
|
|
373
526
|
<SettingsScreen
|
|
374
|
-
api={
|
|
375
|
-
ws={
|
|
376
|
-
bridgeUrl={
|
|
527
|
+
api={activeApi}
|
|
528
|
+
ws={activeWs}
|
|
529
|
+
bridgeUrl={bridgeUrl}
|
|
377
530
|
defaultModelId={defaultModelId}
|
|
378
531
|
defaultReasoningEffort={defaultReasoningEffort}
|
|
379
532
|
onDefaultModelSettingsChange={handleDefaultModelSettingsChange}
|
|
380
533
|
approvalMode={approvalMode}
|
|
381
534
|
onApprovalModeChange={handleApprovalModeChange}
|
|
535
|
+
onEditBridgeUrl={handleOpenBridgeUrlSettings}
|
|
536
|
+
onResetOnboarding={handleResetOnboarding}
|
|
382
537
|
onOpenDrawer={openDrawer}
|
|
383
538
|
onOpenPrivacy={openPrivacy}
|
|
384
539
|
onOpenTerms={openTerms}
|
|
@@ -402,8 +557,8 @@ export default function App() {
|
|
|
402
557
|
return (
|
|
403
558
|
<MainScreen
|
|
404
559
|
ref={mainRef}
|
|
405
|
-
api={
|
|
406
|
-
ws={
|
|
560
|
+
api={activeApi}
|
|
561
|
+
ws={activeWs}
|
|
407
562
|
onOpenDrawer={openDrawer}
|
|
408
563
|
onOpenGit={handleOpenChatGit}
|
|
409
564
|
defaultStartCwd={defaultStartCwd}
|
|
@@ -424,47 +579,49 @@ export default function App() {
|
|
|
424
579
|
};
|
|
425
580
|
|
|
426
581
|
return (
|
|
427
|
-
<
|
|
428
|
-
{
|
|
429
|
-
|
|
430
|
-
{
|
|
431
|
-
|
|
582
|
+
<SafeAreaProvider>
|
|
583
|
+
<View style={styles.root}>
|
|
584
|
+
{/* Main content */}
|
|
585
|
+
<View style={[styles.screen, { width: screenWidth }]}>
|
|
586
|
+
{renderScreen()}
|
|
587
|
+
</View>
|
|
588
|
+
|
|
589
|
+
{/* Overlay */}
|
|
590
|
+
<Animated.View
|
|
591
|
+
pointerEvents={drawerOpen ? 'auto' : 'none'}
|
|
592
|
+
{...closeSwipeResponder.panHandlers}
|
|
593
|
+
style={[styles.overlay, { opacity: overlayAnim }]}
|
|
594
|
+
>
|
|
595
|
+
<Pressable style={StyleSheet.absoluteFill} onPress={closeDrawer} />
|
|
596
|
+
</Animated.View>
|
|
597
|
+
|
|
598
|
+
{/* Drawer */}
|
|
599
|
+
<Animated.View
|
|
600
|
+
{...closeSwipeResponder.panHandlers}
|
|
601
|
+
style={[
|
|
602
|
+
styles.drawer,
|
|
603
|
+
{ transform: [{ translateX: drawerAnim }] },
|
|
604
|
+
]}
|
|
605
|
+
>
|
|
606
|
+
<DrawerContent
|
|
607
|
+
api={activeApi}
|
|
608
|
+
ws={activeWs}
|
|
609
|
+
selectedChatId={selectedChatId}
|
|
610
|
+
selectedDefaultCwd={defaultStartCwd}
|
|
611
|
+
onSelectDefaultCwd={setDefaultStartCwd}
|
|
612
|
+
onSelectChat={handleSelectChat}
|
|
613
|
+
onNewChat={handleNewChat}
|
|
614
|
+
onNavigate={navigate}
|
|
615
|
+
/>
|
|
616
|
+
</Animated.View>
|
|
432
617
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
style={[styles.overlay, { opacity: overlayAnim }]}
|
|
438
|
-
>
|
|
439
|
-
<Pressable style={StyleSheet.absoluteFill} onPress={closeDrawer} />
|
|
440
|
-
</Animated.View>
|
|
441
|
-
|
|
442
|
-
{/* Drawer */}
|
|
443
|
-
<Animated.View
|
|
444
|
-
{...closeSwipeResponder.panHandlers}
|
|
445
|
-
style={[
|
|
446
|
-
styles.drawer,
|
|
447
|
-
{ transform: [{ translateX: drawerAnim }] },
|
|
448
|
-
]}
|
|
449
|
-
>
|
|
450
|
-
<DrawerContent
|
|
451
|
-
api={api}
|
|
452
|
-
ws={ws}
|
|
453
|
-
selectedChatId={selectedChatId}
|
|
454
|
-
selectedDefaultCwd={defaultStartCwd}
|
|
455
|
-
onSelectDefaultCwd={setDefaultStartCwd}
|
|
456
|
-
onSelectChat={handleSelectChat}
|
|
457
|
-
onNewChat={handleNewChat}
|
|
458
|
-
onNavigate={navigate}
|
|
618
|
+
<View
|
|
619
|
+
pointerEvents={drawerOpen ? 'none' : 'auto'}
|
|
620
|
+
style={styles.edgeSwipeZone}
|
|
621
|
+
{...openSwipeResponder.panHandlers}
|
|
459
622
|
/>
|
|
460
|
-
</
|
|
461
|
-
|
|
462
|
-
<View
|
|
463
|
-
pointerEvents={drawerOpen ? 'none' : 'auto'}
|
|
464
|
-
style={styles.edgeSwipeZone}
|
|
465
|
-
{...openSwipeResponder.panHandlers}
|
|
466
|
-
/>
|
|
467
|
-
</View>
|
|
623
|
+
</View>
|
|
624
|
+
</SafeAreaProvider>
|
|
468
625
|
);
|
|
469
626
|
}
|
|
470
627
|
|
|
@@ -478,12 +635,16 @@ function getAppSettingsPath(): string | null {
|
|
|
478
635
|
}
|
|
479
636
|
|
|
480
637
|
function parseAppSettings(raw: string): {
|
|
638
|
+
bridgeUrl: string | null;
|
|
639
|
+
bridgeToken: string | null;
|
|
481
640
|
defaultModelId: string | null;
|
|
482
641
|
defaultReasoningEffort: ReasoningEffort | null;
|
|
483
642
|
approvalMode: ApprovalMode;
|
|
484
643
|
} {
|
|
485
644
|
if (typeof raw !== 'string' || raw.trim().length === 0) {
|
|
486
645
|
return {
|
|
646
|
+
bridgeUrl: null,
|
|
647
|
+
bridgeToken: null,
|
|
487
648
|
defaultModelId: null,
|
|
488
649
|
defaultReasoningEffort: null,
|
|
489
650
|
approvalMode: 'normal',
|
|
@@ -492,12 +653,17 @@ function parseAppSettings(raw: string): {
|
|
|
492
653
|
|
|
493
654
|
try {
|
|
494
655
|
const parsed = JSON.parse(raw);
|
|
656
|
+
const parsedVersion = (parsed as { version?: unknown }).version;
|
|
495
657
|
if (
|
|
496
658
|
!parsed ||
|
|
497
659
|
typeof parsed !== 'object' ||
|
|
498
|
-
|
|
660
|
+
(parsedVersion !== 1 &&
|
|
661
|
+
parsedVersion !== 2 &&
|
|
662
|
+
parsedVersion !== APP_SETTINGS_VERSION)
|
|
499
663
|
) {
|
|
500
664
|
return {
|
|
665
|
+
bridgeUrl: null,
|
|
666
|
+
bridgeToken: null,
|
|
501
667
|
defaultModelId: null,
|
|
502
668
|
defaultReasoningEffort: null,
|
|
503
669
|
approvalMode: 'normal',
|
|
@@ -505,6 +671,8 @@ function parseAppSettings(raw: string): {
|
|
|
505
671
|
}
|
|
506
672
|
|
|
507
673
|
return {
|
|
674
|
+
bridgeUrl: normalizeBridgeUrl((parsed as { bridgeUrl?: unknown }).bridgeUrl),
|
|
675
|
+
bridgeToken: normalizeBridgeToken((parsed as { bridgeToken?: unknown }).bridgeToken),
|
|
508
676
|
defaultModelId: normalizeModelId(
|
|
509
677
|
(parsed as { defaultModelId?: unknown }).defaultModelId
|
|
510
678
|
),
|
|
@@ -517,6 +685,8 @@ function parseAppSettings(raw: string): {
|
|
|
517
685
|
};
|
|
518
686
|
} catch {
|
|
519
687
|
return {
|
|
688
|
+
bridgeUrl: null,
|
|
689
|
+
bridgeToken: null,
|
|
520
690
|
defaultModelId: null,
|
|
521
691
|
defaultReasoningEffort: null,
|
|
522
692
|
approvalMode: 'normal',
|
|
@@ -524,6 +694,23 @@ function parseAppSettings(raw: string): {
|
|
|
524
694
|
}
|
|
525
695
|
}
|
|
526
696
|
|
|
697
|
+
function normalizeBridgeUrl(value: unknown): string | null {
|
|
698
|
+
if (typeof value !== 'string') {
|
|
699
|
+
return null;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
return normalizeBridgeUrlInput(value);
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
function normalizeBridgeToken(value: unknown): string | null {
|
|
706
|
+
if (typeof value !== 'string') {
|
|
707
|
+
return null;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
const trimmed = value.trim();
|
|
711
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
712
|
+
}
|
|
713
|
+
|
|
527
714
|
function normalizeModelId(value: unknown): string | null {
|
|
528
715
|
if (typeof value !== 'string') {
|
|
529
716
|
return null;
|
|
@@ -562,6 +749,12 @@ const styles = StyleSheet.create({
|
|
|
562
749
|
flex: 1,
|
|
563
750
|
backgroundColor: colors.bgMain,
|
|
564
751
|
},
|
|
752
|
+
loadingRoot: {
|
|
753
|
+
flex: 1,
|
|
754
|
+
alignItems: 'center',
|
|
755
|
+
justifyContent: 'center',
|
|
756
|
+
backgroundColor: colors.bgMain,
|
|
757
|
+
},
|
|
565
758
|
screen: {
|
|
566
759
|
flex: 1,
|
|
567
760
|
},
|
package/apps/mobile/app.json
CHANGED
|
@@ -2,23 +2,37 @@
|
|
|
2
2
|
"expo": {
|
|
3
3
|
"name": "Clawdex Mobile",
|
|
4
4
|
"slug": "clawdex-mobile",
|
|
5
|
-
"version": "
|
|
5
|
+
"version": "2.0.0",
|
|
6
6
|
"orientation": "portrait",
|
|
7
7
|
"icon": "./assets/brand/app-icon.png",
|
|
8
8
|
"splash": {
|
|
9
|
-
"image": "./assets/brand/splash-icon.png",
|
|
9
|
+
"image": "./assets/brand/splash-icon-white.png",
|
|
10
10
|
"resizeMode": "contain",
|
|
11
11
|
"backgroundColor": "#000000"
|
|
12
12
|
},
|
|
13
13
|
"ios": {
|
|
14
|
-
"supportsTablet": true
|
|
14
|
+
"supportsTablet": true,
|
|
15
|
+
"infoPlist": {
|
|
16
|
+
"NSCameraUsageDescription": "Clawdex uses the camera to scan bridge pairing QR codes.",
|
|
17
|
+
"NSMicrophoneUsageDescription": "Clawdex uses the microphone for voice-to-text input.",
|
|
18
|
+
"ITSAppUsesNonExemptEncryption": false
|
|
19
|
+
},
|
|
20
|
+
"bundleIdentifier": "com.mohitpatil973.clawdexmobile"
|
|
15
21
|
},
|
|
16
22
|
"android": {
|
|
23
|
+
"usesCleartextTraffic": true,
|
|
17
24
|
"adaptiveIcon": {
|
|
18
25
|
"foregroundImage": "./assets/brand/adaptive-icon.png",
|
|
19
26
|
"backgroundColor": "#000000"
|
|
20
27
|
},
|
|
21
|
-
"
|
|
28
|
+
"permissions": [
|
|
29
|
+
"android.permission.CAMERA",
|
|
30
|
+
"android.permission.RECORD_AUDIO",
|
|
31
|
+
"android.permission.MODIFY_AUDIO_SETTINGS",
|
|
32
|
+
"android.permission.FOREGROUND_SERVICE",
|
|
33
|
+
"android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"
|
|
34
|
+
],
|
|
35
|
+
"package": "com.mohitpatil973.clawdexmobile"
|
|
22
36
|
},
|
|
23
37
|
"web": {
|
|
24
38
|
"favicon": "./assets/brand/favicon.png"
|
|
@@ -28,6 +42,18 @@
|
|
|
28
42
|
"android",
|
|
29
43
|
"web"
|
|
30
44
|
],
|
|
31
|
-
"
|
|
45
|
+
"experiments": {
|
|
46
|
+
"reactCompiler": true
|
|
47
|
+
},
|
|
48
|
+
"jsEngine": "hermes",
|
|
49
|
+
"plugins": [
|
|
50
|
+
"./plugins/withAndroidCleartextTraffic",
|
|
51
|
+
"expo-audio"
|
|
52
|
+
],
|
|
53
|
+
"extra": {
|
|
54
|
+
"eas": {
|
|
55
|
+
"projectId": "07937be8-e2f7-4731-8ea4-cefae952df79"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
32
58
|
}
|
|
33
59
|
}
|
|
Binary file
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cli": {
|
|
3
|
+
"version": ">= 18.0.6",
|
|
4
|
+
"appVersionSource": "remote"
|
|
5
|
+
},
|
|
6
|
+
"build": {
|
|
7
|
+
"development": {
|
|
8
|
+
"developmentClient": true,
|
|
9
|
+
"distribution": "internal",
|
|
10
|
+
"env": {
|
|
11
|
+
"EXPO_PUBLIC_ALLOW_QUERY_TOKEN_AUTH": "true"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"preview": {
|
|
15
|
+
"distribution": "internal",
|
|
16
|
+
"env": {
|
|
17
|
+
"EXPO_PUBLIC_ALLOW_QUERY_TOKEN_AUTH": "true"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"production": {
|
|
21
|
+
"autoIncrement": true,
|
|
22
|
+
"env": {
|
|
23
|
+
"EXPO_PUBLIC_ALLOW_QUERY_TOKEN_AUTH": "true"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"submit": {
|
|
28
|
+
"production": {}
|
|
29
|
+
}
|
|
30
|
+
}
|