mirror of
https://github.com/EthanShoeDev/fressh.git
synced 2026-01-11 14:22:51 +00:00
styling
This commit is contained in:
3
.vscode/extensions.json
vendored
3
.vscode/extensions.json
vendored
@@ -7,6 +7,7 @@
|
||||
"mhutchie.git-graph",
|
||||
"esbenp.prettier-vscode",
|
||||
"yoavbls.pretty-ts-errors",
|
||||
"ctf0.duplicated-code-new"
|
||||
"ctf0.duplicated-code-new",
|
||||
"github.vscode-github-actions"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "expo start",
|
||||
"android": "expo run:android",
|
||||
"android": "expo run:android --port 8082",
|
||||
"ios": "expo run:ios --port 8081",
|
||||
"android:release": "expo run:android --variant release",
|
||||
"build:signed:aab": "tsx scripts/signed-build.ts",
|
||||
"build:signed:apk": "tsx scripts/signed-build.ts --format apk",
|
||||
"ios": "expo run:ios",
|
||||
"web": "expo start --web",
|
||||
"prebuild": "expo prebuild",
|
||||
"prebuild:clean": "expo prebuild --clean",
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
import { NativeTabs, Icon, Label } from 'expo-router/unstable-native-tabs';
|
||||
import React from 'react';
|
||||
import { useTheme } from '@/lib/theme';
|
||||
|
||||
export default function TabsLayout() {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<NativeTabs>
|
||||
<NativeTabs backgroundColor={theme.colors.surface}>
|
||||
<NativeTabs.Trigger name="index">
|
||||
<Label>Host</Label>
|
||||
<Icon sf="house.fill" drawable="custom_android_drawable" />
|
||||
<Icon sf="house.fill" drawable="ic_menu_myplaces" />
|
||||
</NativeTabs.Trigger>
|
||||
<NativeTabs.Trigger name="shell">
|
||||
<Icon sf="gear" drawable="custom_settings_drawable" />
|
||||
<Icon sf="gear" drawable="ic_menu_compass" />
|
||||
<Label>Shell</Label>
|
||||
</NativeTabs.Trigger>
|
||||
<NativeTabs.Trigger name="settings">
|
||||
<Icon sf="gear" drawable="custom_settings_drawable" />
|
||||
<Icon sf="gear" drawable="ic_menu_preferences" />
|
||||
<Label>Settings</Label>
|
||||
</NativeTabs.Trigger>
|
||||
</NativeTabs>
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
connectionDetailsSchema,
|
||||
secretsManager,
|
||||
} from '@/lib/secrets-manager';
|
||||
import { useTheme } from '@/theme';
|
||||
import { useTheme ,type AppTheme } from '@/lib/theme';
|
||||
|
||||
export default function TabsIndex() {
|
||||
return <Host />;
|
||||
@@ -40,6 +40,7 @@ const defaultValues: ConnectionDetails = {
|
||||
|
||||
function Host() {
|
||||
const theme = useTheme();
|
||||
const styles = React.useMemo(() => makeStyles(theme), [theme]);
|
||||
const insets = useSafeAreaInsets();
|
||||
const sshConnMutation = useSshConnMutation();
|
||||
const connectionForm = useAppForm({
|
||||
@@ -192,6 +193,8 @@ function Host() {
|
||||
}
|
||||
|
||||
function KeyIdPickerField() {
|
||||
const theme = useTheme();
|
||||
const styles = React.useMemo(() => makeStyles(theme), [theme]);
|
||||
const field = useFieldContext<string>();
|
||||
const [open, setOpen] = React.useState(false);
|
||||
|
||||
@@ -228,7 +231,7 @@ function KeyIdPickerField() {
|
||||
setOpen(true);
|
||||
}}
|
||||
>
|
||||
<Text style={{ color: '#E5E7EB' }}>{display}</Text>
|
||||
<Text style={{ color: theme.colors.textPrimary }}>{display}</Text>
|
||||
</Pressable>
|
||||
{!selected && (
|
||||
<Text style={styles.mutedText}>
|
||||
@@ -270,6 +273,8 @@ function KeyIdPickerField() {
|
||||
function PreviousConnectionsSection(props: {
|
||||
onSelect: (connection: ConnectionDetails) => void;
|
||||
}) {
|
||||
const theme = useTheme();
|
||||
const styles = React.useMemo(() => makeStyles(theme), [theme]);
|
||||
const listConnectionsQuery = useQuery(secretsManager.connections.query.list);
|
||||
|
||||
return (
|
||||
@@ -300,6 +305,8 @@ function ConnectionRow(props: {
|
||||
id: string;
|
||||
onSelect: (connection: ConnectionDetails) => 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;
|
||||
|
||||
@@ -324,11 +331,12 @@ function ConnectionRow(props: {
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
function makeStyles(theme: AppTheme) {
|
||||
return StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
padding: 24,
|
||||
backgroundColor: '#0B1324',
|
||||
backgroundColor: theme.colors.background,
|
||||
justifyContent: 'center',
|
||||
},
|
||||
scrollContent: {
|
||||
@@ -341,37 +349,37 @@ const styles = StyleSheet.create({
|
||||
appName: {
|
||||
fontSize: 28,
|
||||
fontWeight: '800',
|
||||
color: '#E5E7EB',
|
||||
color: theme.colors.textPrimary,
|
||||
letterSpacing: 1,
|
||||
},
|
||||
appTagline: {
|
||||
marginTop: 4,
|
||||
fontSize: 13,
|
||||
color: '#9AA0A6',
|
||||
color: theme.colors.muted,
|
||||
},
|
||||
card: {
|
||||
backgroundColor: '#111B34',
|
||||
backgroundColor: theme.colors.surface,
|
||||
borderRadius: 20,
|
||||
padding: 24,
|
||||
marginHorizontal: 4,
|
||||
shadowColor: '#000',
|
||||
shadowColor: theme.colors.shadow,
|
||||
shadowOpacity: 0.3,
|
||||
shadowRadius: 16,
|
||||
shadowOffset: { width: 0, height: 4 },
|
||||
elevation: 8,
|
||||
borderWidth: 1,
|
||||
borderColor: '#1E293B',
|
||||
borderColor: theme.colors.borderStrong,
|
||||
},
|
||||
title: {
|
||||
fontSize: 24,
|
||||
fontWeight: '700',
|
||||
color: '#E5E7EB',
|
||||
color: theme.colors.textPrimary,
|
||||
marginBottom: 6,
|
||||
letterSpacing: 0.5,
|
||||
},
|
||||
subtitle: {
|
||||
fontSize: 15,
|
||||
color: '#9AA0A6',
|
||||
color: theme.colors.muted,
|
||||
marginBottom: 24,
|
||||
lineHeight: 20,
|
||||
},
|
||||
@@ -381,14 +389,14 @@ const styles = StyleSheet.create({
|
||||
label: {
|
||||
marginBottom: 6,
|
||||
fontSize: 14,
|
||||
color: '#C6CBD3',
|
||||
color: theme.colors.textSecondary,
|
||||
fontWeight: '600',
|
||||
},
|
||||
input: {
|
||||
borderWidth: 1,
|
||||
borderColor: '#2A3655',
|
||||
backgroundColor: '#0E172B',
|
||||
color: '#E5E7EB',
|
||||
borderColor: theme.colors.border,
|
||||
backgroundColor: theme.colors.inputBackground,
|
||||
color: theme.colors.textPrimary,
|
||||
borderRadius: 10,
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 12,
|
||||
@@ -396,48 +404,48 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
errorText: {
|
||||
marginTop: 6,
|
||||
color: '#FCA5A5',
|
||||
color: theme.colors.danger,
|
||||
fontSize: 12,
|
||||
},
|
||||
actions: {
|
||||
marginTop: 20,
|
||||
},
|
||||
mutedText: {
|
||||
color: '#9AA0A6',
|
||||
color: theme.colors.muted,
|
||||
fontSize: 14,
|
||||
},
|
||||
submitButton: {
|
||||
backgroundColor: '#2563EB',
|
||||
backgroundColor: theme.colors.primary,
|
||||
borderRadius: 12,
|
||||
paddingVertical: 16,
|
||||
alignItems: 'center',
|
||||
shadowColor: '#2563EB',
|
||||
shadowColor: theme.colors.primary,
|
||||
shadowOpacity: 0.3,
|
||||
shadowRadius: 8,
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
elevation: 4,
|
||||
},
|
||||
submitButtonText: {
|
||||
color: '#FFFFFF',
|
||||
color: theme.colors.buttonTextOnPrimary,
|
||||
fontWeight: '700',
|
||||
fontSize: 16,
|
||||
letterSpacing: 0.5,
|
||||
},
|
||||
buttonDisabled: {
|
||||
backgroundColor: '#3B82F6',
|
||||
backgroundColor: theme.colors.primaryDisabled,
|
||||
opacity: 0.6,
|
||||
},
|
||||
secondaryButton: {
|
||||
backgroundColor: 'transparent',
|
||||
backgroundColor: theme.colors.transparent,
|
||||
borderWidth: 1,
|
||||
borderColor: '#2A3655',
|
||||
borderColor: theme.colors.border,
|
||||
borderRadius: 12,
|
||||
paddingVertical: 14,
|
||||
alignItems: 'center',
|
||||
marginTop: 12,
|
||||
},
|
||||
secondaryButtonText: {
|
||||
color: '#C6CBD3',
|
||||
color: theme.colors.textSecondary,
|
||||
fontWeight: '600',
|
||||
fontSize: 14,
|
||||
letterSpacing: 0.3,
|
||||
@@ -448,7 +456,7 @@ const styles = StyleSheet.create({
|
||||
listTitle: {
|
||||
fontSize: 16,
|
||||
fontWeight: '700',
|
||||
color: '#E5E7EB',
|
||||
color: theme.colors.textPrimary,
|
||||
marginBottom: 8,
|
||||
},
|
||||
listContainer: {
|
||||
@@ -458,9 +466,9 @@ const styles = StyleSheet.create({
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
backgroundColor: '#0E172B',
|
||||
backgroundColor: theme.colors.inputBackground,
|
||||
borderWidth: 1,
|
||||
borderColor: '#2A3655',
|
||||
borderColor: theme.colors.border,
|
||||
borderRadius: 12,
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 12,
|
||||
@@ -471,31 +479,31 @@ const styles = StyleSheet.create({
|
||||
marginRight: 12,
|
||||
},
|
||||
rowTitle: {
|
||||
color: '#E5E7EB',
|
||||
color: theme.colors.textPrimary,
|
||||
fontSize: 15,
|
||||
fontWeight: '600',
|
||||
},
|
||||
rowSubtitle: {
|
||||
color: '#9AA0A6',
|
||||
color: theme.colors.muted,
|
||||
marginTop: 2,
|
||||
fontSize: 12,
|
||||
},
|
||||
rowChevron: {
|
||||
color: '#9AA0A6',
|
||||
color: theme.colors.muted,
|
||||
fontSize: 22,
|
||||
paddingHorizontal: 4,
|
||||
},
|
||||
modalOverlay: {
|
||||
flex: 1,
|
||||
backgroundColor: 'rgba(0,0,0,0.4)',
|
||||
backgroundColor: theme.colors.overlay,
|
||||
justifyContent: 'flex-end',
|
||||
},
|
||||
modalSheet: {
|
||||
backgroundColor: '#0B1324',
|
||||
backgroundColor: theme.colors.background,
|
||||
borderTopLeftRadius: 16,
|
||||
borderTopRightRadius: 16,
|
||||
padding: 16,
|
||||
borderColor: '#1E293B',
|
||||
borderColor: theme.colors.borderStrong,
|
||||
borderWidth: 1,
|
||||
maxHeight: '85%',
|
||||
},
|
||||
@@ -510,10 +518,11 @@ const styles = StyleSheet.create({
|
||||
paddingVertical: 6,
|
||||
borderRadius: 8,
|
||||
borderWidth: 1,
|
||||
borderColor: '#2A3655',
|
||||
borderColor: theme.colors.border,
|
||||
},
|
||||
modalCloseText: {
|
||||
color: '#C6CBD3',
|
||||
color: theme.colors.textSecondary,
|
||||
fontWeight: '600',
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
import { RnRussh } from '@fressh/react-native-uniffi-russh';
|
||||
import {
|
||||
Link,
|
||||
Stack,
|
||||
useLocalSearchParams,
|
||||
useNavigation,
|
||||
useRouter,
|
||||
} from 'expo-router';
|
||||
import { Stack, useLocalSearchParams, useRouter } from 'expo-router';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
Platform,
|
||||
@@ -17,7 +11,7 @@ import {
|
||||
View,
|
||||
} from 'react-native';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { useTheme } from '@/theme';
|
||||
import { useTheme } from '@/lib/theme';
|
||||
|
||||
export default function TabsShellDetail() {
|
||||
return <ShellDetail />;
|
||||
|
||||
@@ -8,15 +8,22 @@ import { useQuery } from '@tanstack/react-query';
|
||||
import { Link } from 'expo-router';
|
||||
import React from 'react';
|
||||
import { StyleSheet, Text, View } from 'react-native';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { listSshShellsQueryOptions } from '@/lib/query-fns';
|
||||
import { useTheme ,type AppTheme } from '@/lib/theme';
|
||||
|
||||
export default function TabsShellList() {
|
||||
return <ShellList />;
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: theme.colors.background }}>
|
||||
<ShellContent />
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
|
||||
type ShellWithConnection = SshShellSession & { connection: SshConnection };
|
||||
|
||||
function ShellList() {
|
||||
function ShellContent() {
|
||||
const connectionsWithShells = useQuery(listSshShellsQueryOptions);
|
||||
|
||||
if (!connectionsWithShells.data) {
|
||||
@@ -26,6 +33,8 @@ function ShellList() {
|
||||
}
|
||||
|
||||
function LoadingState() {
|
||||
const theme = useTheme();
|
||||
const styles = React.useMemo(() => makeStyles(theme), [theme]);
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.text}>Loading...</Text>
|
||||
@@ -67,6 +76,8 @@ function LoadedState({
|
||||
}
|
||||
|
||||
function EmptyState() {
|
||||
const theme = useTheme();
|
||||
const styles = React.useMemo(() => makeStyles(theme), [theme]);
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.text}>No active shells. Connect from Host tab.</Text>
|
||||
@@ -76,6 +87,8 @@ function EmptyState() {
|
||||
}
|
||||
|
||||
function ShellCard({ shell }: { shell: ShellWithConnection }) {
|
||||
const theme = useTheme();
|
||||
const styles = React.useMemo(() => makeStyles(theme), [theme]);
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.text}>{shell.connectionId}</Text>
|
||||
@@ -93,7 +106,9 @@ function ShellCard({ shell }: { shell: ShellWithConnection }) {
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
function makeStyles(theme: AppTheme) {
|
||||
return StyleSheet.create({
|
||||
container: { flex: 1, alignItems: 'center', justifyContent: 'center' },
|
||||
text: { color: 'black', marginBottom: 8 },
|
||||
});
|
||||
text: { color: theme.colors.textPrimary, marginBottom: 8 },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ import { QueryClientProvider } from '@tanstack/react-query';
|
||||
import { isLiquidGlassAvailable } from 'expo-glass-effect';
|
||||
import { Stack } from 'expo-router';
|
||||
import React from 'react';
|
||||
import { ThemeProvider } from '../lib/theme';
|
||||
import { queryClient } from '../lib/utils';
|
||||
import { ThemeProvider } from '../theme';
|
||||
|
||||
console.log('Fressh App Init', {
|
||||
isLiquidGlassAvailable: isLiquidGlassAvailable(),
|
||||
|
||||
@@ -6,12 +6,18 @@ export type AppTheme = {
|
||||
surface: string;
|
||||
terminalBackground: string;
|
||||
border: string;
|
||||
borderStrong: string;
|
||||
textPrimary: string;
|
||||
textSecondary: string;
|
||||
muted: string;
|
||||
primary: string;
|
||||
buttonTextOnPrimary: string;
|
||||
inputBackground: string;
|
||||
danger: string;
|
||||
overlay: string;
|
||||
transparent: string;
|
||||
shadow: string;
|
||||
primaryDisabled: string;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -21,12 +27,18 @@ export const darkTheme: AppTheme = {
|
||||
surface: '#111B34',
|
||||
terminalBackground: '#0E172B',
|
||||
border: '#2A3655',
|
||||
borderStrong: '#1E293B',
|
||||
textPrimary: '#E5E7EB',
|
||||
textSecondary: '#C6CBD3',
|
||||
muted: '#9AA0A6',
|
||||
primary: '#2563EB',
|
||||
buttonTextOnPrimary: '#FFFFFF',
|
||||
inputBackground: '#0E172B',
|
||||
danger: '#FCA5A5',
|
||||
overlay: 'rgba(0,0,0,0.4)',
|
||||
transparent: 'transparent',
|
||||
shadow: '#000000',
|
||||
primaryDisabled: '#3B82F6',
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user