This commit is contained in:
EthanShoeDev
2025-09-15 23:03:59 -04:00
parent 1bde6fa8a2
commit db5eb2ff8d
15 changed files with 415 additions and 315 deletions

View File

@@ -8,7 +8,8 @@
"**/node_modules/**",
"**/android/**",
"**/generated/**",
"**/rust/target/**"
"**/rust/target/**",
"**/mobile/ios/**"
],
"threshold": 0,
"minTokens": 50,

View File

@@ -1,5 +1,5 @@
import 'tsx/cjs';
import { type ExpoConfig } from 'expo/config';
import 'tsx/cjs';
import packageJson from './package.json';
const config: ExpoConfig = {

View File

@@ -48,7 +48,7 @@
"expo-haptics": "~15.0.7",
"expo-image": "~3.0.8",
"expo-linking": "~8.0.8",
"expo-router": "6.0.4",
"expo-router": "6.0.5",
"expo-secure-store": "~15.0.7",
"expo-splash-screen": "~31.0.10",
"expo-status-bar": "~3.0.8",
@@ -75,6 +75,7 @@
"jiti": "^2.5.1",
"npm-run-all": "^4.1.5",
"prettier": "^3.6.2",
"prettier-plugin-organize-imports": "^4.2.0",
"tsx": "^4.20.5",
"typescript": "~5.9.2"
},

View File

@@ -1,14 +1,14 @@
import epicConfig from '@epic-web/config/prettier';
// Sometimes this plugin can remove imports that are being edited.
// As a workaround we will only use this in the cli. (pnpm run fmt)
// const sortImports = process.env.SORT_IMPORTS === "true";
const sortImports = process.env.SORT_IMPORTS === 'true-never';
/** @type {import("prettier").Options} */
export default {
...epicConfig,
semi: true,
plugins: [
// ...(sortImports ? ["prettier-plugin-organize-imports"] : []),
...(sortImports ? ['prettier-plugin-organize-imports'] : []),
...(epicConfig.plugins || []),
],
};

View File

@@ -3,7 +3,7 @@
*/
import * as fsp from 'fs/promises';
import * as path from 'path';
import { command, run, option, oneOf, boolean, flag } from 'cmd-ts';
import { boolean, command, flag, oneOf, option, run } from 'cmd-ts';
import { z } from 'zod';
import packageJson from '../package.json' with { type: 'json' };
import { cmd } from './script-lib';

View File

@@ -1,4 +1,4 @@
import { NativeTabs, Icon, Label } from 'expo-router/unstable-native-tabs';
import { Icon, Label, NativeTabs } from 'expo-router/unstable-native-tabs';
import React from 'react';
import { useTheme } from '@/lib/theme';
@@ -15,10 +15,10 @@ export default function TabsLayout() {
// android
backBehavior="initialRoute"
indicatorColor={theme.colors.primary}
labelVisibilityMode="labeled"
// labelVisibilityMode="labeled"
// rippleColor={theme.colors.transparent}
// ios
blurEffect="systemDefault"
// blurEffect="systemChromeMaterial"
>
<NativeTabs.Trigger name="index">
<Label selectedStyle={{ color: theme.colors.textPrimary }}>Hosts</Label>

View File

@@ -3,24 +3,21 @@ import { useStore } from '@tanstack/react-form';
import { useQuery } from '@tanstack/react-query';
import React from 'react';
import {
Modal,
Pressable,
ScrollView,
StyleSheet,
Text,
View,
Modal,
} from 'react-native';
import {
SafeAreaView,
useSafeAreaInsets,
} from 'react-native-safe-area-context';
import { SafeAreaView } from 'react-native-safe-area-context';
import { useAppForm, useFieldContext } from '@/components/form-components';
import { KeyList } from '@/components/key-manager/KeyList';
import { useSshConnMutation } from '@/lib/query-fns';
import {
type ConnectionDetails,
connectionDetailsSchema,
secretsManager,
type ConnectionDetails,
} from '@/lib/secrets-manager';
import { useTheme, type AppTheme } from '@/lib/theme';
@@ -41,7 +38,7 @@ const defaultValues: ConnectionDetails = {
function Host() {
const theme = useTheme();
const styles = React.useMemo(() => makeStyles(theme), [theme]);
const insets = useSafeAreaInsets();
// const insets = useSafeAreaInsets();
const sshConnMutation = useSshConnMutation();
const connectionForm = useAppForm({
// https://tanstack.com/form/latest/docs/framework/react/guides/async-initial-values
@@ -65,10 +62,7 @@ function Host() {
return (
<SafeAreaView style={{ flex: 1, backgroundColor: theme.colors.background }}>
<ScrollView
contentContainerStyle={[
styles.scrollContent,
{ paddingBottom: Math.max(32, insets.bottom + 24) },
]}
contentContainerStyle={[styles.scrollContent]}
keyboardShouldPersistTaps="handled"
style={{ backgroundColor: theme.colors.background }}
>

View File

@@ -1,6 +1,6 @@
import { Link } from 'expo-router';
import React from 'react';
import { Pressable, View, Text, StyleSheet } from 'react-native';
import { Pressable, StyleSheet, Text, View } from 'react-native';
import { useTheme, useThemeControls, type AppTheme } from '@/lib/theme';
export default function Tab() {

View File

@@ -1,8 +1,7 @@
import { Ionicons } from '@expo/vector-icons';
import {
RnRussh,
type RnRussh,
type SshConnection,
type SshShellSession,
} from '@fressh/react-native-uniffi-russh';
import { FlashList } from '@shopify/flash-list';
import { useQuery, useQueryClient } from '@tanstack/react-query';
@@ -11,7 +10,14 @@ import { Link, Stack, useRouter } from 'expo-router';
import React from 'react';
import { Modal, Pressable, StyleSheet, Text, View } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { listSshShellsQueryOptions } from '@/lib/query-fns';
import { IconSegmentedControl } from '@/components/icon-segmented-control';
import { preferences } from '@/lib/preferences';
import {
closeSshShellAndInvalidateQuery,
disconnectSshConnectionAndInvalidateQuery,
listSshShellsQueryOptions,
type ShellWithConnection,
} from '@/lib/query-fns';
import { useTheme, type AppTheme } from '@/lib/theme';
export default function TabsShellList() {
@@ -23,28 +29,24 @@ export default function TabsShellList() {
);
}
type ShellWithConnection = SshShellSession & { connection: SshConnection };
function ShellContent() {
const [viewIndex, setViewIndex] = React.useState(0);
const connectionsWithShells = useQuery(listSshShellsQueryOptions);
const connectionsQuery = useQuery(listSshShellsQueryOptions);
console.log('DEBUG connectionsQuery.data', !!connectionsQuery.data);
return (
<View style={{ flex: 1 }}>
<Stack.Screen
options={{
headerRight: () => (
<TopBarToggle viewIndex={viewIndex} onChange={setViewIndex} />
),
headerRight: () => <TopBarToggle />,
}}
/>
{connectionsWithShells.isLoading || !connectionsWithShells.data ? (
{!connectionsQuery.data ? (
<LoadingState />
) : connectionsQuery.data.length === 0 ? (
<EmptyState />
) : (
<LoadedState
connectionsWithShells={connectionsWithShells.data}
viewIndex={viewIndex}
/>
<LoadedState connections={connectionsQuery.data} />
)}
</View>
);
@@ -60,171 +62,171 @@ function LoadingState() {
);
}
type ActionTarget =
| {
shell: ShellWithConnection;
}
| {
connection: SshConnection;
};
function LoadedState({
connections,
}: {
connections: ReturnType<typeof RnRussh.listSshConnectionsWithShells>;
}) {
const [actionTarget, setActionTarget] = React.useState<null | ActionTarget>(
null,
);
const queryClient = useQueryClient();
const [shellListViewMode] =
preferences.shellListViewMode.useShellListViewModePref();
return (
<View style={{ flex: 1 }}>
{shellListViewMode === 'flat' ? (
<FlatView
connectionsWithShells={connections}
setActionTarget={setActionTarget}
/>
) : (
<GroupedView
connectionsWithShells={connections}
setActionTarget={setActionTarget}
/>
)}
<ActionsSheet
target={actionTarget}
onClose={() => setActionTarget(null)}
onCloseShell={() => {
if (!actionTarget) return;
if (!('shell' in actionTarget)) return;
void closeSshShellAndInvalidateQuery({
channelId: actionTarget.shell.channelId,
connectionId: actionTarget.shell.connectionId,
queryClient: queryClient,
});
}}
onDisconnect={() => {
if (!actionTarget) return;
const connectionId =
'connection' in actionTarget
? actionTarget.connection.connectionId
: actionTarget.shell.connectionId;
void disconnectSshConnectionAndInvalidateQuery({
connectionId: connectionId,
queryClient: queryClient,
});
}}
/>
</View>
);
}
function FlatView({
connectionsWithShells,
viewIndex,
setActionTarget,
}: {
connectionsWithShells: ReturnType<
typeof RnRussh.listSshConnectionsWithShells
>;
viewIndex: number;
setActionTarget: (target: ActionTarget) => void;
}) {
const theme = useTheme();
const styles = React.useMemo(() => makeStyles(theme), [theme]);
const [actionTarget, setActionTarget] = React.useState<null | {
connectionId: string;
channelId: number;
}>(null);
const queryClient = useQueryClient();
const flatShells = React.useMemo(() => {
return connectionsWithShells.reduce<ShellWithConnection[]>((acc, curr) => {
acc.push(...curr.shells.map((shell) => ({ ...shell, connection: curr })));
return acc;
}, []);
}, [connectionsWithShells]);
const [expanded, setExpanded] = React.useState<Record<string, boolean>>({});
React.useEffect(() => {
const init: Record<string, boolean> = {};
for (const c of connectionsWithShells) init[c.connectionId] = true;
setExpanded(init);
}, [connectionsWithShells]);
async function handleCloseShell(connId: string, channelId: number) {
try {
const shell = RnRussh.getSshShell(connId, channelId);
await (shell as any)?.close?.();
} catch {}
await queryClient.invalidateQueries({
queryKey: listSshShellsQueryOptions.queryKey,
});
setActionTarget(null);
}
async function handleDisconnect(connId: string) {
try {
const conn = RnRussh.getSshConnection(connId);
await conn?.disconnect();
} catch {}
await queryClient.invalidateQueries({
queryKey: listSshShellsQueryOptions.queryKey,
});
setActionTarget(null);
}
if (viewIndex === 0) {
return (
<View style={{ flex: 1 }}>
{flatShells.length === 0 ? (
<EmptyState />
) : (
<FlashList
data={flatShells}
keyExtractor={(item) => `${item.connectionId}:${item.channelId}`}
renderItem={({ item }) => (
<ShellCard
shell={item}
onLongPress={() =>
setActionTarget({
connectionId: item.connectionId as string,
channelId: item.channelId as number,
})
}
/>
)}
ItemSeparatorComponent={() => <View style={{ height: 12 }} />}
contentContainerStyle={{
paddingVertical: 16,
paddingHorizontal: 16,
}}
style={{ flex: 1 }}
/>
)}
<ActionsSheet
target={actionTarget}
onClose={() => setActionTarget(null)}
onCloseShell={() =>
actionTarget &&
handleCloseShell(actionTarget.connectionId, actionTarget.channelId)
}
onDisconnect={() =>
actionTarget && handleDisconnect(actionTarget.connectionId)
}
/>
</View>
);
}
return (
<View style={{ flex: 1 }}>
{connectionsWithShells.length === 0 ? (
<EmptyState />
) : (
<FlashList
data={connectionsWithShells}
// estimatedItemSize={80}
keyExtractor={(item) => item.connectionId}
renderItem={({ item }) => (
<View style={styles.groupContainer}>
<Pressable
style={styles.groupHeader}
onPress={() =>
setExpanded((prev) => ({
...prev,
[item.connectionId]: !prev[item.connectionId],
}))
}
>
<View>
<Text style={styles.groupTitle}>
{item.connectionDetails.username}@
{item.connectionDetails.host}
</Text>
<Text style={styles.groupSubtitle}>
Port {item.connectionDetails.port} {item.shells.length}{' '}
shell{item.shells.length === 1 ? '' : 's'}
</Text>
</View>
<Text style={styles.groupChevron}>
{expanded[item.connectionId] ? '▾' : '▸'}
</Text>
</Pressable>
{expanded[item.connectionId] && (
<View style={{ gap: 12 }}>
{item.shells.map((sh) => (
<ShellCard
key={`${sh.connectionId}:${sh.channelId}`}
shell={{ ...sh, connection: item }}
onLongPress={() =>
setActionTarget({
connectionId: sh.connectionId as string,
channelId: sh.channelId as number,
})
}
/>
))}
</View>
)}
</View>
)}
ItemSeparatorComponent={() => <View style={{ height: 16 }} />}
contentContainerStyle={{ paddingVertical: 16, paddingHorizontal: 16 }}
style={{ flex: 1 }}
<FlashList
data={flatShells}
keyExtractor={(item) => `${item.connectionId}:${item.channelId}`}
renderItem={({ item }) => (
<ShellCard
shell={item}
onLongPress={() =>
setActionTarget({
shell: item,
})
}
/>
)}
<ActionsSheet
target={actionTarget}
onClose={() => setActionTarget(null)}
onCloseShell={() =>
actionTarget &&
handleCloseShell(actionTarget.connectionId, actionTarget.channelId)
}
onDisconnect={() =>
actionTarget && handleDisconnect(actionTarget.connectionId)
}
/>
</View>
ItemSeparatorComponent={() => <View style={{ height: 12 }} />}
contentContainerStyle={{
paddingVertical: 16,
paddingHorizontal: 16,
}}
style={{ flex: 1 }}
/>
);
}
function GroupedView({
connectionsWithShells,
setActionTarget,
}: {
connectionsWithShells: ReturnType<
typeof RnRussh.listSshConnectionsWithShells
>;
setActionTarget: (target: ActionTarget) => void;
}) {
const theme = useTheme();
const styles = React.useMemo(() => makeStyles(theme), [theme]);
const [expanded, setExpanded] = React.useState<Record<string, boolean>>({});
return (
<FlashList
data={connectionsWithShells}
// estimatedItemSize={80}
keyExtractor={(item) => item.connectionId}
renderItem={({ item }) => (
<View style={styles.groupContainer}>
<Pressable
style={styles.groupHeader}
onPress={() =>
setExpanded((prev) => ({
...prev,
[item.connectionId]: !prev[item.connectionId],
}))
}
>
<View>
<Text style={styles.groupTitle}>
{item.connectionDetails.username}@{item.connectionDetails.host}
</Text>
<Text style={styles.groupSubtitle}>
Port {item.connectionDetails.port} {item.shells.length} shell
{item.shells.length === 1 ? '' : 's'}
</Text>
</View>
<Text style={styles.groupChevron}>
{expanded[item.connectionId] ? '▾' : '▸'}
</Text>
</Pressable>
{expanded[item.connectionId] && (
<View style={{ gap: 12 }}>
{item.shells.map((sh) => {
const shellWithConnection = { ...sh, connection: item };
return (
<ShellCard
key={`${sh.connectionId}:${sh.channelId}`}
shell={shellWithConnection}
onLongPress={() =>
setActionTarget({
shell: shellWithConnection,
})
}
/>
);
})}
</View>
)}
</View>
)}
ItemSeparatorComponent={() => <View style={{ height: 16 }} />}
contentContainerStyle={{ paddingVertical: 16, paddingHorizontal: 16 }}
style={{ flex: 1 }}
/>
);
}
@@ -239,6 +241,7 @@ function EmptyState() {
<Link href="/" style={styles.link}>
Go to Hosts
</Link>
<TopBarToggle />
</View>
);
}
@@ -291,7 +294,7 @@ function ActionsSheet({
onCloseShell,
onDisconnect,
}: {
target: null | { connectionId: string; channelId: number };
target: null | ActionTarget;
onClose: () => void;
onCloseShell: () => void;
onDisconnect: () => void;
@@ -329,6 +332,37 @@ function ActionsSheet({
);
}
function TopBarToggle() {
const theme = useTheme();
const iconStyle = (isActive: boolean) => ({
color: isActive ? theme.colors.textPrimary : theme.colors.muted,
});
const [shellListViewMode, setShellListViewMode] =
preferences.shellListViewMode.useShellListViewModePref();
return (
<IconSegmentedControl
values={[
{
child: ({ isActive }) => (
<Ionicons name="list" size={18} style={iconStyle(isActive)} />
),
accessibilityLabel: 'Flat list',
value: 'flat',
},
{
child: ({ isActive }) => (
<Ionicons name="git-branch" size={18} style={iconStyle(isActive)} />
),
accessibilityLabel: 'Grouped by connection',
value: 'grouped',
},
]}
value={shellListViewMode}
onChange={setShellListViewMode}
/>
);
}
function makeStyles(theme: AppTheme) {
return StyleSheet.create({
centerContent: {
@@ -450,73 +484,3 @@ function makeStyles(theme: AppTheme) {
},
});
}
function TopBarToggle({
viewIndex,
onChange,
}: {
viewIndex: number;
onChange: (index: number) => void;
}) {
const theme = useTheme();
const styles = React.useMemo(
() =>
StyleSheet.create({
container: {
paddingHorizontal: 16,
paddingTop: 12,
paddingBottom: 8,
alignItems: 'flex-end',
},
toggle: {
flexDirection: 'row',
backgroundColor: theme.colors.surface,
borderWidth: 1,
borderColor: theme.colors.border,
borderRadius: 10,
overflow: 'hidden',
},
segment: {
paddingHorizontal: 10,
paddingVertical: 6,
alignItems: 'center',
justifyContent: 'center',
},
active: {
backgroundColor: theme.colors.inputBackground,
},
iconActive: { color: theme.colors.textPrimary },
iconInactive: { color: theme.colors.muted },
}),
[theme],
);
return (
<View style={styles.container}>
<View style={styles.toggle}>
<Pressable
accessibilityLabel="Flat list"
onPress={() => onChange(0)}
style={[styles.segment, viewIndex === 0 && styles.active]}
>
<Ionicons
name="list"
size={18}
style={viewIndex === 0 ? styles.iconActive : styles.iconInactive}
/>
</Pressable>
<Pressable
accessibilityLabel="Grouped by connection"
onPress={() => onChange(1)}
style={[styles.segment, viewIndex === 1 && styles.active]}
>
<Ionicons
name="git-branch"
size={18}
style={viewIndex === 1 ? styles.iconActive : styles.iconInactive}
/>
</Pressable>
</View>
</View>
);
}

View File

@@ -0,0 +1,63 @@
import React from 'react';
import { Pressable, View } from 'react-native';
import { useTheme } from '@/lib/theme';
export function IconSegmentedControl<T extends string>(props: {
values: {
child: (props: { isActive: boolean }) => React.ReactNode;
accessibilityLabel: string;
value: T;
}[];
value: T;
onChange: (value: T) => void;
}) {
const theme = useTheme();
return (
<View
style={{
flexDirection: 'row',
backgroundColor: theme.colors.surface,
// borderWidth: 1,
// borderColor: theme.colors.border,
borderRadius: 10,
overflow: 'hidden',
}}
>
{props.values.map((item) => (
<Pressable
key={item.value}
accessibilityLabel={item.accessibilityLabel}
onPress={() => {
console.log('DEBUG onPress', {
currentValue: props.value,
itemValue: item.value,
});
if (props.values.length === 2) {
const newValue = props.values.find(
(v) => v.value !== props.value,
)!.value;
console.log('DEBUG newValue', newValue);
props.onChange(newValue);
} else {
props.onChange(item.value);
}
}}
style={[
{
paddingHorizontal: 10,
paddingVertical: 6,
alignItems: 'center',
justifyContent: 'center',
},
props.value === item.value && {
backgroundColor: theme.colors.inputBackground,
},
]}
>
{item.child({ isActive: props.value === item.value })}
</Pressable>
))}
</View>
);
}

View File

@@ -1,12 +1,12 @@
import { useMutation, useQuery } from '@tanstack/react-query';
import React from 'react';
import {
Pressable,
ScrollView,
StyleSheet,
Text,
TextInput,
View,
Pressable,
} from 'react-native';
import { secretsManager } from '@/lib/secrets-manager';

View File

@@ -0,0 +1,50 @@
import { MMKV, useMMKVString } from 'react-native-mmkv';
import { type ThemeName } from './theme';
const storage = new MMKV({ id: 'settings' });
type ShellListViewMode = 'flat' | 'grouped';
export const preferences = {
theme: {
_key: 'theme',
_resolve: (rawTheme: string | undefined): ThemeName =>
rawTheme === 'light' ? 'light' : 'dark',
get: (): ThemeName =>
preferences.theme._resolve(storage.getString(preferences.theme._key)),
set: (name: ThemeName) => storage.set(preferences.theme._key, name),
useThemePref: (): [ThemeName, (name: ThemeName) => void] => {
const [theme, setTheme] = useMMKVString(preferences.theme._key);
return [
preferences.theme._resolve(theme),
(name: ThemeName) => {
setTheme(name);
},
] as const;
},
},
shellListViewMode: {
_key: 'shellListViewMode',
_resolve: (rawMode: string | undefined): ShellListViewMode =>
rawMode === 'grouped' ? 'grouped' : 'flat',
get: (): ShellListViewMode =>
preferences.shellListViewMode._resolve(
storage.getString(preferences.shellListViewMode._key),
),
set: (mode: ShellListViewMode) =>
storage.set(preferences.shellListViewMode._key, mode),
useShellListViewModePref: (): [
ShellListViewMode,
(mode: ShellListViewMode) => void,
] => {
const [mode, setMode] = useMMKVString(preferences.shellListViewMode._key);
return [
preferences.shellListViewMode._resolve(mode),
(mode: 'flat' | 'grouped') => {
setMode(mode);
},
] as const;
},
},
} as const;

View File

@@ -1,8 +1,13 @@
import { RnRussh } from '@fressh/react-native-uniffi-russh';
import {
RnRussh,
type SshConnection,
type SshShellSession,
} from '@fressh/react-native-uniffi-russh';
import {
queryOptions,
useMutation,
useQueryClient,
type QueryClient,
} from '@tanstack/react-query';
import { useRouter } from 'expo-router';
import { secretsManager, type ConnectionDetails } from './secrets-manager';
@@ -74,3 +79,38 @@ export const listSshShellsQueryOptions = queryOptions({
queryKey: ['ssh-shells'],
queryFn: () => RnRussh.listSshConnectionsWithShells(),
});
export type ShellWithConnection = SshShellSession & {
connection: SshConnection;
};
export const closeSshShellAndInvalidateQuery = async (params: {
channelId: number;
connectionId: string;
queryClient: QueryClient;
}) => {
const currentActiveShells = RnRussh.listSshConnectionsWithShells();
const connection = currentActiveShells.find(
(c) => c.connectionId === params.connectionId,
);
if (!connection) throw new Error('Connection not found');
const shell = connection.shells.find((s) => s.channelId === params.channelId);
if (!shell) throw new Error('Shell not found');
await shell.close();
if (connection.shells.length <= 1) await connection.disconnect();
await params.queryClient.invalidateQueries({
queryKey: listSshShellsQueryOptions.queryKey,
});
};
export const disconnectSshConnectionAndInvalidateQuery = async (params: {
connectionId: string;
queryClient: QueryClient;
}) => {
const connection = RnRussh.getSshConnection(params.connectionId);
if (!connection) throw new Error('Connection not found');
await connection.disconnect();
await params.queryClient.invalidateQueries({
queryKey: listSshShellsQueryOptions.queryKey,
});
};

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { MMKV } from 'react-native-mmkv';
import { preferences } from './preferences';
export type AppTheme = {
colors: {
@@ -74,38 +74,23 @@ type ThemeContextValue = {
theme: AppTheme;
themeName: ThemeName;
setThemeName: (name: ThemeName) => void;
// Back-compat; not used externally but kept to avoid breaking imports
setTheme: (theme: AppTheme) => void;
};
const ThemeContext = React.createContext<ThemeContextValue | undefined>(
undefined,
);
const storage = new MMKV({ id: 'settings' });
const THEME_KEY = 'theme';
export function ThemeProvider(props: { children: React.ReactNode }) {
const [themeName, setThemeName] = React.useState<ThemeName>(() => {
const stored = storage.getString(THEME_KEY);
return stored === 'light' ? 'light' : 'dark';
});
const [themeName, setThemeName] = preferences.theme.useThemePref();
const theme = themes[themeName];
const handleSetThemeName = React.useCallback((name: ThemeName) => {
setThemeName(name);
storage.set(THEME_KEY, name);
}, []);
const value = React.useMemo<ThemeContextValue>(
() => ({
theme,
themeName,
setThemeName: handleSetThemeName,
setTheme: () => {},
setThemeName,
}),
[theme, themeName, handleSetThemeName],
[theme, themeName, setThemeName],
);
return (

78
pnpm-lock.yaml generated
View File

@@ -75,7 +75,7 @@ importers:
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.4)(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: 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-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)
@@ -110,8 +110,8 @@ importers:
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)
expo-router:
specifier: 6.0.4
version: 6.0.4(750b4158402c81723fe42a215de7942a)
specifier: 6.0.5
version: 6.0.5(750b4158402c81723fe42a215de7942a)
expo-secure-store:
specifier: ~15.0.7
version: 15.0.7(expo@54.0.7)
@@ -185,6 +185,9 @@ importers:
prettier:
specifier: ^3.6.2
version: 3.6.2
prettier-plugin-organize-imports:
specifier: ^4.2.0
version: 4.2.0(prettier@3.6.2)(typescript@5.9.2)
tsx:
specifier: ^4.20.5
version: 4.20.5
@@ -4800,8 +4803,8 @@ packages:
react: '*'
react-native: '*'
expo-router@6.0.4:
resolution: {integrity: sha512-RZRHAhUCCU6QNTnVhoy0PAJxbLy7OF78F4/4fwJna3cHGTtokPvJYUwz8kOZOOub/7l8jqgxnT7eKAk1dw9uXQ==}
expo-router@6.0.5:
resolution: {integrity: sha512-FK5y/55ppv54WjW7W7X4g5J3r+hiMKHukRYjyS6KI4i92qOWtVF42yssD/Ty90EpjKuZ8N1F72FJGdx9A1UQNA==}
peerDependencies:
'@expo/metro-runtime': ^6.1.2
'@react-navigation/drawer': ^7.5.0
@@ -9895,7 +9898,7 @@ snapshots:
'@eslint/core': 0.15.2
levn: 0.4.1
'@expo/cli@54.0.5(expo-router@6.0.4)(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.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))':
dependencies:
'@0no-co/graphql.web': 1.2.0
'@expo/code-signing-certificates': 0.0.5
@@ -9930,7 +9933,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.4)(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)
freeport-async: 2.0.0
getenv: 2.0.0
glob: 10.4.5
@@ -9962,7 +9965,7 @@ snapshots:
wrap-ansi: 7.0.0
ws: 8.18.3
optionalDependencies:
expo-router: 6.0.4(750b4158402c81723fe42a215de7942a)
expo-router: 6.0.5(750b4158402c81723fe42a215de7942a)
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:
- bufferutil
@@ -10141,7 +10144,7 @@ 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.4)(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)
transitivePeerDependencies:
- bufferutil
- supports-color
@@ -10150,7 +10153,7 @@ snapshots:
'@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)':
dependencies:
anser: 1.4.10
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.4)(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)
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)
@@ -10213,7 +10216,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.4)(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)
resolve-from: 5.0.0
semver: 7.7.2
xml2js: 0.6.0
@@ -12814,7 +12817,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.4)(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)
transitivePeerDependencies:
- '@babel/core'
- supports-color
@@ -14151,7 +14154,7 @@ snapshots:
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):
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.4)(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-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))
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)
@@ -14160,7 +14163,7 @@ snapshots:
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):
dependencies:
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.4)(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)
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)
@@ -14168,7 +14171,7 @@ snapshots:
dependencies:
'@expo/config': 12.0.8
'@expo/env': 2.0.7
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.4)(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)
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
@@ -14176,11 +14179,11 @@ snapshots:
expo-crypto@15.0.7(expo@54.0.7):
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.4)(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-dev-client@6.0.12(expo@54.0.7):
dependencies:
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.4)(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-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)
@@ -14191,7 +14194,7 @@ snapshots:
expo-dev-launcher@6.0.11(expo@54.0.7):
dependencies:
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.4)(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-dev-menu: 7.0.11(expo@54.0.7)
expo-manifests: 1.0.8(expo@54.0.7)
transitivePeerDependencies:
@@ -14199,42 +14202,42 @@ snapshots:
expo-dev-menu-interface@2.0.0(expo@54.0.7):
dependencies:
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.4)(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-dev-menu@7.0.11(expo@54.0.7):
dependencies:
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.4)(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-dev-menu-interface: 2.0.0(expo@54.0.7)
expo-document-picker@14.0.7(expo@54.0.7):
dependencies:
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.4)(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-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)):
dependencies:
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.4)(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)
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):
dependencies:
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.4)(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)
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):
dependencies:
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.4)(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)
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):
dependencies:
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.4)(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-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):
dependencies:
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.4)(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)
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:
@@ -14244,7 +14247,7 @@ snapshots:
expo-keep-awake@15.0.7(expo@54.0.7)(react@19.1.0):
dependencies:
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.4)(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)
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):
@@ -14260,7 +14263,7 @@ snapshots:
expo-manifests@1.0.8(expo@54.0.7):
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.4)(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-json-utils: 0.15.0
transitivePeerDependencies:
- supports-color
@@ -14280,7 +14283,7 @@ snapshots:
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.4(750b4158402c81723fe42a215de7942a):
expo-router@6.0.5(750b4158402c81723fe42a215de7942a):
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/schema-utils': 0.1.7
@@ -14293,7 +14296,7 @@ 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.4)(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-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)
fast-deep-equal: 3.1.3
@@ -14325,12 +14328,12 @@ snapshots:
expo-secure-store@15.0.7(expo@54.0.7):
dependencies:
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.4)(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-splash-screen@31.0.10(expo@54.0.7):
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.4)(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)
transitivePeerDependencies:
- supports-color
@@ -14342,7 +14345,7 @@ snapshots:
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)):
dependencies:
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.4)(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)
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
@@ -14350,7 +14353,7 @@ snapshots:
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.4)(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)
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)
@@ -14359,12 +14362,12 @@ snapshots:
expo-updates-interface@2.0.0(expo@54.0.7):
dependencies:
expo: 54.0.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.4)(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.7(@babel/core@7.28.3)(@expo/metro-runtime@6.1.1)(expo-router@6.0.4)(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):
dependencies:
'@babel/runtime': 7.28.3
'@expo/cli': 54.0.5(expo-router@6.0.4)(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.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/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)
@@ -17045,7 +17048,6 @@ snapshots:
dependencies:
prettier: 3.6.2
typescript: 5.9.2
optional: true
prettier-plugin-tailwindcss@0.6.14(prettier-plugin-astro@0.14.1)(prettier-plugin-organize-imports@4.2.0(prettier@3.6.2)(typescript@5.9.2))(prettier@3.6.2):
dependencies: