diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..4d9ef19 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,21 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + - run: npm ci + - run: npm run typecheck + - run: npm run lint + - run: npm test + - run: npm run build diff --git a/components/chat/EmptyState.tsx b/components/chat/EmptyState.tsx index 2cc252b..53cc0cb 100644 --- a/components/chat/EmptyState.tsx +++ b/components/chat/EmptyState.tsx @@ -3,7 +3,7 @@ import { MessageSquare, Sparkles } from 'lucide-react-native'; import React from 'react'; import { StyleSheet, Text, TouchableOpacity, View } from 'react-native'; -export const PROMPT_SUGGESTIONS = [ +const PROMPT_SUGGESTIONS = [ 'Explain recursion in simple terms', 'Write a short poem about coding', 'What are the best practices for React Native?', diff --git a/components/external-link.tsx b/components/external-link.tsx deleted file mode 100644 index 883e515..0000000 --- a/components/external-link.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { Href, Link } from 'expo-router'; -import { openBrowserAsync, WebBrowserPresentationStyle } from 'expo-web-browser'; -import { type ComponentProps } from 'react'; - -type Props = Omit, 'href'> & { href: Href & string }; - -export function ExternalLink({ href, ...rest }: Props) { - return ( - { - if (process.env.EXPO_OS !== 'web') { - // Prevent the default behavior of linking to the default browser on native. - event.preventDefault(); - // Open the link in an in-app browser. - await openBrowserAsync(href, { - presentationStyle: WebBrowserPresentationStyle.AUTOMATIC, - }); - } - }} - /> - ); -} diff --git a/components/haptic-tab.tsx b/components/haptic-tab.tsx deleted file mode 100644 index 7f3981c..0000000 --- a/components/haptic-tab.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { BottomTabBarButtonProps } from '@react-navigation/bottom-tabs'; -import { PlatformPressable } from '@react-navigation/elements'; -import * as Haptics from 'expo-haptics'; - -export function HapticTab(props: BottomTabBarButtonProps) { - return ( - { - if (process.env.EXPO_OS === 'ios') { - // Add a soft haptic feedback when pressing down on the tabs. - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); - } - props.onPressIn?.(ev); - }} - /> - ); -} diff --git a/components/hello-wave.tsx b/components/hello-wave.tsx deleted file mode 100644 index 5def547..0000000 --- a/components/hello-wave.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import Animated from 'react-native-reanimated'; - -export function HelloWave() { - return ( - - šŸ‘‹ - - ); -} diff --git a/components/parallax-scroll-view.tsx b/components/parallax-scroll-view.tsx deleted file mode 100644 index 6f674a7..0000000 --- a/components/parallax-scroll-view.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import type { PropsWithChildren, ReactElement } from 'react'; -import { StyleSheet } from 'react-native'; -import Animated, { - interpolate, - useAnimatedRef, - useAnimatedStyle, - useScrollOffset, -} from 'react-native-reanimated'; - -import { ThemedView } from '@/components/themed-view'; -import { useColorScheme } from '@/hooks/use-color-scheme'; -import { useThemeColor } from '@/hooks/use-theme-color'; - -const HEADER_HEIGHT = 250; - -type Props = PropsWithChildren<{ - headerImage: ReactElement; - headerBackgroundColor: { dark: string; light: string }; -}>; - -export default function ParallaxScrollView({ - children, - headerImage, - headerBackgroundColor, -}: Props) { - const backgroundColor = useThemeColor({}, 'background'); - const colorScheme = useColorScheme() ?? 'light'; - const scrollRef = useAnimatedRef(); - const scrollOffset = useScrollOffset(scrollRef); - const headerAnimatedStyle = useAnimatedStyle(() => { - return { - transform: [ - { - translateY: interpolate( - scrollOffset.value, - [-HEADER_HEIGHT, 0, HEADER_HEIGHT], - [-HEADER_HEIGHT / 2, 0, HEADER_HEIGHT * 0.75] - ), - }, - { - scale: interpolate(scrollOffset.value, [-HEADER_HEIGHT, 0, HEADER_HEIGHT], [2, 1, 1]), - }, - ], - }; - }); - - return ( - - - {headerImage} - - {children} - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - }, - header: { - height: HEADER_HEIGHT, - overflow: 'hidden', - }, - content: { - flex: 1, - padding: 32, - gap: 16, - overflow: 'hidden', - }, -}); diff --git a/components/themed-text.tsx b/components/themed-text.tsx deleted file mode 100644 index d79d0a1..0000000 --- a/components/themed-text.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { StyleSheet, Text, type TextProps } from 'react-native'; - -import { useThemeColor } from '@/hooks/use-theme-color'; - -export type ThemedTextProps = TextProps & { - lightColor?: string; - darkColor?: string; - type?: 'default' | 'title' | 'defaultSemiBold' | 'subtitle' | 'link'; -}; - -export function ThemedText({ - style, - lightColor, - darkColor, - type = 'default', - ...rest -}: ThemedTextProps) { - const color = useThemeColor({ light: lightColor, dark: darkColor }, 'text'); - - return ( - - ); -} - -const styles = StyleSheet.create({ - default: { - fontSize: 16, - lineHeight: 24, - }, - defaultSemiBold: { - fontSize: 16, - lineHeight: 24, - fontWeight: '600', - }, - title: { - fontSize: 32, - fontWeight: 'bold', - lineHeight: 32, - }, - subtitle: { - fontSize: 20, - fontWeight: 'bold', - }, - link: { - lineHeight: 30, - fontSize: 16, - color: '#0a7ea4', - }, -}); diff --git a/components/themed-view.tsx b/components/themed-view.tsx deleted file mode 100644 index 6f181d8..0000000 --- a/components/themed-view.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { View, type ViewProps } from 'react-native'; - -import { useThemeColor } from '@/hooks/use-theme-color'; - -export type ThemedViewProps = ViewProps & { - lightColor?: string; - darkColor?: string; -}; - -export function ThemedView({ style, lightColor, darkColor, ...otherProps }: ThemedViewProps) { - const backgroundColor = useThemeColor({ light: lightColor, dark: darkColor }, 'background'); - - return ; -} diff --git a/components/ui/Button.tsx b/components/ui/Button.tsx deleted file mode 100644 index 7b17bb1..0000000 --- a/components/ui/Button.tsx +++ /dev/null @@ -1,118 +0,0 @@ -import { Colors } from '@/constants/theme'; -import { useColorScheme } from '@/hooks/use-color-scheme'; -import React from 'react'; -import { ActivityIndicator, Pressable, StyleSheet, Text, TextStyle, ViewStyle } from 'react-native'; - -interface ButtonProps { - title: string; - onPress: () => void; - variant?: 'primary' | 'secondary' | 'outline' | 'ghost'; - size?: 'sm' | 'md' | 'lg'; - loading?: boolean; - disabled?: boolean; - style?: ViewStyle; - textStyle?: TextStyle; - icon?: React.ReactNode; -} - -export function Button({ - title, - onPress, - variant = 'primary', - size = 'md', - loading = false, - disabled = false, - style, - textStyle, - icon -}: ButtonProps) { - const colorScheme = useColorScheme(); - const theme = Colors[colorScheme ?? 'dark']; - - const getBackgroundColor = () => { - if (disabled) return theme.surfaceHighlight; - switch (variant) { - case 'primary': return theme.primary; - case 'secondary': return theme.surfaceHighlight; - case 'outline': return 'transparent'; - case 'ghost': return 'transparent'; - default: return theme.primary; - } - }; - - const getTextColor = () => { - if (disabled) return theme.textSecondary; - switch (variant) { - case 'primary': return theme.background; // Black text on white bg - case 'secondary': return theme.text; - case 'outline': return theme.text; - case 'ghost': return theme.textSecondary; - default: return theme.background; - } - }; - - const getBorder = () => { - if (variant === 'outline') return { borderWidth: 1, borderColor: theme.border }; - return {}; - }; - - const getPadding = () => { - switch (size) { - case 'sm': return { paddingVertical: 8, paddingHorizontal: 12 }; - case 'lg': return { paddingVertical: 16, paddingHorizontal: 24 }; - default: return { paddingVertical: 12, paddingHorizontal: 20 }; - } - }; - - const getFontSize = () => { - switch (size) { - case 'sm': return 13; - case 'lg': return 17; - default: return 15; - } - }; - - return ( - [ - styles.button, - { backgroundColor: getBackgroundColor() }, - getBorder(), - getPadding(), - { opacity: pressed ? 0.8 : 1 }, - style - ]} - > - {loading ? ( - - ) : ( - <> - {icon} - - {title} - - - )} - - ); -} - -const styles = StyleSheet.create({ - button: { - borderRadius: 12, - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', - }, - text: { - fontWeight: '600', - letterSpacing: 0.5, - }, -}); diff --git a/components/ui/GlassCard.tsx b/components/ui/GlassCard.tsx deleted file mode 100644 index 5351866..0000000 --- a/components/ui/GlassCard.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { Colors } from '@/constants/theme'; -import { useColorScheme } from '@/hooks/use-color-scheme'; -import { StyleSheet, View, ViewStyle } from 'react-native'; - -interface GlassCardProps { - children: React.ReactNode; - style?: ViewStyle; - variant?: 'default' | 'highlight'; -} - -export function GlassCard({ children, style, variant = 'default' }: GlassCardProps) { - const colorScheme = useColorScheme(); - const theme = Colors[colorScheme ?? 'dark']; - - return ( - - {children} - - ); -} - -const styles = StyleSheet.create({ - card: { - borderRadius: 16, - borderWidth: 1, - padding: 16, - overflow: 'hidden', - }, -}); diff --git a/components/ui/ScreenWrapper.tsx b/components/ui/ScreenWrapper.tsx deleted file mode 100644 index c309e8a..0000000 --- a/components/ui/ScreenWrapper.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import { Colors } from '@/constants/theme'; -import { useColorScheme } from '@/hooks/use-color-scheme'; -import { StatusBar } from 'expo-status-bar'; -import { ScrollView, StyleSheet, View, ViewStyle } from 'react-native'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; - -interface ScreenWrapperProps { - children: React.ReactNode; - style?: ViewStyle; - contentContainerStyle?: ViewStyle; - scrollable?: boolean; -} - -export function ScreenWrapper({ - children, - style, - contentContainerStyle, - scrollable = false -}: ScreenWrapperProps) { - const colorScheme = useColorScheme(); - const theme = Colors[colorScheme ?? 'dark']; - const insets = useSafeAreaInsets(); - - const containerStyle = [ - styles.container, - { - backgroundColor: theme.background, - paddingTop: insets.top, - paddingLeft: insets.left, - paddingRight: insets.right, - }, - style - ]; - - if (scrollable) { - return ( - - - - {children} - - - ); - } - - return ( - - - - {children} - - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - }, -}); diff --git a/components/ui/collapsible.tsx b/components/ui/collapsible.tsx deleted file mode 100644 index 6345fde..0000000 --- a/components/ui/collapsible.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { PropsWithChildren, useState } from 'react'; -import { StyleSheet, TouchableOpacity } from 'react-native'; - -import { ThemedText } from '@/components/themed-text'; -import { ThemedView } from '@/components/themed-view'; -import { IconSymbol } from '@/components/ui/icon-symbol'; -import { Colors } from '@/constants/theme'; -import { useColorScheme } from '@/hooks/use-color-scheme'; - -export function Collapsible({ children, title }: PropsWithChildren & { title: string }) { - const [isOpen, setIsOpen] = useState(false); - const theme = useColorScheme() ?? 'light'; - - return ( - - setIsOpen((value) => !value)} - activeOpacity={0.8}> - - - {title} - - {isOpen && {children}} - - ); -} - -const styles = StyleSheet.create({ - heading: { - flexDirection: 'row', - alignItems: 'center', - gap: 6, - }, - content: { - marginTop: 6, - marginLeft: 24, - }, -}); diff --git a/components/ui/icon-symbol.ios.tsx b/components/ui/icon-symbol.ios.tsx deleted file mode 100644 index 9177f4d..0000000 --- a/components/ui/icon-symbol.ios.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { SymbolView, SymbolViewProps, SymbolWeight } from 'expo-symbols'; -import { StyleProp, ViewStyle } from 'react-native'; - -export function IconSymbol({ - name, - size = 24, - color, - style, - weight = 'regular', -}: { - name: SymbolViewProps['name']; - size?: number; - color: string; - style?: StyleProp; - weight?: SymbolWeight; -}) { - return ( - - ); -} diff --git a/components/ui/icon-symbol.tsx b/components/ui/icon-symbol.tsx deleted file mode 100644 index b7ece6b..0000000 --- a/components/ui/icon-symbol.tsx +++ /dev/null @@ -1,41 +0,0 @@ -// Fallback for using MaterialIcons on Android and web. - -import MaterialIcons from '@expo/vector-icons/MaterialIcons'; -import { SymbolWeight, SymbolViewProps } from 'expo-symbols'; -import { ComponentProps } from 'react'; -import { OpaqueColorValue, type StyleProp, type TextStyle } from 'react-native'; - -type IconMapping = Record['name']>; -type IconSymbolName = keyof typeof MAPPING; - -/** - * Add your SF Symbols to Material Icons mappings here. - * - see Material Icons in the [Icons Directory](https://icons.expo.fyi). - * - see SF Symbols in the [SF Symbols](https://developer.apple.com/sf-symbols/) app. - */ -const MAPPING = { - 'house.fill': 'home', - 'paperplane.fill': 'send', - 'chevron.left.forwardslash.chevron.right': 'code', - 'chevron.right': 'chevron-right', -} as IconMapping; - -/** - * An icon component that uses native SF Symbols on iOS, and Material Icons on Android and web. - * This ensures a consistent look across platforms, and optimal resource usage. - * Icon `name`s are based on SF Symbols and require manual mapping to Material Icons. - */ -export function IconSymbol({ - name, - size = 24, - color, - style, -}: { - name: IconSymbolName; - size?: number; - color: string | OpaqueColorValue; - style?: StyleProp; - weight?: SymbolWeight; -}) { - return ; -} diff --git a/constants/Models.ts b/constants/Models.ts deleted file mode 100644 index dbca882..0000000 --- a/constants/Models.ts +++ /dev/null @@ -1,33 +0,0 @@ -export type AIModel = { - id: string; - name: string; - parameters: string; - huggingFaceUrl: string; - description: string; -}; - -export const QWEN_MODELS: AIModel[] = [ - { - id: "qwen1.5-0.5b-chat", - name: "Qwen 1.5 Chat", - parameters: "0.5B", - huggingFaceUrl: "https://huggingface.co/Qwen/Qwen1.5-0.5B-Chat", - description: "Ultra-lightweight model, extremely fast for basic tasks.", - }, - { - id: "qwen1.5-1.8b-chat", - name: "Qwen 1.5 Chat", - parameters: "1.8B", - huggingFaceUrl: "https://huggingface.co/Qwen/Qwen1.5-1.8B-Chat", - description: "Good balance of speed and reasoning for mobile devices.", - }, - { - id: "qwen1.5-4b-chat", - name: "Qwen 1.5 Chat", - parameters: "4B", - huggingFaceUrl: "https://huggingface.co/Qwen/Qwen1.5-4B-Chat", - description: "Highest local quality, requires more RAM and processing power.", - } -]; - -export const DEFAULT_MODEL = QWEN_MODELS[0]; diff --git a/constants/theme.ts b/constants/theme.ts deleted file mode 100644 index b129665..0000000 --- a/constants/theme.ts +++ /dev/null @@ -1,87 +0,0 @@ -/** - * ManusAI Theme - Monochromatic/Minimalist - * - * Design System: - * - Primary: White (#ffffff) - * - Background: Deep Matte Black (#09090b) - * - Surface: Zinc-900 (#18181b) - * - Border: Zinc-700 (#3f3f46) - * - Text: Zinc-50 (#fafafa) - */ - -import { Platform } from 'react-native'; - -const ManusColors = { - // Base - black: '#09090b', - zinc900: '#18181b', - zinc800: '#27272a', - zinc700: '#3f3f46', - zinc400: '#a1a1aa', - zinc50: '#fafafa', - white: '#ffffff', - - // Semantic - background: '#09090b', - surface: '#18181b', - surfaceHighlight: '#27272a', - primary: '#ffffff', - border: '#3f3f46', - text: '#fafafa', - textSecondary: '#a1a1aa', - error: '#ef4444', -}; - -export const Colors = { - light: { - text: ManusColors.black, - background: ManusColors.white, - tint: ManusColors.black, - icon: ManusColors.zinc700, - tabIconDefault: ManusColors.zinc400, - tabIconSelected: ManusColors.black, - // Manus Specific - surface: '#f4f4f5', // Zinc-100 - surfaceHighlight: '#e4e4e7', // Zinc-200 - border: '#d4d4d8', // Zinc-300 - textSecondary: ManusColors.zinc700, - primary: ManusColors.black, - }, - dark: { - text: ManusColors.text, - background: ManusColors.background, - tint: ManusColors.primary, - icon: ManusColors.zinc400, - tabIconDefault: ManusColors.zinc400, - tabIconSelected: ManusColors.primary, - // Manus Specific - surface: ManusColors.surface, - surfaceHighlight: ManusColors.surfaceHighlight, - border: ManusColors.border, - textSecondary: ManusColors.textSecondary, - primary: ManusColors.primary, - }, - // Unified access for components that ignore system theme - manus: ManusColors, -}; - -export const Fonts = Platform.select({ - ios: { - sans: 'system-ui', - serif: 'ui-serif', - rounded: 'ui-rounded', - mono: 'ui-monospace', - }, - android: { - sans: 'sans-serif', - serif: 'serif', - rounded: 'sans-serif-medium', - mono: 'monospace', - }, - default: { - sans: 'system-ui', - serif: 'serif', - rounded: 'system-ui', - mono: 'monospace', - }, -}); diff --git a/hooks/use-color-scheme.ts b/hooks/use-color-scheme.ts deleted file mode 100644 index 17e3c63..0000000 --- a/hooks/use-color-scheme.ts +++ /dev/null @@ -1 +0,0 @@ -export { useColorScheme } from 'react-native'; diff --git a/hooks/use-color-scheme.web.ts b/hooks/use-color-scheme.web.ts deleted file mode 100644 index 7eb1c1b..0000000 --- a/hooks/use-color-scheme.web.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { useEffect, useState } from 'react'; -import { useColorScheme as useRNColorScheme } from 'react-native'; - -/** - * To support static rendering, this value needs to be re-calculated on the client side for web - */ -export function useColorScheme() { - const [hasHydrated, setHasHydrated] = useState(false); - - useEffect(() => { - setHasHydrated(true); - }, []); - - const colorScheme = useRNColorScheme(); - - if (hasHydrated) { - return colorScheme; - } - - return 'light'; -} diff --git a/hooks/use-theme-color.ts b/hooks/use-theme-color.ts deleted file mode 100644 index 0cbc3a6..0000000 --- a/hooks/use-theme-color.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Learn more about light and dark modes: - * https://docs.expo.dev/guides/color-schemes/ - */ - -import { Colors } from '@/constants/theme'; -import { useColorScheme } from '@/hooks/use-color-scheme'; - -export function useThemeColor( - props: { light?: string; dark?: string }, - colorName: keyof typeof Colors.light & keyof typeof Colors.dark -) { - const theme = useColorScheme() ?? 'light'; - const colorFromProps = props[theme]; - - if (colorFromProps) { - return colorFromProps; - } else { - return Colors[theme][colorName]; - } -} diff --git a/package-lock.json b/package-lock.json index 1cb8810..156bb84 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,6 @@ "name": "localaiapp", "version": "1.0.0", "dependencies": { - "@babel/plugin-transform-runtime": "^7.29.0", "@babel/runtime": "^7.28.6", "@expo/vector-icons": "^15.0.3", "@react-native-async-storage/async-storage": "2.2.0", @@ -21,14 +20,11 @@ "expo-constants": "~18.0.14", "expo-font": "~14.0.12", "expo-haptics": "~15.0.8", - "expo-image": "~3.0.11", "expo-linking": "~8.0.12", "expo-router": "~6.0.24", "expo-splash-screen": "~31.0.13", "expo-status-bar": "~3.0.9", - "expo-symbols": "~1.0.8", "expo-system-ui": "~6.0.9", - "expo-web-browser": "~15.0.11", "immer": "^11.1.4", "lucide-react-native": "^0.576.0", "react": "19.1.0", @@ -38,7 +34,6 @@ "react-native-reanimated": "~4.1.1", "react-native-safe-area-context": "~5.6.0", "react-native-screens": "~4.16.0", - "react-native-sse": "^1.2.1", "react-native-svg": "15.12.1", "react-native-web": "~0.21.0", "react-native-worklets": "0.5.1", @@ -7111,23 +7106,6 @@ "expo": "*" } }, - "node_modules/expo-image": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/expo-image/-/expo-image-3.0.11.tgz", - "integrity": "sha512-4TudfUCLgYgENv+f48omnU8tjS2S0Pd9EaON5/s1ZUBRwZ7K8acEr4NfvLPSaeXvxW24iLAiyQ7sV7BXQH3RoA==", - "license": "MIT", - "peerDependencies": { - "expo": "*", - "react": "*", - "react-native": "*", - "react-native-web": "*" - }, - "peerDependenciesMeta": { - "react-native-web": { - "optional": true - } - } - }, "node_modules/expo-keep-awake": { "version": "15.0.8", "resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-15.0.8.tgz", @@ -7468,19 +7446,6 @@ "react-native": "*" } }, - "node_modules/expo-symbols": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/expo-symbols/-/expo-symbols-1.0.8.tgz", - "integrity": "sha512-7bNjK350PaQgxBf0owpmSYkdZIpdYYmaPttDBb2WIp6rIKtcEtdzdfmhsc2fTmjBURHYkg36+eCxBFXO25/1hw==", - "license": "MIT", - "dependencies": { - "sf-symbols-typescript": "^2.0.0" - }, - "peerDependencies": { - "expo": "*", - "react-native": "*" - } - }, "node_modules/expo-system-ui": { "version": "6.0.9", "resolved": "https://registry.npmjs.org/expo-system-ui/-/expo-system-ui-6.0.9.tgz", @@ -7501,16 +7466,6 @@ } } }, - "node_modules/expo-web-browser": { - "version": "15.0.11", - "resolved": "https://registry.npmjs.org/expo-web-browser/-/expo-web-browser-15.0.11.tgz", - "integrity": "sha512-r2LS4Ro6DgUPZkcaEfgt8mp9eJuoA93x11Jh7S6utFe0FEzvUNn2yFhxg8XVwESaaHGt2k5V8LuK36rsp0BeIw==", - "license": "MIT", - "peerDependencies": { - "expo": "*", - "react-native": "*" - } - }, "node_modules/expo/node_modules/@expo/cli": { "version": "54.0.27", "resolved": "https://registry.npmjs.org/@expo/cli/-/cli-54.0.27.tgz", @@ -12969,12 +12924,6 @@ "react-native": "*" } }, - "node_modules/react-native-sse": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/react-native-sse/-/react-native-sse-1.2.1.tgz", - "integrity": "sha512-zejanlScF+IB9tYnbdry0MT34qjBXbiV/E72qGz33W/tX1bx8MXsbB4lxiuPETc9v/008vYZ60yjIstW22VlVg==", - "license": "MIT" - }, "node_modules/react-native-svg": { "version": "15.12.1", "resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-15.12.1.tgz", diff --git a/package.json b/package.json index 80d7eb4..f727853 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,6 @@ "version": "1.0.0", "scripts": { "start": "expo start", - "reset-project": "node ./scripts/reset-project.js", "android": "expo start --android", "ios": "expo start --ios", "web": "expo start --web", @@ -14,7 +13,6 @@ "build": "expo export --platform web" }, "dependencies": { - "@babel/plugin-transform-runtime": "^7.29.0", "@babel/runtime": "^7.28.6", "@expo/vector-icons": "^15.0.3", "@react-native-async-storage/async-storage": "2.2.0", @@ -27,14 +25,11 @@ "expo-constants": "~18.0.14", "expo-font": "~14.0.12", "expo-haptics": "~15.0.8", - "expo-image": "~3.0.11", "expo-linking": "~8.0.12", "expo-router": "~6.0.24", "expo-splash-screen": "~31.0.13", "expo-status-bar": "~3.0.9", - "expo-symbols": "~1.0.8", "expo-system-ui": "~6.0.9", - "expo-web-browser": "~15.0.11", "immer": "^11.1.4", "lucide-react-native": "^0.576.0", "react": "19.1.0", @@ -44,7 +39,6 @@ "react-native-reanimated": "~4.1.1", "react-native-safe-area-context": "~5.6.0", "react-native-screens": "~4.16.0", - "react-native-sse": "^1.2.1", "react-native-svg": "15.12.1", "react-native-web": "~0.21.0", "react-native-worklets": "0.5.1", diff --git a/scripts/reset-project.js b/scripts/reset-project.js deleted file mode 100644 index 51dff15..0000000 --- a/scripts/reset-project.js +++ /dev/null @@ -1,112 +0,0 @@ -#!/usr/bin/env node - -/** - * This script is used to reset the project to a blank state. - * It deletes or moves the /app, /components, /hooks, /scripts, and /constants directories to /app-example based on user input and creates a new /app directory with an index.tsx and _layout.tsx file. - * You can remove the `reset-project` script from package.json and safely delete this file after running it. - */ - -const fs = require("fs"); -const path = require("path"); -const readline = require("readline"); - -const root = process.cwd(); -const oldDirs = ["app", "components", "hooks", "constants", "scripts"]; -const exampleDir = "app-example"; -const newAppDir = "app"; -const exampleDirPath = path.join(root, exampleDir); - -const indexContent = `import { Text, View } from "react-native"; - -export default function Index() { - return ( - - Edit app/index.tsx to edit this screen. - - ); -} -`; - -const layoutContent = `import { Stack } from "expo-router"; - -export default function RootLayout() { - return ; -} -`; - -const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout, -}); - -const moveDirectories = async (userInput) => { - try { - if (userInput === "y") { - // Create the app-example directory - await fs.promises.mkdir(exampleDirPath, { recursive: true }); - console.log(`šŸ“ /${exampleDir} directory created.`); - } - - // Move old directories to new app-example directory or delete them - for (const dir of oldDirs) { - const oldDirPath = path.join(root, dir); - if (fs.existsSync(oldDirPath)) { - if (userInput === "y") { - const newDirPath = path.join(root, exampleDir, dir); - await fs.promises.rename(oldDirPath, newDirPath); - console.log(`āž”ļø /${dir} moved to /${exampleDir}/${dir}.`); - } else { - await fs.promises.rm(oldDirPath, { recursive: true, force: true }); - console.log(`āŒ /${dir} deleted.`); - } - } else { - console.log(`āž”ļø /${dir} does not exist, skipping.`); - } - } - - // Create new /app directory - const newAppDirPath = path.join(root, newAppDir); - await fs.promises.mkdir(newAppDirPath, { recursive: true }); - console.log("\nšŸ“ New /app directory created."); - - // Create index.tsx - const indexPath = path.join(newAppDirPath, "index.tsx"); - await fs.promises.writeFile(indexPath, indexContent); - console.log("šŸ“„ app/index.tsx created."); - - // Create _layout.tsx - const layoutPath = path.join(newAppDirPath, "_layout.tsx"); - await fs.promises.writeFile(layoutPath, layoutContent); - console.log("šŸ“„ app/_layout.tsx created."); - - console.log("\nāœ… Project reset complete. Next steps:"); - console.log( - `1. Run \`npx expo start\` to start a development server.\n2. Edit app/index.tsx to edit the main screen.${ - userInput === "y" - ? `\n3. Delete the /${exampleDir} directory when you're done referencing it.` - : "" - }` - ); - } catch (error) { - console.error(`āŒ Error during script execution: ${error.message}`); - } -}; - -rl.question( - "Do you want to move existing files to /app-example instead of deleting them? (Y/n): ", - (answer) => { - const userInput = answer.trim().toLowerCase() || "y"; - if (userInput === "y" || userInput === "n") { - moveDirectories(userInput).finally(() => rl.close()); - } else { - console.log("āŒ Invalid input. Please enter 'Y' or 'N'."); - rl.close(); - } - } -);