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,4 +1,5 @@
|
|
|
1
1
|
import { Ionicons } from '@expo/vector-icons';
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
2
3
|
import {
|
|
3
4
|
ActivityIndicator,
|
|
4
5
|
Modal,
|
|
@@ -12,10 +13,7 @@ import {
|
|
|
12
13
|
} from 'react-native';
|
|
13
14
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
14
15
|
|
|
15
|
-
import type {
|
|
16
|
-
FileSystemEntry,
|
|
17
|
-
WorkspaceSummary,
|
|
18
|
-
} from '../api/types';
|
|
16
|
+
import type { FileSystemEntry, WorkspaceSummary } from '../api/types';
|
|
19
17
|
import { colors, radius, spacing, typography } from '../theme';
|
|
20
18
|
|
|
21
19
|
interface WorkspacePickerModalProps {
|
|
@@ -26,16 +24,20 @@ interface WorkspacePickerModalProps {
|
|
|
26
24
|
currentPath?: string | null;
|
|
27
25
|
parentPath?: string | null;
|
|
28
26
|
entries: FileSystemEntry[];
|
|
29
|
-
draftPath: string;
|
|
30
27
|
loadingRecent?: boolean;
|
|
31
28
|
loadingEntries?: boolean;
|
|
32
29
|
error?: string | null;
|
|
33
|
-
onDraftPathChange: (value: string) => void;
|
|
34
30
|
onBrowsePath: (path: string | null) => void;
|
|
35
31
|
onSelectPath: (path: string | null) => void;
|
|
36
32
|
onClose: () => void;
|
|
37
33
|
}
|
|
38
34
|
|
|
35
|
+
interface BreadcrumbItem {
|
|
36
|
+
key: string;
|
|
37
|
+
label: string;
|
|
38
|
+
path: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
39
41
|
export function WorkspacePickerModal({
|
|
40
42
|
visible,
|
|
41
43
|
selectedPath = null,
|
|
@@ -44,24 +46,39 @@ export function WorkspacePickerModal({
|
|
|
44
46
|
currentPath = null,
|
|
45
47
|
parentPath = null,
|
|
46
48
|
entries,
|
|
47
|
-
draftPath,
|
|
48
49
|
loadingRecent = false,
|
|
49
50
|
loadingEntries = false,
|
|
50
51
|
error = null,
|
|
51
|
-
onDraftPathChange,
|
|
52
52
|
onBrowsePath,
|
|
53
53
|
onSelectPath,
|
|
54
54
|
onClose,
|
|
55
55
|
}: WorkspacePickerModalProps) {
|
|
56
56
|
const insets = useSafeAreaInsets();
|
|
57
57
|
const { height: windowHeight } = useWindowDimensions();
|
|
58
|
+
const [searchQuery, setSearchQuery] = useState('');
|
|
58
59
|
const topInset = Math.max(insets.top + spacing.lg, 72);
|
|
59
60
|
const bottomInset = Math.max(insets.bottom + spacing.lg, 72);
|
|
60
61
|
const cardHeight = Math.min(
|
|
61
|
-
Math.max(
|
|
62
|
+
Math.max(560, Math.round(windowHeight * 0.82)),
|
|
62
63
|
windowHeight - topInset - bottomInset
|
|
63
64
|
);
|
|
64
65
|
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
if (!visible) {
|
|
68
|
+
setSearchQuery('');
|
|
69
|
+
}
|
|
70
|
+
}, [visible]);
|
|
71
|
+
|
|
72
|
+
const normalizedSearch = searchQuery.trim().toLowerCase();
|
|
73
|
+
const filteredRecentWorkspaces = recentWorkspaces.filter((workspace) =>
|
|
74
|
+
matchesSearch([workspace.path, toPathBasename(workspace.path)], normalizedSearch)
|
|
75
|
+
);
|
|
76
|
+
const filteredEntries = entries.filter((entry) =>
|
|
77
|
+
matchesSearch([entry.name, entry.path], normalizedSearch)
|
|
78
|
+
);
|
|
79
|
+
const breadcrumbs = buildPathBreadcrumbs(currentPath ?? bridgeRoot);
|
|
80
|
+
const footerPath = currentPath ?? bridgeRoot ?? 'Bridge default workspace';
|
|
81
|
+
|
|
65
82
|
return (
|
|
66
83
|
<Modal
|
|
67
84
|
visible={visible}
|
|
@@ -74,165 +91,240 @@ export function WorkspacePickerModal({
|
|
|
74
91
|
<Pressable style={StyleSheet.absoluteFill} onPress={onClose} />
|
|
75
92
|
<View style={[styles.outer, { paddingTop: topInset, paddingBottom: bottomInset }]}>
|
|
76
93
|
<View style={[styles.card, { height: cardHeight }]}>
|
|
77
|
-
<View style={styles.handle} />
|
|
78
94
|
<View style={styles.header}>
|
|
79
|
-
<View style={styles.
|
|
80
|
-
|
|
81
|
-
<Text style={styles.title}>Start directory</Text>
|
|
82
|
-
<Text style={styles.subtitle}>
|
|
83
|
-
Choose a known Codex workspace or browse any folder on the bridge host.
|
|
84
|
-
</Text>
|
|
85
|
-
</View>
|
|
95
|
+
<View style={styles.headerSpacer} />
|
|
96
|
+
<Text style={styles.title}>Choose Directory</Text>
|
|
86
97
|
<Pressable
|
|
87
98
|
onPress={onClose}
|
|
88
|
-
style={({ pressed }) => [styles.
|
|
99
|
+
style={({ pressed }) => [styles.closeButton, pressed && styles.pressed]}
|
|
89
100
|
>
|
|
90
101
|
<Ionicons name="close" size={18} color={colors.textSecondary} />
|
|
91
102
|
</Pressable>
|
|
92
103
|
</View>
|
|
93
104
|
|
|
94
105
|
<View style={styles.body}>
|
|
95
|
-
<View style={styles.
|
|
96
|
-
<Text style={styles.
|
|
106
|
+
<View style={styles.connectionRow}>
|
|
107
|
+
<Text style={styles.connectionText} numberOfLines={1}>
|
|
108
|
+
{bridgeRoot ? `Bridge root: ${bridgeRoot}` : 'Browse folders on the bridge host'}
|
|
109
|
+
</Text>
|
|
110
|
+
<Pressable
|
|
111
|
+
onPress={() => onSelectPath(null)}
|
|
112
|
+
style={({ pressed }) => [
|
|
113
|
+
styles.defaultButton,
|
|
114
|
+
selectedPath === null && styles.defaultButtonSelected,
|
|
115
|
+
pressed && styles.pressed,
|
|
116
|
+
]}
|
|
117
|
+
>
|
|
118
|
+
<Text
|
|
119
|
+
style={[
|
|
120
|
+
styles.defaultButtonText,
|
|
121
|
+
selectedPath === null && styles.defaultButtonTextSelected,
|
|
122
|
+
]}
|
|
123
|
+
>
|
|
124
|
+
{selectedPath === null ? 'Default' : 'Use default'}
|
|
125
|
+
</Text>
|
|
126
|
+
</Pressable>
|
|
127
|
+
</View>
|
|
128
|
+
|
|
129
|
+
<View style={styles.searchField}>
|
|
130
|
+
<Ionicons name="search" size={16} color={colors.textMuted} />
|
|
131
|
+
<TextInput
|
|
132
|
+
value={searchQuery}
|
|
133
|
+
onChangeText={setSearchQuery}
|
|
134
|
+
keyboardAppearance="dark"
|
|
135
|
+
placeholder="Search folders"
|
|
136
|
+
placeholderTextColor={colors.textMuted}
|
|
137
|
+
style={styles.searchInput}
|
|
138
|
+
autoCapitalize="none"
|
|
139
|
+
autoCorrect={false}
|
|
140
|
+
returnKeyType="search"
|
|
141
|
+
/>
|
|
142
|
+
</View>
|
|
143
|
+
|
|
144
|
+
<View style={styles.sectionHeader}>
|
|
145
|
+
<Text style={styles.sectionTitle}>Recent Directories</Text>
|
|
146
|
+
</View>
|
|
147
|
+
|
|
148
|
+
<View style={styles.recentCard}>
|
|
149
|
+
{loadingRecent ? (
|
|
150
|
+
<LoadingRow label="Refreshing recent directories..." compact />
|
|
151
|
+
) : filteredRecentWorkspaces.length > 0 ? (
|
|
152
|
+
<ScrollView
|
|
153
|
+
showsVerticalScrollIndicator={false}
|
|
154
|
+
keyboardShouldPersistTaps="handled"
|
|
155
|
+
>
|
|
156
|
+
{filteredRecentWorkspaces.map((workspace, index) => (
|
|
157
|
+
<Pressable
|
|
158
|
+
key={workspace.path}
|
|
159
|
+
onPress={() => onBrowsePath(workspace.path)}
|
|
160
|
+
style={({ pressed }) => [
|
|
161
|
+
styles.recentRow,
|
|
162
|
+
workspace.path === currentPath && styles.recentRowSelected,
|
|
163
|
+
index === filteredRecentWorkspaces.length - 1 &&
|
|
164
|
+
styles.recentRowLast,
|
|
165
|
+
pressed && styles.pressed,
|
|
166
|
+
]}
|
|
167
|
+
>
|
|
168
|
+
<View style={styles.recentIconWrap}>
|
|
169
|
+
<Ionicons name="time-outline" size={16} color={colors.textSecondary} />
|
|
170
|
+
</View>
|
|
171
|
+
<View style={styles.recentCopy}>
|
|
172
|
+
<Text style={styles.recentTitle} numberOfLines={1}>
|
|
173
|
+
{toPathBasename(workspace.path)}
|
|
174
|
+
</Text>
|
|
175
|
+
<Text style={styles.recentPath} numberOfLines={1}>
|
|
176
|
+
{workspace.path}
|
|
177
|
+
</Text>
|
|
178
|
+
</View>
|
|
179
|
+
<Text style={styles.recentMeta}>
|
|
180
|
+
{formatWorkspaceMeta(workspace)}
|
|
181
|
+
</Text>
|
|
182
|
+
</Pressable>
|
|
183
|
+
))}
|
|
184
|
+
</ScrollView>
|
|
185
|
+
) : (
|
|
186
|
+
<EmptyRow
|
|
187
|
+
label={
|
|
188
|
+
normalizedSearch
|
|
189
|
+
? 'No recent directories match this search.'
|
|
190
|
+
: 'No recent directories yet.'
|
|
191
|
+
}
|
|
192
|
+
compact
|
|
193
|
+
/>
|
|
194
|
+
)}
|
|
195
|
+
</View>
|
|
196
|
+
|
|
197
|
+
<Text style={styles.helperText}>Open a recent folder or browse below.</Text>
|
|
198
|
+
|
|
199
|
+
<View style={styles.breadcrumbRow}>
|
|
200
|
+
<Pressable
|
|
201
|
+
onPress={() => parentPath && onBrowsePath(parentPath)}
|
|
202
|
+
disabled={!parentPath || loadingEntries}
|
|
203
|
+
style={({ pressed }) => [
|
|
204
|
+
styles.upButton,
|
|
205
|
+
(!parentPath || loadingEntries) && styles.buttonDisabled,
|
|
206
|
+
pressed && parentPath && !loadingEntries && styles.pressed,
|
|
207
|
+
]}
|
|
208
|
+
>
|
|
209
|
+
<Ionicons name="return-up-back" size={14} color={colors.textSecondary} />
|
|
210
|
+
<Text style={styles.upButtonText}>Up one level</Text>
|
|
211
|
+
</Pressable>
|
|
212
|
+
|
|
97
213
|
<ScrollView
|
|
98
214
|
horizontal
|
|
99
215
|
showsHorizontalScrollIndicator={false}
|
|
100
|
-
contentContainerStyle={styles.
|
|
216
|
+
contentContainerStyle={styles.breadcrumbScroll}
|
|
101
217
|
>
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
218
|
+
{breadcrumbs.length > 0
|
|
219
|
+
? breadcrumbs.map((item, index) => {
|
|
220
|
+
const isLast = index === breadcrumbs.length - 1;
|
|
221
|
+
return (
|
|
222
|
+
<View key={item.key} style={styles.breadcrumbItem}>
|
|
223
|
+
{index > 0 ? <Text style={styles.breadcrumbSlash}>/</Text> : null}
|
|
224
|
+
<Pressable
|
|
225
|
+
onPress={() => onBrowsePath(item.path)}
|
|
226
|
+
style={({ pressed }) => [
|
|
227
|
+
styles.breadcrumbChip,
|
|
228
|
+
isLast && styles.breadcrumbChipActive,
|
|
229
|
+
pressed && styles.pressed,
|
|
230
|
+
]}
|
|
231
|
+
>
|
|
232
|
+
<Text
|
|
233
|
+
style={[
|
|
234
|
+
styles.breadcrumbText,
|
|
235
|
+
isLast && styles.breadcrumbTextActive,
|
|
236
|
+
]}
|
|
237
|
+
>
|
|
238
|
+
{item.label}
|
|
239
|
+
</Text>
|
|
240
|
+
</Pressable>
|
|
241
|
+
</View>
|
|
242
|
+
);
|
|
243
|
+
})
|
|
244
|
+
: (
|
|
245
|
+
<Text style={styles.breadcrumbEmpty}>Loading path...</Text>
|
|
246
|
+
)}
|
|
119
247
|
</ScrollView>
|
|
120
|
-
{loadingRecent ? (
|
|
121
|
-
<Text style={styles.inlineStatusText}>Refreshing known workspaces…</Text>
|
|
122
|
-
) : null}
|
|
123
248
|
</View>
|
|
124
249
|
|
|
125
|
-
<
|
|
126
|
-
<View style={styles.browserHeader}>
|
|
127
|
-
<View style={styles.browserHeaderCopy}>
|
|
128
|
-
<Text style={styles.sectionTitle}>Browse folders</Text>
|
|
129
|
-
<Text style={styles.pathValue} numberOfLines={2}>
|
|
130
|
-
{currentPath ?? bridgeRoot ?? 'Loading…'}
|
|
131
|
-
</Text>
|
|
132
|
-
</View>
|
|
133
|
-
<Pressable
|
|
134
|
-
onPress={() => parentPath && onBrowsePath(parentPath)}
|
|
135
|
-
disabled={!parentPath || loadingEntries}
|
|
136
|
-
style={({ pressed }) => [
|
|
137
|
-
styles.iconButton,
|
|
138
|
-
(!parentPath || loadingEntries) && styles.buttonDisabled,
|
|
139
|
-
pressed && parentPath && !loadingEntries && styles.pressed,
|
|
140
|
-
]}
|
|
141
|
-
>
|
|
142
|
-
<Ionicons name="arrow-up-outline" size={18} color={colors.textPrimary} />
|
|
143
|
-
</Pressable>
|
|
144
|
-
</View>
|
|
250
|
+
{error ? <Text style={styles.errorText}>{error}</Text> : null}
|
|
145
251
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
252
|
+
<View style={styles.browserCard}>
|
|
253
|
+
{loadingEntries ? (
|
|
254
|
+
<LoadingRow label="Loading folders..." />
|
|
255
|
+
) : filteredEntries.length > 0 ? (
|
|
256
|
+
<ScrollView
|
|
257
|
+
style={styles.entryListScroll}
|
|
258
|
+
contentContainerStyle={styles.entryListContent}
|
|
259
|
+
showsVerticalScrollIndicator={false}
|
|
260
|
+
keyboardShouldPersistTaps="handled"
|
|
261
|
+
>
|
|
262
|
+
{filteredEntries.map((entry, index) => (
|
|
263
|
+
<Pressable
|
|
264
|
+
key={entry.path}
|
|
265
|
+
onPress={() => onBrowsePath(entry.path)}
|
|
266
|
+
style={({ pressed }) => [
|
|
267
|
+
styles.entryRow,
|
|
268
|
+
entry.path === selectedPath && styles.entryRowSelected,
|
|
269
|
+
index === filteredEntries.length - 1 && styles.entryRowLast,
|
|
270
|
+
pressed && styles.pressed,
|
|
271
|
+
]}
|
|
272
|
+
>
|
|
273
|
+
<View style={styles.entryIconWrap}>
|
|
274
|
+
<Ionicons
|
|
275
|
+
name={entry.isGitRepo ? 'git-branch-outline' : 'folder-outline'}
|
|
276
|
+
size={18}
|
|
277
|
+
color={colors.textSecondary}
|
|
278
|
+
/>
|
|
279
|
+
</View>
|
|
280
|
+
<View style={styles.entryCopy}>
|
|
281
|
+
<Text style={styles.entryName} numberOfLines={1}>
|
|
282
|
+
{entry.name}
|
|
283
|
+
</Text>
|
|
284
|
+
</View>
|
|
285
|
+
<Ionicons name="chevron-forward" size={16} color={colors.textMuted} />
|
|
286
|
+
</Pressable>
|
|
287
|
+
))}
|
|
288
|
+
</ScrollView>
|
|
289
|
+
) : (
|
|
290
|
+
<EmptyRow
|
|
291
|
+
label={
|
|
292
|
+
normalizedSearch
|
|
293
|
+
? 'No folders match this search.'
|
|
294
|
+
: 'No folders found here.'
|
|
295
|
+
}
|
|
158
296
|
/>
|
|
297
|
+
)}
|
|
298
|
+
</View>
|
|
299
|
+
|
|
300
|
+
<View style={styles.footer}>
|
|
301
|
+
<Text style={styles.footerPath} numberOfLines={1}>
|
|
302
|
+
{footerPath}
|
|
303
|
+
</Text>
|
|
304
|
+
<View style={styles.footerActions}>
|
|
159
305
|
<Pressable
|
|
160
|
-
onPress={
|
|
161
|
-
disabled={loadingEntries}
|
|
306
|
+
onPress={onClose}
|
|
162
307
|
style={({ pressed }) => [
|
|
163
|
-
styles.
|
|
164
|
-
|
|
165
|
-
pressed &&
|
|
308
|
+
styles.footerButton,
|
|
309
|
+
styles.footerButtonSecondary,
|
|
310
|
+
pressed && styles.pressed,
|
|
166
311
|
]}
|
|
167
312
|
>
|
|
168
|
-
<Text style={styles.
|
|
313
|
+
<Text style={styles.footerButtonSecondaryText}>Cancel</Text>
|
|
169
314
|
</Pressable>
|
|
170
|
-
</View>
|
|
171
|
-
|
|
172
|
-
<View style={styles.browserActions}>
|
|
173
315
|
<Pressable
|
|
174
316
|
onPress={() => currentPath && onSelectPath(currentPath)}
|
|
175
317
|
disabled={!currentPath || loadingEntries}
|
|
176
318
|
style={({ pressed }) => [
|
|
177
|
-
styles.
|
|
319
|
+
styles.footerButton,
|
|
320
|
+
styles.footerButtonPrimary,
|
|
178
321
|
(!currentPath || loadingEntries) && styles.buttonDisabled,
|
|
179
|
-
pressed && currentPath && !loadingEntries && styles.
|
|
322
|
+
pressed && currentPath && !loadingEntries && styles.footerButtonPrimaryPressed,
|
|
180
323
|
]}
|
|
181
324
|
>
|
|
182
|
-
<
|
|
183
|
-
<Text style={styles.primaryButtonText}>Use this folder</Text>
|
|
325
|
+
<Text style={styles.footerButtonPrimaryText}>Select Folder</Text>
|
|
184
326
|
</Pressable>
|
|
185
327
|
</View>
|
|
186
|
-
|
|
187
|
-
{error ? <Text style={styles.errorText}>{error}</Text> : null}
|
|
188
|
-
|
|
189
|
-
<View style={styles.entryListCard}>
|
|
190
|
-
{loadingEntries ? (
|
|
191
|
-
<LoadingRow label="Loading folders…" />
|
|
192
|
-
) : entries.length > 0 ? (
|
|
193
|
-
<ScrollView
|
|
194
|
-
style={styles.entryListScroll}
|
|
195
|
-
contentContainerStyle={styles.entryListContent}
|
|
196
|
-
showsVerticalScrollIndicator={false}
|
|
197
|
-
keyboardShouldPersistTaps="handled"
|
|
198
|
-
>
|
|
199
|
-
{entries.map((entry) => (
|
|
200
|
-
<Pressable
|
|
201
|
-
key={entry.path}
|
|
202
|
-
onPress={() => onBrowsePath(entry.path)}
|
|
203
|
-
style={({ pressed }) => [
|
|
204
|
-
styles.entryRow,
|
|
205
|
-
entry.path === selectedPath && styles.entryRowSelected,
|
|
206
|
-
pressed && styles.pressed,
|
|
207
|
-
]}
|
|
208
|
-
>
|
|
209
|
-
<View style={styles.entryIconWrap}>
|
|
210
|
-
<Ionicons
|
|
211
|
-
name={entry.isGitRepo ? 'git-branch-outline' : 'folder-outline'}
|
|
212
|
-
size={18}
|
|
213
|
-
color={colors.textPrimary}
|
|
214
|
-
/>
|
|
215
|
-
</View>
|
|
216
|
-
<View style={styles.entryCopy}>
|
|
217
|
-
<Text style={styles.entryName} numberOfLines={1}>
|
|
218
|
-
{entry.name}
|
|
219
|
-
</Text>
|
|
220
|
-
<Text style={styles.entryPath} numberOfLines={1}>
|
|
221
|
-
{entry.path}
|
|
222
|
-
</Text>
|
|
223
|
-
</View>
|
|
224
|
-
<Ionicons
|
|
225
|
-
name="chevron-forward"
|
|
226
|
-
size={16}
|
|
227
|
-
color={colors.textMuted}
|
|
228
|
-
/>
|
|
229
|
-
</Pressable>
|
|
230
|
-
))}
|
|
231
|
-
</ScrollView>
|
|
232
|
-
) : (
|
|
233
|
-
<EmptyRow label="No folders found here." />
|
|
234
|
-
)}
|
|
235
|
-
</View>
|
|
236
328
|
</View>
|
|
237
329
|
</View>
|
|
238
330
|
</View>
|
|
@@ -242,53 +334,30 @@ export function WorkspacePickerModal({
|
|
|
242
334
|
);
|
|
243
335
|
}
|
|
244
336
|
|
|
245
|
-
function
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
selected,
|
|
249
|
-
onPress,
|
|
337
|
+
function LoadingRow({
|
|
338
|
+
label,
|
|
339
|
+
compact = false,
|
|
250
340
|
}: {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
selected: boolean;
|
|
254
|
-
onPress: () => void;
|
|
341
|
+
label: string;
|
|
342
|
+
compact?: boolean;
|
|
255
343
|
}) {
|
|
256
344
|
return (
|
|
257
|
-
<
|
|
258
|
-
onPress={onPress}
|
|
259
|
-
style={({ pressed }) => [
|
|
260
|
-
styles.quickPickChip,
|
|
261
|
-
selected && styles.quickPickChipSelected,
|
|
262
|
-
pressed && styles.pressed,
|
|
263
|
-
]}
|
|
264
|
-
>
|
|
265
|
-
<Text style={styles.quickPickTitle} numberOfLines={1}>
|
|
266
|
-
{title}
|
|
267
|
-
</Text>
|
|
268
|
-
{meta ? (
|
|
269
|
-
<Text style={styles.quickPickMeta} numberOfLines={1}>
|
|
270
|
-
{meta}
|
|
271
|
-
</Text>
|
|
272
|
-
) : null}
|
|
273
|
-
{selected ? (
|
|
274
|
-
<Ionicons name="checkmark-circle" size={16} color={colors.textPrimary} />
|
|
275
|
-
) : null}
|
|
276
|
-
</Pressable>
|
|
277
|
-
);
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
function LoadingRow({ label }: { label: string }) {
|
|
281
|
-
return (
|
|
282
|
-
<View style={styles.statusRow}>
|
|
345
|
+
<View style={[styles.statusRow, compact && styles.statusRowCompact]}>
|
|
283
346
|
<ActivityIndicator color={colors.textPrimary} />
|
|
284
347
|
<Text style={styles.statusText}>{label}</Text>
|
|
285
348
|
</View>
|
|
286
349
|
);
|
|
287
350
|
}
|
|
288
351
|
|
|
289
|
-
function EmptyRow({
|
|
352
|
+
function EmptyRow({
|
|
353
|
+
label,
|
|
354
|
+
compact = false,
|
|
355
|
+
}: {
|
|
356
|
+
label: string;
|
|
357
|
+
compact?: boolean;
|
|
358
|
+
}) {
|
|
290
359
|
return (
|
|
291
|
-
<View style={styles.statusRow}>
|
|
360
|
+
<View style={[styles.statusRow, compact && styles.statusRowCompact]}>
|
|
292
361
|
<Text style={styles.statusText}>{label}</Text>
|
|
293
362
|
</View>
|
|
294
363
|
);
|
|
@@ -302,10 +371,98 @@ function toPathBasename(path: string): string {
|
|
|
302
371
|
return parts[parts.length - 1] ?? path;
|
|
303
372
|
}
|
|
304
373
|
|
|
374
|
+
function matchesSearch(values: string[], query: string): boolean {
|
|
375
|
+
if (!query) {
|
|
376
|
+
return true;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return values.some((value) => value.toLowerCase().includes(query));
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function buildPathBreadcrumbs(path: string | null): BreadcrumbItem[] {
|
|
383
|
+
if (!path) {
|
|
384
|
+
return [];
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const normalized = path.replace(/\\/g, '/');
|
|
388
|
+
const driveMatch = normalized.match(/^[A-Za-z]:/);
|
|
389
|
+
const isAbsolute = normalized.startsWith('/');
|
|
390
|
+
let remainder = normalized;
|
|
391
|
+
const items: BreadcrumbItem[] = [];
|
|
392
|
+
|
|
393
|
+
if (driveMatch) {
|
|
394
|
+
const root = driveMatch[0];
|
|
395
|
+
items.push({ key: root, label: root, path: root });
|
|
396
|
+
remainder = normalized.slice(root.length).replace(/^\/+/, '');
|
|
397
|
+
} else if (isAbsolute) {
|
|
398
|
+
items.push({ key: '/', label: '/', path: '/' });
|
|
399
|
+
remainder = normalized.slice(1);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const parts = remainder.split('/').filter(Boolean);
|
|
403
|
+
let accumulated = driveMatch ? driveMatch[0] : isAbsolute ? '' : '';
|
|
404
|
+
|
|
405
|
+
for (const part of parts) {
|
|
406
|
+
accumulated = driveMatch
|
|
407
|
+
? `${accumulated}/${part}`
|
|
408
|
+
: isAbsolute
|
|
409
|
+
? `${accumulated}/${part}`
|
|
410
|
+
: accumulated
|
|
411
|
+
? `${accumulated}/${part}`
|
|
412
|
+
: part;
|
|
413
|
+
items.push({
|
|
414
|
+
key: accumulated,
|
|
415
|
+
label: part,
|
|
416
|
+
path: accumulated,
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
return items;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function formatWorkspaceMeta(workspace: WorkspaceSummary): string {
|
|
424
|
+
const relative = formatRelativeTime(workspace.updatedAt);
|
|
425
|
+
if (relative) {
|
|
426
|
+
return relative;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
if (workspace.chatCount === 1) {
|
|
430
|
+
return '1 chat';
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
return `${String(workspace.chatCount)} chats`;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
function formatRelativeTime(iso?: string): string | null {
|
|
437
|
+
if (!iso) {
|
|
438
|
+
return null;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
const timestamp = Date.parse(iso);
|
|
442
|
+
if (!Number.isFinite(timestamp)) {
|
|
443
|
+
return null;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
const diffMs = Math.max(0, Date.now() - timestamp);
|
|
447
|
+
const seconds = Math.floor(diffMs / 1000);
|
|
448
|
+
const minutes = Math.floor(diffMs / 60000);
|
|
449
|
+
const hours = Math.floor(diffMs / 3600000);
|
|
450
|
+
const days = Math.floor(diffMs / 86400000);
|
|
451
|
+
const weeks = Math.floor(days / 7);
|
|
452
|
+
|
|
453
|
+
if (seconds < 10) return 'now';
|
|
454
|
+
if (seconds < 60) return `${String(seconds)} sec ago`;
|
|
455
|
+
if (minutes < 60) return `${String(minutes)} min ago`;
|
|
456
|
+
if (hours < 24) return `${String(hours)} hr ago`;
|
|
457
|
+
if (days < 7) return `${String(days)} ${days === 1 ? 'day' : 'days'} ago`;
|
|
458
|
+
if (weeks < 5) return `${String(weeks)} wk ago`;
|
|
459
|
+
return `${String(Math.floor(days / 30))} mo ago`;
|
|
460
|
+
}
|
|
461
|
+
|
|
305
462
|
const styles = StyleSheet.create({
|
|
306
463
|
backdrop: {
|
|
307
464
|
flex: 1,
|
|
308
|
-
backgroundColor: 'rgba(0, 0, 0, 0.
|
|
465
|
+
backgroundColor: 'rgba(0, 0, 0, 0.62)',
|
|
309
466
|
},
|
|
310
467
|
outer: {
|
|
311
468
|
flex: 1,
|
|
@@ -313,195 +470,235 @@ const styles = StyleSheet.create({
|
|
|
313
470
|
paddingHorizontal: spacing.lg,
|
|
314
471
|
},
|
|
315
472
|
card: {
|
|
316
|
-
borderRadius:
|
|
473
|
+
borderRadius: 28,
|
|
317
474
|
borderCurve: 'continuous',
|
|
318
|
-
backgroundColor: '#
|
|
475
|
+
backgroundColor: '#07090C',
|
|
319
476
|
borderWidth: 1,
|
|
320
|
-
borderColor:
|
|
477
|
+
borderColor: 'rgba(255, 255, 255, 0.09)',
|
|
321
478
|
overflow: 'hidden',
|
|
322
479
|
boxShadow: '0 24px 44px rgba(0, 0, 0, 0.34)',
|
|
323
480
|
},
|
|
324
|
-
handle: {
|
|
325
|
-
alignSelf: 'center',
|
|
326
|
-
width: 48,
|
|
327
|
-
height: 5,
|
|
328
|
-
borderRadius: radius.full,
|
|
329
|
-
backgroundColor: 'rgba(255,255,255,0.16)',
|
|
330
|
-
marginTop: spacing.sm,
|
|
331
|
-
},
|
|
332
481
|
header: {
|
|
482
|
+
minHeight: 68,
|
|
333
483
|
flexDirection: 'row',
|
|
334
|
-
alignItems: '
|
|
484
|
+
alignItems: 'center',
|
|
335
485
|
justifyContent: 'space-between',
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
paddingTop: spacing.lg,
|
|
339
|
-
paddingBottom: spacing.md,
|
|
340
|
-
borderBottomWidth: 1,
|
|
341
|
-
borderBottomColor: colors.borderLight,
|
|
486
|
+
paddingHorizontal: spacing.lg,
|
|
487
|
+
paddingTop: spacing.md,
|
|
342
488
|
},
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
gap: spacing.xs,
|
|
346
|
-
},
|
|
347
|
-
eyebrow: {
|
|
348
|
-
...typography.caption,
|
|
349
|
-
color: colors.textSecondary,
|
|
350
|
-
textTransform: 'uppercase',
|
|
351
|
-
letterSpacing: 1.2,
|
|
489
|
+
headerSpacer: {
|
|
490
|
+
width: 36,
|
|
352
491
|
},
|
|
353
492
|
title: {
|
|
354
|
-
...typography.
|
|
355
|
-
fontSize:
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
...typography.body,
|
|
359
|
-
color: colors.textMuted,
|
|
493
|
+
...typography.headline,
|
|
494
|
+
fontSize: 18,
|
|
495
|
+
fontWeight: '700',
|
|
496
|
+
textAlign: 'center',
|
|
360
497
|
},
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
height: 34,
|
|
498
|
+
closeButton: {
|
|
499
|
+
width: 36,
|
|
500
|
+
height: 36,
|
|
365
501
|
borderRadius: radius.full,
|
|
366
|
-
borderWidth: 1,
|
|
367
|
-
borderColor: colors.borderLight,
|
|
368
502
|
alignItems: 'center',
|
|
369
503
|
justifyContent: 'center',
|
|
370
504
|
backgroundColor: 'rgba(255,255,255,0.04)',
|
|
505
|
+
borderWidth: 1,
|
|
506
|
+
borderColor: colors.borderLight,
|
|
371
507
|
},
|
|
372
508
|
body: {
|
|
373
509
|
flex: 1,
|
|
374
|
-
paddingHorizontal: spacing.
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
gap: spacing.lg,
|
|
510
|
+
paddingHorizontal: spacing.lg,
|
|
511
|
+
paddingBottom: spacing.lg,
|
|
512
|
+
gap: spacing.md,
|
|
378
513
|
},
|
|
379
|
-
|
|
380
|
-
|
|
514
|
+
connectionRow: {
|
|
515
|
+
flexDirection: 'row',
|
|
516
|
+
alignItems: 'center',
|
|
517
|
+
gap: spacing.md,
|
|
381
518
|
},
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
519
|
+
connectionText: {
|
|
520
|
+
flex: 1,
|
|
521
|
+
...typography.caption,
|
|
522
|
+
color: colors.textSecondary,
|
|
385
523
|
},
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
borderRadius: radius.lg,
|
|
524
|
+
defaultButton: {
|
|
525
|
+
minHeight: 32,
|
|
526
|
+
paddingHorizontal: spacing.md,
|
|
527
|
+
borderRadius: radius.full,
|
|
391
528
|
borderWidth: 1,
|
|
392
|
-
borderColor: colors.
|
|
529
|
+
borderColor: colors.border,
|
|
393
530
|
backgroundColor: colors.bgItem,
|
|
394
|
-
|
|
395
|
-
paddingVertical: spacing.sm,
|
|
531
|
+
alignItems: 'center',
|
|
396
532
|
justifyContent: 'center',
|
|
397
|
-
gap: 2,
|
|
398
533
|
},
|
|
399
|
-
|
|
534
|
+
defaultButtonSelected: {
|
|
400
535
|
borderColor: colors.borderHighlight,
|
|
401
|
-
backgroundColor:
|
|
536
|
+
backgroundColor: colors.bgInput,
|
|
402
537
|
},
|
|
403
|
-
|
|
404
|
-
...typography.
|
|
538
|
+
defaultButtonText: {
|
|
539
|
+
...typography.caption,
|
|
540
|
+
color: colors.textSecondary,
|
|
405
541
|
fontWeight: '600',
|
|
406
542
|
},
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
color: colors.textMuted,
|
|
543
|
+
defaultButtonTextSelected: {
|
|
544
|
+
color: colors.textPrimary,
|
|
410
545
|
},
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
546
|
+
searchField: {
|
|
547
|
+
minHeight: 44,
|
|
548
|
+
borderRadius: radius.lg,
|
|
549
|
+
borderWidth: 1,
|
|
550
|
+
borderColor: colors.borderLight,
|
|
551
|
+
backgroundColor: colors.bgInput,
|
|
552
|
+
paddingHorizontal: spacing.md,
|
|
553
|
+
flexDirection: 'row',
|
|
554
|
+
alignItems: 'center',
|
|
555
|
+
gap: spacing.sm,
|
|
417
556
|
},
|
|
418
|
-
|
|
557
|
+
searchInput: {
|
|
419
558
|
flex: 1,
|
|
420
|
-
|
|
559
|
+
...typography.body,
|
|
560
|
+
paddingVertical: 0,
|
|
421
561
|
},
|
|
422
|
-
|
|
562
|
+
breadcrumbRow: {
|
|
423
563
|
flexDirection: 'row',
|
|
564
|
+
alignItems: 'center',
|
|
424
565
|
gap: spacing.sm,
|
|
566
|
+
},
|
|
567
|
+
upButton: {
|
|
568
|
+
minHeight: 32,
|
|
569
|
+
paddingHorizontal: spacing.sm,
|
|
570
|
+
borderRadius: radius.full,
|
|
571
|
+
borderWidth: 1,
|
|
572
|
+
borderColor: colors.borderLight,
|
|
573
|
+
flexDirection: 'row',
|
|
425
574
|
alignItems: 'center',
|
|
575
|
+
gap: 6,
|
|
576
|
+
backgroundColor: colors.bgItem,
|
|
426
577
|
},
|
|
427
|
-
|
|
428
|
-
|
|
578
|
+
upButtonText: {
|
|
579
|
+
...typography.caption,
|
|
580
|
+
color: colors.textSecondary,
|
|
581
|
+
fontWeight: '600',
|
|
582
|
+
},
|
|
583
|
+
breadcrumbScroll: {
|
|
584
|
+
alignItems: 'center',
|
|
585
|
+
paddingRight: spacing.md,
|
|
429
586
|
gap: spacing.xs,
|
|
430
587
|
},
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
height: 42,
|
|
434
|
-
borderRadius: radius.lg,
|
|
435
|
-
borderWidth: 1,
|
|
436
|
-
borderColor: colors.border,
|
|
437
|
-
backgroundColor: colors.bgItem,
|
|
588
|
+
breadcrumbItem: {
|
|
589
|
+
flexDirection: 'row',
|
|
438
590
|
alignItems: 'center',
|
|
591
|
+
gap: spacing.xs,
|
|
592
|
+
},
|
|
593
|
+
breadcrumbSlash: {
|
|
594
|
+
...typography.mono,
|
|
595
|
+
color: colors.textMuted,
|
|
596
|
+
},
|
|
597
|
+
breadcrumbChip: {
|
|
598
|
+
minHeight: 30,
|
|
599
|
+
paddingHorizontal: spacing.sm,
|
|
600
|
+
borderRadius: radius.md,
|
|
439
601
|
justifyContent: 'center',
|
|
440
602
|
},
|
|
441
|
-
|
|
603
|
+
breadcrumbChipActive: {
|
|
604
|
+
backgroundColor: colors.bgInput,
|
|
605
|
+
borderWidth: 1,
|
|
606
|
+
borderColor: colors.borderHighlight,
|
|
607
|
+
},
|
|
608
|
+
breadcrumbText: {
|
|
442
609
|
...typography.mono,
|
|
443
|
-
fontSize:
|
|
610
|
+
fontSize: 12,
|
|
611
|
+
color: colors.textSecondary,
|
|
444
612
|
},
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
613
|
+
breadcrumbTextActive: {
|
|
614
|
+
color: colors.textPrimary,
|
|
615
|
+
fontWeight: '700',
|
|
448
616
|
},
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
alignItems: 'center',
|
|
617
|
+
breadcrumbEmpty: {
|
|
618
|
+
...typography.caption,
|
|
619
|
+
color: colors.textMuted,
|
|
453
620
|
},
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
621
|
+
sectionHeader: {
|
|
622
|
+
paddingTop: spacing.xs,
|
|
623
|
+
},
|
|
624
|
+
sectionTitle: {
|
|
625
|
+
...typography.caption,
|
|
626
|
+
fontSize: 11,
|
|
627
|
+
color: colors.textSecondary,
|
|
628
|
+
fontWeight: '600',
|
|
629
|
+
textTransform: 'uppercase',
|
|
630
|
+
letterSpacing: 0.8,
|
|
631
|
+
},
|
|
632
|
+
recentCard: {
|
|
633
|
+
maxHeight: 184,
|
|
457
634
|
borderRadius: radius.lg,
|
|
458
635
|
borderWidth: 1,
|
|
459
636
|
borderColor: colors.borderLight,
|
|
460
|
-
backgroundColor: colors.
|
|
637
|
+
backgroundColor: colors.bgItem,
|
|
638
|
+
overflow: 'hidden',
|
|
639
|
+
},
|
|
640
|
+
recentRow: {
|
|
641
|
+
minHeight: 54,
|
|
461
642
|
paddingHorizontal: spacing.md,
|
|
462
|
-
|
|
643
|
+
paddingVertical: spacing.sm,
|
|
644
|
+
flexDirection: 'row',
|
|
645
|
+
alignItems: 'center',
|
|
646
|
+
gap: spacing.sm,
|
|
647
|
+
borderBottomWidth: StyleSheet.hairlineWidth,
|
|
648
|
+
borderBottomColor: colors.borderLight,
|
|
463
649
|
},
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
650
|
+
recentRowSelected: {
|
|
651
|
+
backgroundColor: colors.bgInput,
|
|
652
|
+
},
|
|
653
|
+
recentRowLast: {
|
|
654
|
+
borderBottomWidth: 0,
|
|
655
|
+
},
|
|
656
|
+
recentIconWrap: {
|
|
657
|
+
width: 30,
|
|
658
|
+
height: 30,
|
|
659
|
+
borderRadius: radius.full,
|
|
468
660
|
alignItems: 'center',
|
|
469
661
|
justifyContent: 'center',
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
flex: 1,
|
|
662
|
+
backgroundColor: colors.bgInput,
|
|
663
|
+
borderWidth: 1,
|
|
664
|
+
borderColor: colors.borderLight,
|
|
474
665
|
},
|
|
475
|
-
|
|
476
|
-
|
|
666
|
+
recentCopy: {
|
|
667
|
+
flex: 1,
|
|
668
|
+
gap: 2,
|
|
477
669
|
},
|
|
478
|
-
|
|
670
|
+
recentTitle: {
|
|
479
671
|
...typography.body,
|
|
480
|
-
|
|
672
|
+
fontSize: 13,
|
|
673
|
+
lineHeight: 18,
|
|
481
674
|
fontWeight: '600',
|
|
482
675
|
},
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
backgroundColor: colors.bgItem,
|
|
489
|
-
alignItems: 'center',
|
|
490
|
-
justifyContent: 'center',
|
|
491
|
-
flexDirection: 'row',
|
|
492
|
-
gap: spacing.xs,
|
|
493
|
-
paddingHorizontal: spacing.md,
|
|
676
|
+
recentPath: {
|
|
677
|
+
...typography.caption,
|
|
678
|
+
fontSize: 11,
|
|
679
|
+
lineHeight: 15,
|
|
680
|
+
color: colors.textMuted,
|
|
494
681
|
},
|
|
495
|
-
|
|
496
|
-
...typography.
|
|
682
|
+
recentMeta: {
|
|
683
|
+
...typography.caption,
|
|
684
|
+
fontSize: 11,
|
|
685
|
+
lineHeight: 15,
|
|
686
|
+
color: colors.textSecondary,
|
|
497
687
|
fontWeight: '600',
|
|
498
688
|
},
|
|
499
|
-
|
|
500
|
-
|
|
689
|
+
helperText: {
|
|
690
|
+
...typography.caption,
|
|
691
|
+
fontSize: 11,
|
|
692
|
+
lineHeight: 15,
|
|
693
|
+
color: colors.textMuted,
|
|
694
|
+
},
|
|
695
|
+
errorText: {
|
|
696
|
+
...typography.caption,
|
|
697
|
+
color: '#FF8A8A',
|
|
501
698
|
},
|
|
502
|
-
|
|
699
|
+
browserCard: {
|
|
503
700
|
flex: 1,
|
|
504
|
-
minHeight:
|
|
701
|
+
minHeight: 228,
|
|
505
702
|
borderRadius: radius.lg,
|
|
506
703
|
borderWidth: 1,
|
|
507
704
|
borderColor: colors.borderLight,
|
|
@@ -512,61 +709,104 @@ const styles = StyleSheet.create({
|
|
|
512
709
|
flex: 1,
|
|
513
710
|
},
|
|
514
711
|
entryListContent: {
|
|
515
|
-
|
|
712
|
+
paddingVertical: spacing.xs,
|
|
516
713
|
},
|
|
517
714
|
entryRow: {
|
|
715
|
+
minHeight: 54,
|
|
716
|
+
paddingHorizontal: spacing.md,
|
|
518
717
|
flexDirection: 'row',
|
|
519
718
|
alignItems: 'center',
|
|
520
719
|
gap: spacing.sm,
|
|
521
|
-
paddingHorizontal: spacing.md,
|
|
522
|
-
paddingVertical: spacing.md,
|
|
523
|
-
backgroundColor: colors.bgItem,
|
|
524
720
|
borderBottomWidth: StyleSheet.hairlineWidth,
|
|
525
721
|
borderBottomColor: colors.borderLight,
|
|
526
722
|
},
|
|
527
723
|
entryRowSelected: {
|
|
528
|
-
backgroundColor:
|
|
724
|
+
backgroundColor: colors.bgInput,
|
|
725
|
+
},
|
|
726
|
+
entryRowLast: {
|
|
727
|
+
borderBottomWidth: 0,
|
|
529
728
|
},
|
|
530
729
|
entryIconWrap: {
|
|
531
|
-
width:
|
|
532
|
-
height:
|
|
730
|
+
width: 32,
|
|
731
|
+
height: 32,
|
|
533
732
|
borderRadius: radius.md,
|
|
534
733
|
alignItems: 'center',
|
|
535
734
|
justifyContent: 'center',
|
|
536
|
-
backgroundColor:
|
|
735
|
+
backgroundColor: colors.bgInput,
|
|
537
736
|
borderWidth: 1,
|
|
538
737
|
borderColor: colors.borderLight,
|
|
539
738
|
},
|
|
540
739
|
entryCopy: {
|
|
541
740
|
flex: 1,
|
|
542
|
-
gap: 2,
|
|
543
741
|
},
|
|
544
742
|
entryName: {
|
|
545
743
|
...typography.body,
|
|
744
|
+
fontSize: 13,
|
|
745
|
+
lineHeight: 18,
|
|
546
746
|
fontWeight: '600',
|
|
547
747
|
},
|
|
548
|
-
|
|
549
|
-
|
|
748
|
+
footer: {
|
|
749
|
+
gap: spacing.sm,
|
|
750
|
+
},
|
|
751
|
+
footerPath: {
|
|
752
|
+
...typography.mono,
|
|
753
|
+
fontSize: 10,
|
|
754
|
+
lineHeight: 14,
|
|
550
755
|
color: colors.textMuted,
|
|
551
756
|
},
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
757
|
+
footerActions: {
|
|
758
|
+
flexDirection: 'row',
|
|
759
|
+
gap: spacing.sm,
|
|
760
|
+
},
|
|
761
|
+
footerButton: {
|
|
762
|
+
minHeight: 48,
|
|
763
|
+
borderRadius: radius.lg,
|
|
764
|
+
alignItems: 'center',
|
|
765
|
+
justifyContent: 'center',
|
|
766
|
+
paddingHorizontal: spacing.lg,
|
|
767
|
+
flex: 1,
|
|
768
|
+
},
|
|
769
|
+
footerButtonSecondary: {
|
|
770
|
+
backgroundColor: colors.bgMain,
|
|
771
|
+
borderWidth: 1,
|
|
772
|
+
borderColor: colors.border,
|
|
773
|
+
},
|
|
774
|
+
footerButtonPrimary: {
|
|
775
|
+
backgroundColor: colors.accent,
|
|
776
|
+
},
|
|
777
|
+
footerButtonPrimaryPressed: {
|
|
778
|
+
backgroundColor: colors.accentPressed,
|
|
779
|
+
},
|
|
780
|
+
footerButtonSecondaryText: {
|
|
781
|
+
...typography.body,
|
|
782
|
+
color: colors.textSecondary,
|
|
783
|
+
fontWeight: '600',
|
|
784
|
+
},
|
|
785
|
+
footerButtonPrimaryText: {
|
|
786
|
+
...typography.body,
|
|
787
|
+
color: colors.black,
|
|
788
|
+
fontWeight: '700',
|
|
555
789
|
},
|
|
556
790
|
statusRow: {
|
|
557
791
|
flex: 1,
|
|
558
|
-
minHeight:
|
|
792
|
+
minHeight: 132,
|
|
559
793
|
alignItems: 'center',
|
|
560
794
|
justifyContent: 'center',
|
|
561
795
|
gap: spacing.sm,
|
|
562
796
|
paddingHorizontal: spacing.lg,
|
|
563
797
|
},
|
|
798
|
+
statusRowCompact: {
|
|
799
|
+
minHeight: 96,
|
|
800
|
+
},
|
|
564
801
|
statusText: {
|
|
565
802
|
...typography.body,
|
|
566
803
|
textAlign: 'center',
|
|
567
804
|
color: colors.textMuted,
|
|
568
805
|
},
|
|
806
|
+
buttonDisabled: {
|
|
807
|
+
opacity: 0.42,
|
|
808
|
+
},
|
|
569
809
|
pressed: {
|
|
570
|
-
opacity: 0.
|
|
810
|
+
opacity: 0.86,
|
|
571
811
|
},
|
|
572
812
|
});
|