inline styles

This commit is contained in:
EthanShoeDev
2025-09-16 19:23:08 -04:00
parent 4413566fec
commit c9aabccd84
10 changed files with 887 additions and 879 deletions

View File

@@ -36,9 +36,9 @@
"@tanstack/react-form": "^1.20.0",
"@tanstack/react-query": "^5.87.4",
"date-fns": "^4.1.0",
"expo": "54.0.7",
"expo": "54.0.8",
"expo-clipboard": "~8.0.7",
"expo-constants": "~18.0.8",
"expo-constants": "~18.0.9",
"expo-crypto": "~15.0.7",
"expo-dev-client": "~6.0.12",
"expo-document-picker": "~14.0.7",
@@ -48,7 +48,7 @@
"expo-haptics": "~15.0.7",
"expo-image": "~3.0.8",
"expo-linking": "~8.0.8",
"expo-router": "6.0.5",
"expo-router": "6.0.6",
"expo-secure-store": "~15.0.7",
"expo-splash-screen": "~31.0.10",
"expo-status-bar": "~3.0.8",

View File

@@ -28,6 +28,3 @@ export default function KeyManagerModalRoute() {
</SafeAreaView>
);
}
// // styles kept for potential future local additions
// const styles = StyleSheet.create({});

View File

@@ -2,14 +2,7 @@ import SegmentedControl from '@react-native-segmented-control/segmented-control'
import { useStore } from '@tanstack/react-form';
import { useQuery } from '@tanstack/react-query';
import React from 'react';
import {
Modal,
Pressable,
ScrollView,
StyleSheet,
Text,
View,
} from 'react-native';
import { Modal, Pressable, ScrollView, Text, View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { useAppForm, useFieldContext } from '@/components/form-components';
import { KeyList } from '@/components/key-manager/KeyList';
@@ -19,7 +12,7 @@ import {
secretsManager,
type InputConnectionDetails,
} from '@/lib/secrets-manager';
import { useTheme, type AppTheme } from '@/lib/theme';
import { useTheme } from '@/lib/theme';
export default function TabsIndex() {
return <Host />;
@@ -37,7 +30,6 @@ const defaultValues: InputConnectionDetails = {
function Host() {
const theme = useTheme();
const styles = React.useMemo(() => makeStyles(theme), [theme]);
// const insets = useSafeAreaInsets();
const sshConnMutation = useSshConnMutation();
const connectionForm = useAppForm({
@@ -62,23 +54,74 @@ function Host() {
return (
<SafeAreaView style={{ flex: 1, backgroundColor: theme.colors.background }}>
<ScrollView
contentContainerStyle={[styles.scrollContent]}
contentContainerStyle={[{ paddingBottom: 32 }]}
keyboardShouldPersistTaps="handled"
style={{ backgroundColor: theme.colors.background }}
>
<View
style={[
styles.container,
{
flex: 1,
padding: 24,
backgroundColor: theme.colors.background,
justifyContent: 'center',
},
{ backgroundColor: theme.colors.background },
]}
>
<View style={styles.header}>
<Text style={styles.appName}>fressh</Text>
<Text style={styles.appTagline}>A fast, friendly SSH client</Text>
<View style={{ marginBottom: 16, alignItems: 'center' }}>
<Text
style={{
fontSize: 28,
fontWeight: '800',
color: theme.colors.textPrimary,
letterSpacing: 1,
}}
>
fressh
</Text>
<Text
style={{ marginTop: 4, fontSize: 13, color: theme.colors.muted }}
>
A fast, friendly SSH client
</Text>
</View>
<View style={styles.card}>
<Text style={styles.title}>Connect to SSH Server</Text>
<Text style={styles.subtitle}>Enter your server credentials</Text>
<View
style={{
backgroundColor: theme.colors.surface,
borderRadius: 20,
padding: 24,
marginHorizontal: 4,
shadowColor: theme.colors.shadow,
shadowOpacity: 0.3,
shadowRadius: 16,
shadowOffset: { width: 0, height: 4 },
elevation: 8,
borderWidth: 1,
borderColor: theme.colors.borderStrong,
}}
>
<Text
style={{
fontSize: 24,
fontWeight: '700',
color: theme.colors.textPrimary,
marginBottom: 6,
letterSpacing: 0.5,
}}
>
Connect to SSH Server
</Text>
<Text
style={{
fontSize: 15,
color: theme.colors.muted,
marginBottom: 24,
lineHeight: 20,
}}
>
Enter your server credentials
</Text>
<connectionForm.AppForm>
<connectionForm.AppField name="host">
@@ -114,7 +157,7 @@ function Host() {
</connectionForm.AppField>
<connectionForm.AppField name="security.type">
{(field) => (
<View style={styles.inputGroup}>
<View style={{ marginBottom: 12 }}>
<SegmentedControl
values={['Password', 'Private Key']}
selectedIndex={field.state.value === 'password' ? 0 : 1}
@@ -146,7 +189,7 @@ function Host() {
</connectionForm.AppField>
)}
<View style={styles.actions}>
<View style={{ marginTop: 20 }}>
<connectionForm.SubmitButton
title="Connect"
testID="connect"
@@ -188,7 +231,6 @@ function Host() {
function KeyIdPickerField() {
const theme = useTheme();
const styles = React.useMemo(() => makeStyles(theme), [theme]);
const field = useFieldContext<string>();
const [open, setOpen] = React.useState(false);
@@ -216,10 +258,29 @@ function KeyIdPickerField() {
return (
<>
<View style={styles.inputGroup}>
<Text style={styles.label}>Private Key</Text>
<View style={{ marginBottom: 12 }}>
<Text
style={{
marginBottom: 6,
fontSize: 14,
color: theme.colors.textSecondary,
fontWeight: '600',
}}
>
Private Key
</Text>
<Pressable
style={[styles.input, { justifyContent: 'center' }]}
style={[
{
borderWidth: 1,
borderColor: theme.colors.border,
backgroundColor: theme.colors.inputBackground,
borderRadius: 10,
paddingHorizontal: 12,
paddingVertical: 12,
justifyContent: 'center',
},
]}
onPress={() => {
void listPrivateKeysQuery.refetch();
setOpen(true);
@@ -228,7 +289,7 @@ function KeyIdPickerField() {
<Text style={{ color: theme.colors.textPrimary }}>{display}</Text>
</Pressable>
{!selected && (
<Text style={styles.mutedText}>
<Text style={{ color: theme.colors.muted, fontSize: 14 }}>
Open Key Manager to add/select a key
</Text>
)}
@@ -239,15 +300,59 @@ function KeyIdPickerField() {
animationType="slide"
onRequestClose={() => setOpen(false)}
>
<View style={styles.modalOverlay}>
<View style={styles.modalSheet}>
<View style={styles.modalHeader}>
<Text style={styles.title}>Select Key</Text>
<View
style={{
flex: 1,
backgroundColor: theme.colors.overlay,
justifyContent: 'flex-end',
}}
>
<View
style={{
backgroundColor: theme.colors.background,
borderTopLeftRadius: 16,
borderTopRightRadius: 16,
padding: 16,
borderColor: theme.colors.borderStrong,
borderWidth: 1,
maxHeight: '85%',
}}
>
<View
style={{
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 8,
}}
>
<Text
style={{
color: theme.colors.textPrimary,
fontSize: 18,
fontWeight: '700',
}}
>
Select Key
</Text>
<Pressable
style={styles.modalCloseButton}
style={{
paddingHorizontal: 8,
paddingVertical: 6,
borderRadius: 8,
borderWidth: 1,
borderColor: theme.colors.border,
}}
onPress={() => setOpen(false)}
>
<Text style={styles.modalCloseText}>Close</Text>
<Text
style={{
color: theme.colors.textSecondary,
fontWeight: '600',
}}
>
Close
</Text>
</Pressable>
</View>
<KeyList
@@ -268,18 +373,32 @@ function PreviousConnectionsSection(props: {
onSelect: (connection: InputConnectionDetails) => void;
}) {
const theme = useTheme();
const styles = React.useMemo(() => makeStyles(theme), [theme]);
const listConnectionsQuery = useQuery(secretsManager.connections.query.list);
return (
<View style={styles.listSection}>
<Text style={styles.listTitle}>Previous Connections</Text>
<View style={{ marginTop: 20 }}>
<Text
style={{
fontSize: 16,
fontWeight: '700',
color: theme.colors.textPrimary,
marginBottom: 8,
}}
>
Previous Connections
</Text>
{listConnectionsQuery.isLoading ? (
<Text style={styles.mutedText}>Loading connections...</Text>
<Text style={{ color: theme.colors.muted, fontSize: 14 }}>
Loading connections...
</Text>
) : listConnectionsQuery.isError ? (
<Text style={styles.errorText}>Error loading connections</Text>
<Text
style={{ marginTop: 6, color: theme.colors.danger, fontSize: 12 }}
>
Error loading connections
</Text>
) : listConnectionsQuery.data?.length ? (
<View style={styles.listContainer}>
<View>
{listConnectionsQuery.data?.map((conn) => (
<ConnectionRow
key={conn.id}
@@ -289,7 +408,9 @@ function PreviousConnectionsSection(props: {
))}
</View>
) : (
<Text style={styles.mutedText}>No saved connections yet</Text>
<Text style={{ color: theme.colors.muted, fontSize: 14 }}>
No saved connections yet
</Text>
)}
</View>
);
@@ -300,223 +421,51 @@ function ConnectionRow(props: {
onSelect: (connection: InputConnectionDetails) => void;
}) {
const theme = useTheme();
const styles = React.useMemo(() => makeStyles(theme), [theme]);
const detailsQuery = useQuery(secretsManager.connections.query.get(props.id));
const details = detailsQuery.data?.value;
return (
<Pressable
style={styles.row}
style={{
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
backgroundColor: theme.colors.inputBackground,
borderWidth: 1,
borderColor: theme.colors.border,
borderRadius: 12,
paddingHorizontal: 12,
paddingVertical: 12,
marginBottom: 8,
}}
onPress={() => {
if (details) props.onSelect(details);
}}
disabled={!details}
>
<View style={styles.rowTextContainer}>
<Text style={styles.rowTitle}>
<View style={{ flex: 1, marginRight: 12 }}>
<Text
style={{
color: theme.colors.textPrimary,
fontSize: 15,
fontWeight: '600',
}}
>
{details ? `${details.username}@${details.host}` : 'Loading...'}
</Text>
<Text style={styles.rowSubtitle}>
<Text style={{ color: theme.colors.muted, marginTop: 2, fontSize: 12 }}>
{details ? `Port ${details.port}${details.security.type}` : ''}
</Text>
</View>
<Text style={styles.rowChevron}></Text>
</Pressable>
);
}
function makeStyles(theme: AppTheme) {
return StyleSheet.create({
container: {
flex: 1,
padding: 24,
backgroundColor: theme.colors.background,
justifyContent: 'center',
},
scrollContent: {
paddingBottom: 32,
},
header: {
marginBottom: 16,
alignItems: 'center',
},
appName: {
fontSize: 28,
fontWeight: '800',
color: theme.colors.textPrimary,
letterSpacing: 1,
},
appTagline: {
marginTop: 4,
fontSize: 13,
color: theme.colors.muted,
},
card: {
backgroundColor: theme.colors.surface,
borderRadius: 20,
padding: 24,
marginHorizontal: 4,
shadowColor: theme.colors.shadow,
shadowOpacity: 0.3,
shadowRadius: 16,
shadowOffset: { width: 0, height: 4 },
elevation: 8,
borderWidth: 1,
borderColor: theme.colors.borderStrong,
},
title: {
fontSize: 24,
fontWeight: '700',
color: theme.colors.textPrimary,
marginBottom: 6,
letterSpacing: 0.5,
},
subtitle: {
fontSize: 15,
color: theme.colors.muted,
marginBottom: 24,
lineHeight: 20,
},
inputGroup: {
marginBottom: 12,
},
label: {
marginBottom: 6,
fontSize: 14,
color: theme.colors.textSecondary,
fontWeight: '600',
},
input: {
borderWidth: 1,
borderColor: theme.colors.border,
backgroundColor: theme.colors.inputBackground,
color: theme.colors.textPrimary,
borderRadius: 10,
paddingHorizontal: 12,
paddingVertical: 12,
fontSize: 16,
},
errorText: {
marginTop: 6,
color: theme.colors.danger,
fontSize: 12,
},
actions: {
marginTop: 20,
},
mutedText: {
color: theme.colors.muted,
fontSize: 14,
},
submitButton: {
backgroundColor: theme.colors.primary,
borderRadius: 12,
paddingVertical: 16,
alignItems: 'center',
shadowColor: theme.colors.primary,
shadowOpacity: 0.3,
shadowRadius: 8,
shadowOffset: { width: 0, height: 2 },
elevation: 4,
},
submitButtonText: {
color: theme.colors.buttonTextOnPrimary,
fontWeight: '700',
fontSize: 16,
letterSpacing: 0.5,
},
buttonDisabled: {
backgroundColor: theme.colors.primaryDisabled,
opacity: 0.6,
},
secondaryButton: {
backgroundColor: theme.colors.transparent,
borderWidth: 1,
borderColor: theme.colors.border,
borderRadius: 12,
paddingVertical: 14,
alignItems: 'center',
marginTop: 12,
},
secondaryButtonText: {
color: theme.colors.textSecondary,
fontWeight: '600',
fontSize: 14,
letterSpacing: 0.3,
},
listSection: {
marginTop: 20,
},
listTitle: {
fontSize: 16,
fontWeight: '700',
color: theme.colors.textPrimary,
marginBottom: 8,
},
listContainer: {
// Intentionally empty for RN compatibility
},
row: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
backgroundColor: theme.colors.inputBackground,
borderWidth: 1,
borderColor: theme.colors.border,
borderRadius: 12,
paddingHorizontal: 12,
paddingVertical: 12,
marginBottom: 8,
},
rowTextContainer: {
flex: 1,
marginRight: 12,
},
rowTitle: {
color: theme.colors.textPrimary,
fontSize: 15,
fontWeight: '600',
},
rowSubtitle: {
color: theme.colors.muted,
marginTop: 2,
fontSize: 12,
},
rowChevron: {
<Text
style={{
color: theme.colors.muted,
fontSize: 22,
paddingHorizontal: 4,
},
modalOverlay: {
flex: 1,
backgroundColor: theme.colors.overlay,
justifyContent: 'flex-end',
},
modalSheet: {
backgroundColor: theme.colors.background,
borderTopLeftRadius: 16,
borderTopRightRadius: 16,
padding: 16,
borderColor: theme.colors.borderStrong,
borderWidth: 1,
maxHeight: '85%',
},
modalHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 8,
},
modalCloseButton: {
paddingHorizontal: 8,
paddingVertical: 6,
borderRadius: 8,
borderWidth: 1,
borderColor: theme.colors.border,
},
modalCloseText: {
color: theme.colors.textSecondary,
fontWeight: '600',
},
});
}}
>
</Text>
</Pressable>
);
}

View File

@@ -1,18 +1,27 @@
import { Link } from 'expo-router';
import React from 'react';
import { Pressable, StyleSheet, Text, View } from 'react-native';
import { useTheme, useThemeControls, type AppTheme } from '@/lib/theme';
import { Pressable, Text, View } from 'react-native';
import { useTheme, useThemeControls } from '@/lib/theme';
export default function Tab() {
const theme = useTheme();
const styles = React.useMemo(() => makeStyles(theme), [theme]);
const { themeName, setThemeName } = useThemeControls();
return (
<View style={styles.container}>
<View style={styles.section}>
<Text style={styles.sectionTitle}>Theme</Text>
<View style={styles.rowGroup}>
<View
style={{ flex: 1, padding: 16, backgroundColor: theme.colors.background }}
>
<View style={{ marginBottom: 24 }}>
<Text
style={{
color: theme.colors.textSecondary,
fontSize: 14,
marginBottom: 8,
}}
>
Theme
</Text>
<View style={{ gap: 8 }}>
<Row
label="Dark"
selected={themeName === 'dark'}
@@ -26,12 +35,49 @@ export default function Tab() {
</View>
</View>
<View style={styles.section}>
<Text style={styles.sectionTitle}>Security</Text>
<View style={{ marginBottom: 24 }}>
<Text
style={{
color: theme.colors.textSecondary,
fontSize: 14,
marginBottom: 8,
}}
>
Security
</Text>
<Link href="/(tabs)/settings/key-manager" asChild>
<Pressable style={styles.callout} accessibilityRole="button">
<Text style={styles.calloutLabel}>Manage Keys</Text>
<Text style={styles.calloutChevron}></Text>
<Pressable
style={{
backgroundColor: theme.colors.surface,
borderWidth: 1,
borderColor: theme.colors.border,
borderRadius: 12,
paddingHorizontal: 12,
paddingVertical: 14,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
}}
accessibilityRole="button"
>
<Text
style={{
color: theme.colors.textPrimary,
fontSize: 16,
fontWeight: '600',
}}
>
Manage Keys
</Text>
<Text
style={{
color: theme.colors.muted,
fontSize: 22,
paddingHorizontal: 4,
}}
>
</Text>
</Pressable>
</Link>
</View>
@@ -49,38 +95,11 @@ function Row({
onPress: () => void;
}) {
const theme = useTheme();
const styles = React.useMemo(() => makeStyles(theme), [theme]);
return (
<Pressable
onPress={onPress}
style={[styles.row, selected && styles.rowSelected]}
accessibilityRole="button"
accessibilityState={{ selected }}
>
<Text style={styles.rowLabel}>{label}</Text>
<Text style={styles.rowCheck}>{selected ? '✔' : ''}</Text>
</Pressable>
);
}
function makeStyles(theme: AppTheme) {
return StyleSheet.create({
container: {
flex: 1,
padding: 16,
backgroundColor: theme.colors.background,
},
// Title removed; screen header provides the title
section: {
marginBottom: 24,
},
sectionTitle: {
color: theme.colors.textSecondary,
fontSize: 14,
marginBottom: 8,
},
rowGroup: { gap: 8 },
row: {
style={[
{
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
@@ -91,39 +110,25 @@ function makeStyles(theme: AppTheme) {
paddingHorizontal: 12,
paddingVertical: 12,
},
rowSelected: {
borderColor: theme.colors.primary,
},
rowLabel: {
selected ? { borderColor: theme.colors.primary } : undefined,
]}
accessibilityRole="button"
accessibilityState={{ selected }}
>
<Text
style={{
color: theme.colors.textPrimary,
fontSize: 16,
fontWeight: '600',
},
rowCheck: {
color: theme.colors.primary,
fontSize: 16,
fontWeight: '800',
},
callout: {
backgroundColor: theme.colors.surface,
borderWidth: 1,
borderColor: theme.colors.border,
borderRadius: 12,
paddingHorizontal: 12,
paddingVertical: 14,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
calloutLabel: {
color: theme.colors.textPrimary,
fontSize: 16,
fontWeight: '600',
},
calloutChevron: {
color: theme.colors.muted,
fontSize: 22,
paddingHorizontal: 4,
},
});
}}
>
{label}
</Text>
<Text
style={{ color: theme.colors.primary, fontSize: 16, fontWeight: '800' }}
>
{selected ? '✔' : ''}
</Text>
</Pressable>
);
}

View File

@@ -6,7 +6,6 @@ import {
Platform,
Pressable,
ScrollView,
StyleSheet,
Text,
TextInput,
View,
@@ -36,8 +35,6 @@ function ShellDetail() {
: undefined;
const [shellData, setShellData] = useState('');
const [inputValue, setInputValue] = useState('');
const hiddenInputRef = useRef<TextInput | null>(null);
useEffect(() => {
if (!connection) return;
@@ -61,42 +58,11 @@ function ShellDetail() {
scrollViewRef.current?.scrollToEnd({ animated: true });
}, [shellData]);
useEffect(() => {
const focusTimeout = setTimeout(() => {
hiddenInputRef.current?.focus();
}, 0);
return () => clearTimeout(focusTimeout);
}, []);
async function sendChunk(chunk: string) {
if (!shell || !chunk) return;
const bytes = Uint8Array.from(new TextEncoder().encode(chunk)).buffer;
try {
await shell.sendData(bytes);
} catch {}
}
return (
<SafeAreaView style={{ flex: 1, backgroundColor: theme.colors.background }}>
<Stack.Screen
options={{
headerBackVisible: true,
headerLeft:
Platform.OS === 'android'
? () => (
<Pressable
onPress={() => router.back()}
hitSlop={10}
style={{ paddingHorizontal: 4, paddingVertical: 4 }}
>
<Ionicons
name="chevron-back"
size={22}
color={theme.colors.textPrimary}
/>
</Pressable>
)
: undefined,
headerRight: () => (
<Pressable
accessibilityLabel="Disconnect"
@@ -114,77 +80,35 @@ function ShellDetail() {
}}
/>
<View
style={[styles.container, { backgroundColor: theme.colors.background }]}
style={[
{ flex: 1, backgroundColor: '#0B1324', padding: 12 },
{ backgroundColor: theme.colors.background },
]}
>
<View
style={styles.terminal}
onStartShouldSetResponder={() => {
hiddenInputRef.current?.focus();
return false;
}}
>
<ScrollView
ref={scrollViewRef}
contentContainerStyle={styles.terminalContent}
keyboardShouldPersistTaps="handled"
>
<Text selectable style={styles.terminalText}>
{shellData || 'Connected. Output will appear here...'}
</Text>
</ScrollView>
<TextInput
ref={hiddenInputRef}
value={inputValue}
onChangeText={async (text) => {
if (!text) return;
await sendChunk(text);
setInputValue('');
}}
onKeyPress={async (e) => {
const key = e.nativeEvent.key;
if (key === 'Backspace') {
await sendChunk('\b');
}
}}
onSubmitEditing={async () => {
await sendChunk('\n');
}}
style={styles.hiddenInput}
autoFocus
multiline
caretHidden
autoCorrect={false}
autoCapitalize="none"
keyboardType="visible-password"
blurOnSubmit={false}
/>
</View>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#0B1324',
padding: 12,
},
terminal: {
style={{
flex: 1,
backgroundColor: '#0E172B',
borderRadius: 12,
height: 400,
borderWidth: 1,
borderColor: '#2A3655',
overflow: 'hidden',
marginBottom: 12,
},
terminalContent: {
}}
>
<ScrollView
ref={scrollViewRef}
contentContainerStyle={{
paddingHorizontal: 12,
paddingTop: 4,
paddingBottom: 12,
},
terminalText: {
}}
keyboardShouldPersistTaps="handled"
>
<Text
selectable
style={{
color: '#D1D5DB',
fontSize: 14,
lineHeight: 18,
@@ -193,14 +117,83 @@ const styles = StyleSheet.create({
android: 'monospace',
default: 'monospace',
}),
}}
>
{shellData || 'Connected. Output will appear here...'}
</Text>
</ScrollView>
</View>
<CommandInput
executeCommand={async (command) => {
await shell?.sendData(
Uint8Array.from(new TextEncoder().encode(command + '\n')).buffer,
);
}}
/>
</View>
</SafeAreaView>
);
}
function CommandInput(props: {
executeCommand: (command: string) => Promise<void>;
}) {
const [command, setCommand] = useState('');
async function handleExecute() {
if (!command.trim()) return;
await props.executeCommand(command);
setCommand('');
}
return (
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
<TextInput
testID="command-input"
style={{
flex: 1,
backgroundColor: '#0E172B',
borderWidth: 1,
borderColor: '#2A3655',
borderRadius: 10,
paddingHorizontal: 12,
paddingVertical: 12,
color: '#E5E7EB',
fontSize: 16,
fontFamily: Platform.select({
ios: 'Menlo',
android: 'monospace',
default: 'monospace',
}),
}}
value={command}
onChangeText={setCommand}
placeholder="Type a command and press Enter or Execute"
placeholderTextColor="#9AA0A6"
autoCapitalize="none"
autoCorrect={false}
returnKeyType="send"
onSubmitEditing={handleExecute}
/>
<Pressable
style={[
{
backgroundColor: '#2563EB',
borderRadius: 10,
paddingHorizontal: 16,
paddingVertical: 12,
alignItems: 'center',
justifyContent: 'center',
},
hiddenInput: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
opacity: 0,
color: 'transparent',
},
});
{ marginTop: 8 },
]}
onPress={handleExecute}
testID="execute-button"
>
<Text style={{ color: '#FFFFFF', fontWeight: '700', fontSize: 14 }}>
Execute
</Text>
</Pressable>
</View>
);
}

View File

@@ -13,7 +13,6 @@ import {
Modal,
Platform,
Pressable,
StyleSheet,
Text,
View,
} from 'react-native';
@@ -25,7 +24,7 @@ import {
listSshShellsQueryOptions,
type ShellWithConnection,
} from '@/lib/query-fns';
import { useTheme, type AppTheme } from '@/lib/theme';
import { useTheme } from '@/lib/theme';
export default function TabsShellList() {
const theme = useTheme();
@@ -59,10 +58,16 @@ function ShellContent() {
function LoadingState() {
const theme = useTheme();
const styles = React.useMemo(() => makeStyles(theme), [theme]);
return (
<View style={styles.centerContent}>
<Text style={styles.mutedText}>Loading...</Text>
<View
style={{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
gap: 12,
}}
>
<Text style={{ color: theme.colors.muted }}>Loading...</Text>
</View>
);
}
@@ -177,7 +182,6 @@ function GroupedView({
setActionTarget: (target: ActionTarget) => void;
}) {
const theme = useTheme();
const styles = React.useMemo(() => makeStyles(theme), [theme]);
const [expanded, setExpanded] = React.useState<Record<string, boolean>>({});
return (
<FlashList
@@ -185,9 +189,19 @@ function GroupedView({
// estimatedItemSize={80}
keyExtractor={(item) => item.connectionId}
renderItem={({ item }) => (
<View style={styles.groupContainer}>
<View style={{ gap: 12 }}>
<Pressable
style={styles.groupHeader}
style={{
backgroundColor: theme.colors.surface,
borderWidth: 1,
borderColor: theme.colors.border,
borderRadius: 12,
paddingHorizontal: 12,
paddingVertical: 12,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
}}
onPress={() =>
setExpanded((prev) => ({
...prev,
@@ -196,15 +210,27 @@ function GroupedView({
}
>
<View>
<Text style={styles.groupTitle}>
<Text
style={{
color: theme.colors.textPrimary,
fontSize: 16,
fontWeight: '700',
}}
>
{item.connectionDetails.username}@{item.connectionDetails.host}
</Text>
<Text style={styles.groupSubtitle}>
<Text
style={{
color: theme.colors.muted,
fontSize: 12,
marginTop: 2,
}}
>
Port {item.connectionDetails.port} {item.shells.length} shell
{item.shells.length === 1 ? '' : 's'}
</Text>
</View>
<Text style={styles.groupChevron}>
<Text style={{ color: theme.colors.muted, fontSize: 18 }}>
{expanded[item.connectionId] ? '▾' : '▸'}
</Text>
</Pressable>
@@ -237,13 +263,19 @@ function GroupedView({
function EmptyState() {
const theme = useTheme();
const styles = React.useMemo(() => makeStyles(theme), [theme]);
return (
<View style={styles.centerContent}>
<Text style={styles.mutedText}>
<View
style={{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
gap: 12,
}}
>
<Text style={{ color: theme.colors.muted }}>
No active shells. Connect from Host tab.
</Text>
<Link href="/" style={styles.link}>
<Link href="/" style={{ color: theme.colors.primary, fontWeight: '600' }}>
Go to Hosts
</Link>
</View>
@@ -258,14 +290,23 @@ function ShellCard({
onLongPress?: () => void;
}) {
const theme = useTheme();
const styles = React.useMemo(() => makeStyles(theme), [theme]);
const router = useRouter();
const since = formatDistanceToNow(new Date(shell.createdAtMs), {
addSuffix: true,
});
return (
<Pressable
style={styles.card}
style={{
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
backgroundColor: theme.colors.inputBackground,
borderWidth: 1,
borderColor: theme.colors.border,
borderRadius: 12,
paddingHorizontal: 12,
paddingVertical: 12,
}}
onPress={() =>
router.push({
pathname: '/shell/detail',
@@ -278,16 +319,40 @@ function ShellCard({
onLongPress={onLongPress}
>
<View style={{ flex: 1 }}>
<Text style={styles.cardTitle} numberOfLines={1}>
<Text
style={{
color: theme.colors.textPrimary,
fontSize: 15,
fontWeight: '600',
}}
numberOfLines={1}
>
{shell.connection.connectionDetails.username}@
{shell.connection.connectionDetails.host}
</Text>
<Text style={styles.cardSubtitle} numberOfLines={1}>
<Text
style={{
color: theme.colors.textSecondary,
fontSize: 12,
marginTop: 2,
}}
numberOfLines={1}
>
Port {shell.connection.connectionDetails.port} {shell.pty}
</Text>
<Text style={styles.cardMeta}>Started {since}</Text>
<Text style={{ color: theme.colors.muted, fontSize: 12, marginTop: 6 }}>
Started {since}
</Text>
</View>
<Text style={styles.cardChevron}></Text>
<Text
style={{
color: theme.colors.muted,
fontSize: 22,
paddingHorizontal: 4,
}}
>
</Text>
</Pressable>
);
}
@@ -304,7 +369,6 @@ function ActionsSheet({
onDisconnect: () => void;
}) {
const theme = useTheme();
const styles = React.useMemo(() => makeStyles(theme), [theme]);
const open = !!target;
return (
<Modal
@@ -313,22 +377,98 @@ function ActionsSheet({
animationType="slide"
onRequestClose={onClose}
>
<View style={styles.modalOverlay}>
<View style={styles.modalSheet}>
<Text style={styles.title}>Shell Actions</Text>
<View
style={{
flex: 1,
backgroundColor: theme.colors.overlay,
justifyContent: 'flex-end',
}}
>
<View
style={{
backgroundColor: theme.colors.background,
borderTopLeftRadius: 16,
borderTopRightRadius: 16,
padding: 16,
borderColor: theme.colors.borderStrong,
borderWidth: 1,
}}
>
<Text
style={{
color: theme.colors.textPrimary,
fontSize: 18,
fontWeight: '700',
}}
>
Shell Actions
</Text>
<View style={{ height: 12 }} />
<Pressable style={styles.primaryButton} onPress={onCloseShell}>
<Text style={styles.primaryButtonText}>Close Shell</Text>
<Pressable
style={{
backgroundColor: theme.colors.primary,
borderRadius: 12,
paddingVertical: 14,
alignItems: 'center',
}}
onPress={onCloseShell}
>
<Text
style={{
color: theme.colors.buttonTextOnPrimary,
fontWeight: '700',
fontSize: 14,
letterSpacing: 0.3,
}}
>
Close Shell
</Text>
</Pressable>
<View style={{ height: 8 }} />
<Pressable style={styles.secondaryButton} onPress={onDisconnect}>
<Text style={styles.secondaryButtonText}>
<Pressable
style={{
backgroundColor: theme.colors.transparent,
borderWidth: 1,
borderColor: theme.colors.border,
borderRadius: 12,
paddingVertical: 14,
alignItems: 'center',
}}
onPress={onDisconnect}
>
<Text
style={{
color: theme.colors.textSecondary,
fontWeight: '600',
fontSize: 14,
letterSpacing: 0.3,
}}
>
Disconnect Connection
</Text>
</Pressable>
<View style={{ height: 8 }} />
<Pressable style={styles.secondaryButton} onPress={onClose}>
<Text style={styles.secondaryButtonText}>Cancel</Text>
<Pressable
style={{
backgroundColor: theme.colors.transparent,
borderWidth: 1,
borderColor: theme.colors.border,
borderRadius: 12,
paddingVertical: 14,
alignItems: 'center',
}}
onPress={onClose}
>
<Text
style={{
color: theme.colors.textSecondary,
fontWeight: '600',
fontSize: 14,
letterSpacing: 0.3,
}}
>
Cancel
</Text>
</Pressable>
</View>
</View>
@@ -382,125 +522,3 @@ function HeaderViewModeButton() {
</Pressable>
);
}
function makeStyles(theme: AppTheme) {
return StyleSheet.create({
centerContent: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
gap: 12,
},
mutedText: { color: theme.colors.muted },
link: { color: theme.colors.primary, fontWeight: '600' },
// headerBar/title removed in favor of TopBarToggle
groupContainer: {
gap: 12,
},
groupHeader: {
backgroundColor: theme.colors.surface,
borderWidth: 1,
borderColor: theme.colors.border,
borderRadius: 12,
paddingHorizontal: 12,
paddingVertical: 12,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
groupTitle: {
color: theme.colors.textPrimary,
fontSize: 16,
fontWeight: '700',
},
groupSubtitle: {
color: theme.colors.muted,
fontSize: 12,
marginTop: 2,
},
groupChevron: {
color: theme.colors.muted,
fontSize: 18,
},
card: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
backgroundColor: theme.colors.inputBackground,
borderWidth: 1,
borderColor: theme.colors.border,
borderRadius: 12,
paddingHorizontal: 12,
paddingVertical: 12,
},
cardTitle: {
color: theme.colors.textPrimary,
fontSize: 15,
fontWeight: '600',
},
cardSubtitle: {
color: theme.colors.textSecondary,
fontSize: 12,
marginTop: 2,
},
cardMeta: {
color: theme.colors.muted,
fontSize: 12,
marginTop: 6,
},
cardChevron: {
color: theme.colors.muted,
fontSize: 22,
paddingHorizontal: 4,
},
modalOverlay: {
flex: 1,
backgroundColor: theme.colors.overlay,
justifyContent: 'flex-end',
},
modalSheet: {
backgroundColor: theme.colors.background,
borderTopLeftRadius: 16,
borderTopRightRadius: 16,
padding: 16,
borderColor: theme.colors.borderStrong,
borderWidth: 1,
},
title: {
color: theme.colors.textPrimary,
fontSize: 18,
fontWeight: '700',
},
primaryButton: {
backgroundColor: theme.colors.primary,
borderRadius: 12,
paddingVertical: 14,
alignItems: 'center',
},
primaryButtonText: {
color: theme.colors.buttonTextOnPrimary,
fontWeight: '700',
fontSize: 14,
letterSpacing: 0.3,
},
secondaryButton: {
backgroundColor: theme.colors.transparent,
borderWidth: 1,
borderColor: theme.colors.border,
borderRadius: 12,
paddingVertical: 14,
alignItems: 'center',
},
secondaryButtonText: {
color: theme.colors.textSecondary,
fontWeight: '600',
fontSize: 14,
letterSpacing: 0.3,
},
});
}

View File

@@ -1,4 +1,5 @@
import { QueryClientProvider } from '@tanstack/react-query';
import * as DevClient from 'expo-dev-client';
import { isLiquidGlassAvailable } from 'expo-glass-effect';
import { Stack } from 'expo-router';
import React from 'react';
@@ -10,6 +11,15 @@ console.log('Fressh App Init', {
isLiquidGlassAvailable: isLiquidGlassAvailable(),
});
void DevClient.registerDevMenuItems([
{
callback: () => {
console.log('Hello from dev menu');
},
name: 'Hello from dev menu',
},
]);
export default function RootLayout() {
return (
<QueryClientProvider client={queryClient}>

View File

@@ -3,14 +3,7 @@ import {
createFormHookContexts,
useStore,
} from '@tanstack/react-form';
import {
Pressable,
StyleSheet,
Switch,
Text,
TextInput,
View,
} from 'react-native';
import { Pressable, Switch, Text, TextInput, View } from 'react-native';
function FieldInfo() {
const field = useFieldContext();
@@ -18,9 +11,11 @@ function FieldInfo() {
const errorMessage = meta?.errors?.[0]; // TODO: typesafe errors
return (
<View style={styles.fieldInfo}>
<View style={{ marginTop: 6 }}>
{errorMessage ? (
<Text style={styles.errorText}>{String(errorMessage)}</Text>
<Text style={{ color: '#FCA5A5', fontSize: 12 }}>
{String(errorMessage)}
</Text>
) : null}
</View>
);
@@ -36,10 +31,33 @@ export function TextField(
const field = useFieldContext<string>();
return (
<View style={styles.inputGroup}>
{label ? <Text style={styles.label}>{label}</Text> : null}
<View style={{ marginBottom: 16 }}>
{label ? (
<Text
style={{
marginBottom: 6,
fontSize: 14,
color: '#C6CBD3',
fontWeight: '600',
}}
>
{label}
</Text>
) : null}
<TextInput
style={[styles.input, style]}
style={[
{
borderWidth: 1,
borderColor: '#2A3655',
backgroundColor: '#0E172B',
color: '#E5E7EB',
borderRadius: 10,
paddingHorizontal: 12,
paddingVertical: 12,
fontSize: 16,
},
style,
]}
placeholderTextColor="#9AA0A6"
value={field.state.value}
onChangeText={field.handleChange}
@@ -59,11 +77,34 @@ export function NumberField(
const { label, style, keyboardType, onChangeText, ...rest } = props;
const field = useFieldContext<number>();
return (
<View style={styles.inputGroup}>
{label ? <Text style={styles.label}>{label}</Text> : null}
<View style={{ marginBottom: 16 }}>
{label ? (
<Text
style={{
marginBottom: 6,
fontSize: 14,
color: '#C6CBD3',
fontWeight: '600',
}}
>
{label}
</Text>
) : null}
<TextInput
keyboardType={keyboardType ?? 'numeric'}
style={[styles.input, style]}
style={[
{
borderWidth: 1,
borderColor: '#2A3655',
backgroundColor: '#0E172B',
color: '#E5E7EB',
borderRadius: 10,
paddingHorizontal: 12,
paddingVertical: 12,
fontSize: 16,
},
style,
]}
placeholderTextColor="#9AA0A6"
value={field.state.value.toString()}
onChangeText={(text) => field.handleChange(Number(text))}
@@ -84,10 +125,31 @@ export function SwitchField(
const field = useFieldContext<boolean>();
return (
<View style={styles.inputGroup}>
{label ? <Text style={styles.label}>{label}</Text> : null}
<View style={{ marginBottom: 16 }}>
{label ? (
<Text
style={{
marginBottom: 6,
fontSize: 14,
color: '#C6CBD3',
fontWeight: '600',
}}
>
{label}
</Text>
) : null}
<Switch
style={[styles.input, style]}
style={[
{
borderWidth: 1,
borderColor: '#2A3655',
backgroundColor: '#0E172B',
borderRadius: 10,
paddingHorizontal: 12,
paddingVertical: 12,
},
style,
]}
value={field.state.value}
onChange={(event) => field.handleChange(event.nativeEvent.value)}
onBlur={field.handleBlur}
@@ -114,13 +176,18 @@ export function SubmitButton(
<Pressable
{...rest}
style={[
styles.submitButton,
disabled ? styles.buttonDisabled : undefined,
{
backgroundColor: '#2563EB',
borderRadius: 10,
paddingVertical: 14,
alignItems: 'center',
},
disabled ? { backgroundColor: '#3B82F6', opacity: 0.6 } : undefined,
]}
onPress={onPress}
disabled={disabled || isSubmitting}
>
<Text style={styles.submitButtonText}>
<Text style={{ color: '#FFFFFF', fontWeight: '700', fontSize: 16 }}>
{isSubmitting ? 'Connecting...' : title}
</Text>
</Pressable>
@@ -145,56 +212,4 @@ export const { useAppForm, withForm, withFieldGroup } = createFormHook({
formContext,
});
const styles = StyleSheet.create({
inputGroup: {
marginBottom: 16,
},
label: {
marginBottom: 6,
fontSize: 14,
color: '#C6CBD3',
fontWeight: '600',
},
input: {
borderWidth: 1,
borderColor: '#2A3655',
backgroundColor: '#0E172B',
color: '#E5E7EB',
borderRadius: 10,
paddingHorizontal: 12,
paddingVertical: 12,
fontSize: 16,
},
errorText: {
marginTop: 6,
color: '#FCA5A5',
fontSize: 12,
},
submitButton: {
backgroundColor: '#2563EB',
borderRadius: 10,
paddingVertical: 14,
alignItems: 'center',
},
submitButtonText: {
color: '#FFFFFF',
fontWeight: '700',
fontSize: 16,
},
buttonDisabled: {
backgroundColor: '#3B82F6',
opacity: 0.6,
},
fieldInfo: {
marginTop: 6,
color: '#FCA5A5',
fontSize: 12,
},
pickerContainer: {
paddingHorizontal: 8,
paddingVertical: 4,
},
picker: {
color: '#E5E7EB',
},
});
// Styles inlined per component

View File

@@ -1,13 +1,6 @@
import { useMutation, useQuery } from '@tanstack/react-query';
import React from 'react';
import {
Pressable,
ScrollView,
StyleSheet,
Text,
TextInput,
View,
} from 'react-native';
import { Pressable, ScrollView, Text, TextInput, View } from 'react-native';
import { secretsManager } from '@/lib/secrets-manager';
export type KeyListMode = 'manage' | 'select';
@@ -38,13 +31,19 @@ export function KeyList(props: {
<ScrollView contentContainerStyle={{ padding: 16 }}>
<Pressable
style={[
styles.primaryButton,
{
backgroundColor: '#2563EB',
borderRadius: 10,
paddingVertical: 12,
alignItems: 'center',
marginBottom: 12,
},
generateMutation.isPending && { opacity: 0.7 },
]}
disabled={generateMutation.isPending}
onPress={() => generateMutation.mutate()}
>
<Text style={styles.primaryButtonText}>
<Text style={{ color: '#FFFFFF', fontWeight: '700', fontSize: 14 }}>
{generateMutation.isPending
? 'Generating…'
: 'Generate New RSA 4096 Key'}
@@ -52,9 +51,9 @@ export function KeyList(props: {
</Pressable>
{listKeysQuery.isLoading ? (
<Text style={styles.muted}>Loading keys</Text>
<Text style={{ color: '#9AA0A6' }}>Loading keys</Text>
) : listKeysQuery.isError ? (
<Text style={styles.error}>Error loading keys</Text>
<Text style={{ color: '#FCA5A5' }}>Error loading keys</Text>
) : listKeysQuery.data?.length ? (
<View>
{listKeysQuery.data.map((k) => (
@@ -67,7 +66,7 @@ export function KeyList(props: {
))}
</View>
) : (
<Text style={styles.muted}>No keys yet</Text>
<Text style={{ color: '#9AA0A6' }}>No keys yet</Text>
)}
</ScrollView>
);
@@ -135,77 +134,8 @@ function KeyRow(props: {
if (!entry) return null;
return (
<View style={styles.row}>
<View style={{ flex: 1, marginRight: 8 }}>
<Text style={styles.rowTitle}>
{entry.manifestEntry.metadata?.label ?? entry.manifestEntry.id}
{entry.manifestEntry.metadata?.isDefault ? ' • Default' : ''}
</Text>
<Text style={styles.rowSub}>ID: {entry.manifestEntry.id}</Text>
{props.mode === 'manage' ? (
<TextInput
style={styles.input}
placeholder="Display name"
placeholderTextColor="#9AA0A6"
value={label}
onChangeText={setLabel}
/>
) : null}
</View>
<View style={styles.rowActions}>
{props.mode === 'select' ? (
<Pressable
onPress={() => setDefaultMutation.mutate()}
style={styles.primaryButton}
>
<Text style={styles.primaryButtonText}>Select</Text>
</Pressable>
) : null}
{props.mode === 'manage' ? (
<Pressable
style={[
styles.secondaryButton,
renameMutation.isPending && { opacity: 0.6 },
]}
onPress={() => renameMutation.mutate(label)}
disabled={renameMutation.isPending}
>
<Text style={styles.secondaryButtonText}>
{renameMutation.isPending ? 'Saving…' : 'Save'}
</Text>
</Pressable>
) : null}
{!entry.manifestEntry.metadata?.isDefault ? (
<Pressable
style={styles.secondaryButton}
onPress={() => setDefaultMutation.mutate()}
>
<Text style={styles.secondaryButtonText}>Set Default</Text>
</Pressable>
) : null}
<Pressable
style={styles.dangerButton}
onPress={() => deleteMutation.mutate()}
>
<Text style={styles.dangerButtonText}>Delete</Text>
</Pressable>
</View>
</View>
);
}
const styles = StyleSheet.create({
primaryButton: {
backgroundColor: '#2563EB',
borderRadius: 10,
paddingVertical: 12,
alignItems: 'center',
marginBottom: 12,
},
primaryButtonText: { color: '#FFFFFF', fontWeight: '700', fontSize: 14 },
muted: { color: '#9AA0A6' },
error: { color: '#FCA5A5' },
row: {
<View
style={{
flexDirection: 'row',
alignItems: 'flex-start',
justifyContent: 'space-between',
@@ -216,31 +146,19 @@ const styles = StyleSheet.create({
paddingHorizontal: 12,
paddingVertical: 12,
marginBottom: 10,
},
rowTitle: { color: '#E5E7EB', fontSize: 15, fontWeight: '600' },
rowSub: { color: '#9AA0A6', fontSize: 12, marginTop: 2 },
rowActions: { gap: 6, alignItems: 'flex-end' },
secondaryButton: {
backgroundColor: 'transparent',
borderWidth: 1,
borderColor: '#2A3655',
borderRadius: 10,
paddingVertical: 8,
paddingHorizontal: 10,
alignItems: 'center',
},
secondaryButtonText: { color: '#C6CBD3', fontWeight: '600', fontSize: 12 },
dangerButton: {
backgroundColor: 'transparent',
borderWidth: 1,
borderColor: '#7F1D1D',
borderRadius: 10,
paddingVertical: 8,
paddingHorizontal: 10,
alignItems: 'center',
},
dangerButtonText: { color: '#FCA5A5', fontWeight: '700', fontSize: 12 },
input: {
}}
>
<View style={{ flex: 1, marginRight: 8 }}>
<Text style={{ color: '#E5E7EB', fontSize: 15, fontWeight: '600' }}>
{entry.manifestEntry.metadata?.label ?? entry.manifestEntry.id}
{entry.manifestEntry.metadata?.isDefault ? ' • Default' : ''}
</Text>
<Text style={{ color: '#9AA0A6', fontSize: 12, marginTop: 2 }}>
ID: {entry.manifestEntry.id}
</Text>
{props.mode === 'manage' ? (
<TextInput
style={{
borderWidth: 1,
borderColor: '#2A3655',
backgroundColor: '#0E172B',
@@ -250,5 +168,88 @@ const styles = StyleSheet.create({
paddingVertical: 10,
fontSize: 16,
marginTop: 8,
}}
placeholder="Display name"
placeholderTextColor="#9AA0A6"
value={label}
onChangeText={setLabel}
/>
) : null}
</View>
<View style={{ gap: 6, alignItems: 'flex-end' }}>
{props.mode === 'select' ? (
<Pressable
onPress={() => setDefaultMutation.mutate()}
style={{
backgroundColor: '#2563EB',
borderRadius: 10,
paddingVertical: 12,
paddingHorizontal: 10,
alignItems: 'center',
}}
>
<Text style={{ color: '#FFFFFF', fontWeight: '700', fontSize: 12 }}>
Select
</Text>
</Pressable>
) : null}
{props.mode === 'manage' ? (
<Pressable
style={[
{
backgroundColor: 'transparent',
borderWidth: 1,
borderColor: '#2A3655',
borderRadius: 10,
paddingVertical: 8,
paddingHorizontal: 10,
alignItems: 'center',
},
});
renameMutation.isPending && { opacity: 0.6 },
]}
onPress={() => renameMutation.mutate(label)}
disabled={renameMutation.isPending}
>
<Text style={{ color: '#C6CBD3', fontWeight: '600', fontSize: 12 }}>
{renameMutation.isPending ? 'Saving…' : 'Save'}
</Text>
</Pressable>
) : null}
{!entry.manifestEntry.metadata?.isDefault ? (
<Pressable
style={{
backgroundColor: 'transparent',
borderWidth: 1,
borderColor: '#2A3655',
borderRadius: 10,
paddingVertical: 8,
paddingHorizontal: 10,
alignItems: 'center',
}}
onPress={() => setDefaultMutation.mutate()}
>
<Text style={{ color: '#C6CBD3', fontWeight: '600', fontSize: 12 }}>
Set Default
</Text>
</Pressable>
) : null}
<Pressable
style={{
backgroundColor: 'transparent',
borderWidth: 1,
borderColor: '#7F1D1D',
borderRadius: 10,
paddingVertical: 8,
paddingHorizontal: 10,
alignItems: 'center',
}}
onPress={() => deleteMutation.mutate()}
>
<Text style={{ color: '#FCA5A5', fontWeight: '700', fontSize: 12 }}>
Delete
</Text>
</Pressable>
</View>
</View>
);
}

284
pnpm-lock.yaml generated
View File

@@ -42,7 +42,7 @@ importers:
dependencies:
'@expo/vector-icons':
specifier: ^15.0.2
version: 15.0.2(expo-font@14.0.8(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
version: 15.0.2(expo-font@14.0.8(expo@54.0.8)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
'@fressh/assets':
specifier: workspace:*
version: link:../../packages/assets
@@ -74,59 +74,59 @@ importers:
specifier: ^4.1.0
version: 4.1.0
expo:
specifier: 54.0.7
version: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.5)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
specifier: 54.0.8
version: 54.0.8(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.6)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo-clipboard:
specifier: ~8.0.7
version: 8.0.7(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
version: 8.0.7(expo@54.0.8)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo-constants:
specifier: ~18.0.8
version: 18.0.8(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))
specifier: ~18.0.9
version: 18.0.9(expo@54.0.8)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))
expo-crypto:
specifier: ~15.0.7
version: 15.0.7(expo@54.0.7)
version: 15.0.7(expo@54.0.8)
expo-dev-client:
specifier: ~6.0.12
version: 6.0.12(expo@54.0.7)
version: 6.0.12(expo@54.0.8)
expo-document-picker:
specifier: ~14.0.7
version: 14.0.7(expo@54.0.7)
version: 14.0.7(expo@54.0.8)
expo-file-system:
specifier: ~19.0.14
version: 19.0.14(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))
version: 19.0.14(expo@54.0.8)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))
expo-font:
specifier: ~14.0.8
version: 14.0.8(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
version: 14.0.8(expo@54.0.8)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo-glass-effect:
specifier: ^0.1.3
version: 0.1.3(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
version: 0.1.3(expo@54.0.8)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo-haptics:
specifier: ~15.0.7
version: 15.0.7(expo@54.0.7)
version: 15.0.7(expo@54.0.8)
expo-image:
specifier: ~3.0.8
version: 3.0.8(expo@54.0.7)(react-native-web@0.21.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
version: 3.0.8(expo@54.0.8)(react-native-web@0.21.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo-linking:
specifier: ~8.0.8
version: 8.0.8(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
version: 8.0.8(expo@54.0.8)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo-router:
specifier: 6.0.5
version: 6.0.5(750b4158402c81723fe42a215de7942a)
specifier: 6.0.6
version: 6.0.6(be6ce5c43438e2db372447bf3c6a78fa)
expo-secure-store:
specifier: ~15.0.7
version: 15.0.7(expo@54.0.7)
version: 15.0.7(expo@54.0.8)
expo-splash-screen:
specifier: ~31.0.10
version: 31.0.10(expo@54.0.7)
version: 31.0.10(expo@54.0.8)
expo-status-bar:
specifier: ~3.0.8
version: 3.0.8(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo-symbols:
specifier: ~1.0.7
version: 1.0.7(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))
version: 1.0.7(expo@54.0.8)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))
expo-system-ui:
specifier: ~6.0.7
version: 6.0.7(expo@54.0.7)(react-native-web@0.21.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))
version: 6.0.7(expo@54.0.8)(react-native-web@0.21.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))
react:
specifier: 19.1.0
version: 19.1.0
@@ -1297,8 +1297,8 @@ packages:
resolution: {integrity: sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@expo/cli@54.0.5':
resolution: {integrity: sha512-8MZOZKHfHRHTBQu2/PXBi7eCKc2aF1i1JsZweL/P7aX8nivhrP6KV6An5PtO1/rrdnS9z7pmX2KwMygvvaFNhg==}
'@expo/cli@54.0.6':
resolution: {integrity: sha512-BgxJshNqSODb4Rq4q4lHLBVWVL4683Q+PSJ2fd+m3D5Jqd8nu9zGvcq6I/H8AXV/Ux31eIuUgAojPCjW8LRyZA==}
hasBin: true
peerDependencies:
expo: '*'
@@ -1348,8 +1348,8 @@ packages:
'@expo/env@2.0.7':
resolution: {integrity: sha512-BNETbLEohk3HQ2LxwwezpG8pq+h7Fs7/vAMP3eAtFT1BCpprLYoBBFZH7gW4aqGfqOcVP4Lc91j014verrYNGg==}
'@expo/fingerprint@0.15.0':
resolution: {integrity: sha512-PrLA6fxScZfnLy7OHZ2GHXsDG9YbE7L5DbNhion6j/U/O+FQgz4VbxJarW5C00kMg1ll2u6EghB7ENAvL1T4qg==}
'@expo/fingerprint@0.15.1':
resolution: {integrity: sha512-U1S9DwiapCHQjHdHDDyO/oXsl/1oEHSHZRRkWDDrHgXRUDiAVIySw9Unvvcr118Ee6/x4NmKSZY1X0VagrqmFg==}
hasBin: true
'@expo/image-utils@0.8.7':
@@ -1361,6 +1361,14 @@ packages:
'@expo/json-file@10.0.7':
resolution: {integrity: sha512-z2OTC0XNO6riZu98EjdNHC05l51ySeTto6GP7oSQrCvQgG9ARBwD1YvMQaVZ9wU7p/4LzSf1O7tckL3B45fPpw==}
'@expo/mcp-tunnel@0.0.7':
resolution: {integrity: sha512-ht8Q1nKtiHobZqkUqt/7awwjW2D59ardP6XDVmGceGjQtoZELVaJDHyMIX+aVG9SZ9aj8+uGlhQYeBi57SZPMA==}
peerDependencies:
'@modelcontextprotocol/sdk': ^1.13.2
peerDependenciesMeta:
'@modelcontextprotocol/sdk':
optional: true
'@expo/metro-config@54.0.3':
resolution: {integrity: sha512-TQ5MKSGFB6zJxi+Yr8VYXQFHzRXgvSJzNsHX1otTqnxjXbptwYiXhljAqGSjr3pByq4+sHX/GifMk6fGgAANmA==}
peerDependencies:
@@ -1387,8 +1395,8 @@ packages:
resolution: {integrity: sha512-IClSOXxR0YUFxIriUJVqyYki7lLMIHrrzOaP01yxAL1G8pj2DWV5eW1y5jSzIcIfSCNhtGsshGd1tU/AYup5iQ==}
engines: {node: '>=12'}
'@expo/package-manager@1.9.7':
resolution: {integrity: sha512-k3uky8Qzlv21rxuPvP2KUTAy8NI0b/LP7BSXcwJpS/rH7RmiAqUXgzPar3I1OmKGgxpod78Y9Mae//F8d3aiOQ==}
'@expo/package-manager@1.9.8':
resolution: {integrity: sha512-4/I6OWquKXYnzo38pkISHCOCOXxfeEmu4uDoERq1Ei/9Ur/s9y3kLbAamEkitUkDC7gHk1INxRWEfFNzGbmOrA==}
'@expo/plist@0.4.6':
resolution: {integrity: sha512-6yklhtUWohs1rBSC8dGyBBpElEbosjXN0zJN/+1/B121n7pPWvd9y/UGJm+2x7b81VnW3AHmWVnbU/u0INQsqA==}
@@ -4704,8 +4712,8 @@ packages:
react: '*'
react-native: '*'
expo-constants@18.0.8:
resolution: {integrity: sha512-Tetphsx6RVImCTZeBAclRQMy0WOODY3y6qrUoc88YGUBVm8fAKkErCSWxLTCc6nFcJxdoOMYi62LgNIUFjZCLA==}
expo-constants@18.0.9:
resolution: {integrity: sha512-sqoXHAOGDcr+M9NlXzj1tGoZyd3zxYDy215W6E0Z0n8fgBaqce9FAYQE2bu5X4G629AYig5go7U6sQz7Pjcm8A==}
peerDependencies:
expo: '*'
react-native: '*'
@@ -4796,24 +4804,24 @@ packages:
peerDependencies:
expo: '*'
expo-modules-autolinking@3.0.10:
resolution: {integrity: sha512-6pwaz9H7aK/iYraHbX7zjg8QFTUuMfGEs8Vyc6bAoBd8Rovtb91WX955Kq5sazwNrQjs3WePwQ23LEAmls3u5g==}
expo-modules-autolinking@3.0.11:
resolution: {integrity: sha512-Sz1ptcSZ4mvWJ7Rf1aB6Pe1fuEeIkACPILg2tmXDo3wwLTxPqugitMOePjbBZyvacBDirtDZlMb2A6LQDPVFOg==}
hasBin: true
expo-modules-core@3.0.15:
resolution: {integrity: sha512-vGI7osd0/IjprldD08k4bckWSu7ID4HhZNP68l/UtilONQ8XZig8mWJd/Fm7i7KGvE3HyuF+HOXE9l671no42Q==}
expo-modules-core@3.0.16:
resolution: {integrity: sha512-rCxzJiTdeSdqLVmDYYnogxqHS3NB65YTd76tAtSACujN2TQco08/toxCCov+9uULq1NGPxDJnfTkrtGaGWfatQ==}
peerDependencies:
react: '*'
react-native: '*'
expo-router@6.0.5:
resolution: {integrity: sha512-FK5y/55ppv54WjW7W7X4g5J3r+hiMKHukRYjyS6KI4i92qOWtVF42yssD/Ty90EpjKuZ8N1F72FJGdx9A1UQNA==}
expo-router@6.0.6:
resolution: {integrity: sha512-uSuKQanivBI9RtwmAznLI7It5aPwQLVL2tVBPAOJ70tv6BzP62SpVCf0I8o0j9PmEzORPRLrU2LbQOL962yBHg==}
peerDependencies:
'@expo/metro-runtime': ^6.1.2
'@react-navigation/drawer': ^7.5.0
'@testing-library/react-native': '>= 12.0.0'
expo: '*'
expo-constants: ^18.0.8
expo-constants: ^18.0.9
expo-linking: ^8.0.8
react: '*'
react-dom: '*'
@@ -4877,8 +4885,8 @@ packages:
peerDependencies:
expo: '*'
expo@54.0.7:
resolution: {integrity: sha512-DftN6nMdpHYUCw5Xnh7+h7wnusjtly4JzQknvuD7MzIvqoyJL9uffQyMQrmZkXrUbgm+cKBm47vtooIz4qj0Qg==}
expo@54.0.8:
resolution: {integrity: sha512-H4nUVvAczd9ZPWrAR3oXxEr/EkLfPxXg5gBvFgZI4WnGNthehqEYB37urXgj9fvgSBxNaRUkySL4uwr9VB2J8Q==}
hasBin: true
peerDependencies:
'@expo/dom-webview': '*'
@@ -9908,7 +9916,7 @@ snapshots:
'@eslint/core': 0.15.2
levn: 0.4.1
'@expo/cli@54.0.5(expo-router@6.0.5)(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))':
'@expo/cli@54.0.6(expo-router@6.0.6)(expo@54.0.8)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))':
dependencies:
'@0no-co/graphql.web': 1.2.0
'@expo/code-signing-certificates': 0.0.5
@@ -9918,12 +9926,13 @@ snapshots:
'@expo/env': 2.0.7
'@expo/image-utils': 0.8.7
'@expo/json-file': 10.0.7
'@expo/mcp-tunnel': 0.0.7
'@expo/metro': 0.1.1
'@expo/metro-config': 54.0.3(expo@54.0.7)
'@expo/metro-config': 54.0.3(expo@54.0.8)
'@expo/osascript': 2.3.7
'@expo/package-manager': 1.9.7
'@expo/package-manager': 1.9.8
'@expo/plist': 0.4.7
'@expo/prebuild-config': 54.0.3(expo@54.0.7)
'@expo/prebuild-config': 54.0.3(expo@54.0.8)
'@expo/schema-utils': 0.1.7
'@expo/server': 0.7.4
'@expo/spawn-async': 1.7.2
@@ -9943,7 +9952,7 @@ snapshots:
connect: 3.7.0
debug: 4.4.1
env-editor: 0.4.2
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.5)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo: 54.0.8(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.6)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
freeport-async: 2.0.0
getenv: 2.0.0
glob: 10.4.5
@@ -9975,9 +9984,10 @@ snapshots:
wrap-ansi: 7.0.0
ws: 8.18.3
optionalDependencies:
expo-router: 6.0.5(750b4158402c81723fe42a215de7942a)
expo-router: 6.0.6(be6ce5c43438e2db372447bf3c6a78fa)
react-native: 0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0)
transitivePeerDependencies:
- '@modelcontextprotocol/sdk'
- bufferutil
- graphql
- supports-color
@@ -10091,7 +10101,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@expo/fingerprint@0.15.0':
'@expo/fingerprint@0.15.1':
dependencies:
'@expo/spawn-async': 1.7.2
arg: 5.0.2
@@ -10130,7 +10140,16 @@ snapshots:
'@babel/code-frame': 7.10.4
json5: 2.2.3
'@expo/metro-config@54.0.3(expo@54.0.7)':
'@expo/mcp-tunnel@0.0.7':
dependencies:
ws: 8.18.3
zod: 3.25.76
zod-to-json-schema: 3.24.6(zod@3.25.76)
transitivePeerDependencies:
- bufferutil
- utf-8-validate
'@expo/metro-config@54.0.3(expo@54.0.8)':
dependencies:
'@babel/code-frame': 7.27.1
'@babel/core': 7.28.3
@@ -10154,16 +10173,16 @@ snapshots:
postcss: 8.4.49
resolve-from: 5.0.0
optionalDependencies:
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.5)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo: 54.0.8(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.6)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
transitivePeerDependencies:
- bufferutil
- supports-color
- utf-8-validate
'@expo/metro-runtime@6.1.1(expo@54.0.7)(react-dom@19.1.0(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)':
'@expo/metro-runtime@6.1.1(expo@54.0.8)(react-dom@19.1.0(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)':
dependencies:
anser: 1.4.10
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.5)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo: 54.0.8(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.6)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
pretty-format: 29.7.0
react: 19.1.0
react-native: 0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0)
@@ -10196,7 +10215,7 @@ snapshots:
'@expo/spawn-async': 1.7.2
exec-async: 2.2.0
'@expo/package-manager@1.9.7':
'@expo/package-manager@1.9.8':
dependencies:
'@expo/json-file': 10.0.7
'@expo/spawn-async': 1.7.2
@@ -10217,7 +10236,7 @@ snapshots:
base64-js: 1.5.1
xmlbuilder: 15.1.1
'@expo/prebuild-config@54.0.3(expo@54.0.7)':
'@expo/prebuild-config@54.0.3(expo@54.0.8)':
dependencies:
'@expo/config': 12.0.9
'@expo/config-plugins': 54.0.1
@@ -10226,7 +10245,7 @@ snapshots:
'@expo/json-file': 10.0.7
'@react-native/normalize-colors': 0.81.4
debug: 4.4.1
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.5)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo: 54.0.8(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.6)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
resolve-from: 5.0.0
semver: 7.7.2
xml2js: 0.6.0
@@ -10250,9 +10269,9 @@ snapshots:
'@expo/sudo-prompt@9.3.2': {}
'@expo/vector-icons@15.0.2(expo-font@14.0.8(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)':
'@expo/vector-icons@15.0.2(expo-font@14.0.8(expo@54.0.8)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)':
dependencies:
expo-font: 14.0.8(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo-font: 14.0.8(expo@54.0.8)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
react: 19.1.0
react-native: 0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0)
@@ -12800,7 +12819,7 @@ snapshots:
'@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.28.3)
'@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.28.3)
babel-preset-expo@54.0.1(@babel/core@7.28.3)(@babel/runtime@7.28.3)(expo@54.0.7)(react-refresh@0.14.2):
babel-preset-expo@54.0.1(@babel/core@7.28.3)(@babel/runtime@7.28.3)(expo@54.0.8)(react-refresh@0.14.2):
dependencies:
'@babel/helper-module-imports': 7.27.1
'@babel/plugin-proposal-decorators': 7.28.0(@babel/core@7.28.3)
@@ -12827,7 +12846,7 @@ snapshots:
resolve-from: 5.0.0
optionalDependencies:
'@babel/runtime': 7.28.3
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.5)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo: 54.0.8(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.6)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
transitivePeerDependencies:
- '@babel/core'
- supports-color
@@ -14161,93 +14180,93 @@ snapshots:
jest-mock: 30.0.5
jest-util: 30.0.5
expo-asset@12.0.8(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0):
expo-asset@12.0.8(expo@54.0.8)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0):
dependencies:
'@expo/image-utils': 0.8.7
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.5)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo-constants: 18.0.8(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))
expo: 54.0.8(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.6)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo-constants: 18.0.9(expo@54.0.8)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))
react: 19.1.0
react-native: 0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0)
transitivePeerDependencies:
- supports-color
expo-clipboard@8.0.7(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0):
expo-clipboard@8.0.7(expo@54.0.8)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0):
dependencies:
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.5)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo: 54.0.8(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.6)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
react: 19.1.0
react-native: 0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0)
expo-constants@18.0.8(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0)):
expo-constants@18.0.9(expo@54.0.8)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0)):
dependencies:
'@expo/config': 12.0.8
'@expo/config': 12.0.9
'@expo/env': 2.0.7
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.5)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo: 54.0.8(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.6)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
react-native: 0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0)
transitivePeerDependencies:
- supports-color
expo-crypto@15.0.7(expo@54.0.7):
expo-crypto@15.0.7(expo@54.0.8):
dependencies:
base64-js: 1.5.1
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.5)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo: 54.0.8(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.6)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo-dev-client@6.0.12(expo@54.0.7):
expo-dev-client@6.0.12(expo@54.0.8):
dependencies:
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.5)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo-dev-launcher: 6.0.11(expo@54.0.7)
expo-dev-menu: 7.0.11(expo@54.0.7)
expo-dev-menu-interface: 2.0.0(expo@54.0.7)
expo-manifests: 1.0.8(expo@54.0.7)
expo-updates-interface: 2.0.0(expo@54.0.7)
expo: 54.0.8(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.6)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo-dev-launcher: 6.0.11(expo@54.0.8)
expo-dev-menu: 7.0.11(expo@54.0.8)
expo-dev-menu-interface: 2.0.0(expo@54.0.8)
expo-manifests: 1.0.8(expo@54.0.8)
expo-updates-interface: 2.0.0(expo@54.0.8)
transitivePeerDependencies:
- supports-color
expo-dev-launcher@6.0.11(expo@54.0.7):
expo-dev-launcher@6.0.11(expo@54.0.8):
dependencies:
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.5)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo-dev-menu: 7.0.11(expo@54.0.7)
expo-manifests: 1.0.8(expo@54.0.7)
expo: 54.0.8(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.6)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo-dev-menu: 7.0.11(expo@54.0.8)
expo-manifests: 1.0.8(expo@54.0.8)
transitivePeerDependencies:
- supports-color
expo-dev-menu-interface@2.0.0(expo@54.0.7):
expo-dev-menu-interface@2.0.0(expo@54.0.8):
dependencies:
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.5)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo: 54.0.8(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.6)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo-dev-menu@7.0.11(expo@54.0.7):
expo-dev-menu@7.0.11(expo@54.0.8):
dependencies:
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.5)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo-dev-menu-interface: 2.0.0(expo@54.0.7)
expo: 54.0.8(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.6)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo-dev-menu-interface: 2.0.0(expo@54.0.8)
expo-document-picker@14.0.7(expo@54.0.7):
expo-document-picker@14.0.7(expo@54.0.8):
dependencies:
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.5)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo: 54.0.8(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.6)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo-file-system@19.0.14(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0)):
expo-file-system@19.0.14(expo@54.0.8)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0)):
dependencies:
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.5)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo: 54.0.8(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.6)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
react-native: 0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0)
expo-font@14.0.8(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0):
expo-font@14.0.8(expo@54.0.8)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0):
dependencies:
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.5)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo: 54.0.8(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.6)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
fontfaceobserver: 2.3.0
react: 19.1.0
react-native: 0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0)
expo-glass-effect@0.1.3(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0):
expo-glass-effect@0.1.3(expo@54.0.8)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0):
dependencies:
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.5)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo: 54.0.8(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.6)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
react: 19.1.0
react-native: 0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0)
expo-haptics@15.0.7(expo@54.0.7):
expo-haptics@15.0.7(expo@54.0.8):
dependencies:
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.5)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo: 54.0.8(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.6)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo-image@3.0.8(expo@54.0.7)(react-native-web@0.21.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0):
expo-image@3.0.8(expo@54.0.8)(react-native-web@0.21.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0):
dependencies:
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.5)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo: 54.0.8(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.6)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
react: 19.1.0
react-native: 0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0)
optionalDependencies:
@@ -14255,14 +14274,14 @@ snapshots:
expo-json-utils@0.15.0: {}
expo-keep-awake@15.0.7(expo@54.0.7)(react@19.1.0):
expo-keep-awake@15.0.7(expo@54.0.8)(react@19.1.0):
dependencies:
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.5)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo: 54.0.8(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.6)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
react: 19.1.0
expo-linking@8.0.8(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0):
expo-linking@8.0.8(expo@54.0.8)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0):
dependencies:
expo-constants: 18.0.8(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))
expo-constants: 18.0.9(expo@54.0.8)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))
invariant: 2.2.4
react: 19.1.0
react-native: 0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0)
@@ -14270,15 +14289,15 @@ snapshots:
- expo
- supports-color
expo-manifests@1.0.8(expo@54.0.7):
expo-manifests@1.0.8(expo@54.0.8):
dependencies:
'@expo/config': 12.0.8
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.5)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo: 54.0.8(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.6)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo-json-utils: 0.15.0
transitivePeerDependencies:
- supports-color
expo-modules-autolinking@3.0.10:
expo-modules-autolinking@3.0.11:
dependencies:
'@expo/spawn-async': 1.7.2
chalk: 4.1.2
@@ -14287,15 +14306,15 @@ snapshots:
require-from-string: 2.0.2
resolve-from: 5.0.0
expo-modules-core@3.0.15(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0):
expo-modules-core@3.0.16(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0):
dependencies:
invariant: 2.2.4
react: 19.1.0
react-native: 0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0)
expo-router@6.0.5(750b4158402c81723fe42a215de7942a):
expo-router@6.0.6(be6ce5c43438e2db372447bf3c6a78fa):
dependencies:
'@expo/metro-runtime': 6.1.1(expo@54.0.7)(react-dom@19.1.0(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
'@expo/metro-runtime': 6.1.1(expo@54.0.8)(react-dom@19.1.0(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
'@expo/schema-utils': 0.1.7
'@expo/server': 0.7.4
'@radix-ui/react-slot': 1.2.0(@types/react@19.1.12)(react@19.1.0)
@@ -14306,9 +14325,9 @@ snapshots:
client-only: 0.0.1
debug: 4.4.1
escape-string-regexp: 4.0.0
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.5)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo-constants: 18.0.8(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))
expo-linking: 8.0.8(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo: 54.0.8(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.6)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo-constants: 18.0.9(expo@54.0.8)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))
expo-linking: 8.0.8(expo@54.0.8)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
fast-deep-equal: 3.1.3
invariant: 2.2.4
nanoid: 3.3.11
@@ -14336,14 +14355,14 @@ snapshots:
- '@types/react-dom'
- supports-color
expo-secure-store@15.0.7(expo@54.0.7):
expo-secure-store@15.0.7(expo@54.0.8):
dependencies:
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.5)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo: 54.0.8(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.6)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo-splash-screen@31.0.10(expo@54.0.7):
expo-splash-screen@31.0.10(expo@54.0.8):
dependencies:
'@expo/prebuild-config': 54.0.3(expo@54.0.7)
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.5)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
'@expo/prebuild-config': 54.0.3(expo@54.0.8)
expo: 54.0.8(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.6)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
transitivePeerDependencies:
- supports-color
@@ -14353,56 +14372,57 @@ snapshots:
react-native: 0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0)
react-native-is-edge-to-edge: 1.2.1(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo-symbols@1.0.7(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0)):
expo-symbols@1.0.7(expo@54.0.8)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0)):
dependencies:
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.5)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo: 54.0.8(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.6)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
react-native: 0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0)
sf-symbols-typescript: 2.1.0
expo-system-ui@6.0.7(expo@54.0.7)(react-native-web@0.21.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0)):
expo-system-ui@6.0.7(expo@54.0.8)(react-native-web@0.21.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0)):
dependencies:
'@react-native/normalize-colors': 0.81.4
debug: 4.4.1
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.5)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo: 54.0.8(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.6)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
react-native: 0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0)
optionalDependencies:
react-native-web: 0.21.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
transitivePeerDependencies:
- supports-color
expo-updates-interface@2.0.0(expo@54.0.7):
expo-updates-interface@2.0.0(expo@54.0.8):
dependencies:
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.5)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo: 54.0.8(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.6)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo@54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.5)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0):
expo@54.0.8(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.6)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0):
dependencies:
'@babel/runtime': 7.28.3
'@expo/cli': 54.0.5(expo-router@6.0.5)(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))
'@expo/cli': 54.0.6(expo-router@6.0.6)(expo@54.0.8)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))
'@expo/config': 12.0.9
'@expo/config-plugins': 54.0.1
'@expo/devtools': 0.1.7(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
'@expo/fingerprint': 0.15.0
'@expo/fingerprint': 0.15.1
'@expo/metro': 0.1.1
'@expo/metro-config': 54.0.3(expo@54.0.7)
'@expo/vector-icons': 15.0.2(expo-font@14.0.8(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
'@expo/metro-config': 54.0.3(expo@54.0.8)
'@expo/vector-icons': 15.0.2(expo-font@14.0.8(expo@54.0.8)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
'@ungap/structured-clone': 1.3.0
babel-preset-expo: 54.0.1(@babel/core@7.28.3)(@babel/runtime@7.28.3)(expo@54.0.7)(react-refresh@0.14.2)
expo-asset: 12.0.8(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo-constants: 18.0.8(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))
expo-file-system: 19.0.14(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))
expo-font: 14.0.8(expo@54.0.7)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo-keep-awake: 15.0.7(expo@54.0.7)(react@19.1.0)
expo-modules-autolinking: 3.0.10
expo-modules-core: 3.0.15(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
babel-preset-expo: 54.0.1(@babel/core@7.28.3)(@babel/runtime@7.28.3)(expo@54.0.8)(react-refresh@0.14.2)
expo-asset: 12.0.8(expo@54.0.8)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo-constants: 18.0.9(expo@54.0.8)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))
expo-file-system: 19.0.14(expo@54.0.8)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))
expo-font: 14.0.8(expo@54.0.8)(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
expo-keep-awake: 15.0.7(expo@54.0.8)(react@19.1.0)
expo-modules-autolinking: 3.0.11
expo-modules-core: 3.0.16(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
pretty-format: 29.7.0
react: 19.1.0
react-native: 0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0)
react-refresh: 0.14.2
whatwg-url-without-unicode: 8.0.0-3
optionalDependencies:
'@expo/metro-runtime': 6.1.1(expo@54.0.7)(react-dom@19.1.0(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
'@expo/metro-runtime': 6.1.1(expo@54.0.8)(react-dom@19.1.0(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.3)(@react-native-community/cli@20.0.2(typescript@5.9.2))(@types/react@19.1.12)(react@19.1.0))(react@19.1.0)
transitivePeerDependencies:
- '@babel/core'
- '@modelcontextprotocol/sdk'
- bufferutil
- expo-router
- graphql