commit da373199e0725ce4877ba1125576bef691e3264a Author: Jonathan Atta Date: Tue Mar 3 10:33:56 2026 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/app/.bundle/config b/app/.bundle/config new file mode 100644 index 0000000..848943b --- /dev/null +++ b/app/.bundle/config @@ -0,0 +1,2 @@ +BUNDLE_PATH: "vendor/bundle" +BUNDLE_FORCE_RUBY_PLATFORM: 1 diff --git a/app/.eslintrc.js b/app/.eslintrc.js new file mode 100644 index 0000000..f7b8b63 --- /dev/null +++ b/app/.eslintrc.js @@ -0,0 +1,46 @@ +module.exports = { + root: true, + extends: '@react-native', + rules: { + // React Native + 'react-native/no-unused-styles': 'error', + 'react-native/no-inline-styles': 'warn', + 'react-native/no-color-literals': 'warn', + 'react-native/no-raw-text': 'off', + + // TypeScript + '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }], + '@typescript-eslint/no-explicit-any': 'error', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-non-null-assertion': 'warn', + '@typescript-eslint/no-unused-expressions': 'error', + '@typescript-eslint/no-shadow': 'error', + 'no-shadow': 'off', // Désactivé au profit de la version TypeScript + + // React/Hooks + 'react-hooks/rules-of-hooks': 'error', + 'react-hooks/exhaustive-deps': 'warn', + 'react/no-unstable-nested-components': ['error', { allowAsProps: true }], + 'react/jsx-no-bind': ['warn', { allowArrowFunctions: true }], + 'react/no-array-index-key': 'error', + + // Code Quality + 'no-console': ['warn', { allow: ['warn', 'error'] }], + 'prefer-const': 'error', + 'no-var': 'error', + 'eqeqeq': ['error', 'always'], + 'no-duplicate-imports': 'error', + 'no-return-await': 'error', + 'no-param-reassign': ['error', { props: false }], + 'no-throw-literal': 'error', + 'max-lines': ['error', { max: 600, skipBlankLines: true }], + 'max-len': ['error', { code: 100, ignoreUrls: true, ignoreStrings: true, ignoreTemplateLiterals: true }], + + // Style + 'arrow-body-style': ['error', 'as-needed'], + 'object-shorthand': ['error', 'always'], + 'prefer-template': 'error', + 'prefer-destructuring': ['error', { array: false, object: true }], + }, +}; diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..4021130 --- /dev/null +++ b/app/.gitignore @@ -0,0 +1,76 @@ +# OSX +# +.DS_Store + +# Xcode +# +build/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata +*.xccheckout +*.moved-aside +DerivedData +*.hmap +*.ipa +*.xcuserstate +**/.xcode.env.local + +# Android/IntelliJ +# +build/ +.idea +.gradle +local.properties +*.iml +*.hprof +.cxx/ +*.keystore +!debug.keystore +.kotlin/ + +# node.js +# +node_modules/ +npm-debug.log +yarn-error.log + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the +# screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/ + +**/fastlane/report.xml +**/fastlane/Preview.html +**/fastlane/screenshots +**/fastlane/test_output + +# Bundle artifact +*.jsbundle + +# Ruby / CocoaPods +**/Pods/ +/vendor/bundle/ + +# Temporary files created by Metro to check the health of the file watcher +.metro-health-check* + +# testing +/coverage + +# Yarn +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/sdks +!.yarn/versions +pocketpal-ai-main \ No newline at end of file diff --git a/app/.prettierrc.js b/app/.prettierrc.js new file mode 100644 index 0000000..06860c8 --- /dev/null +++ b/app/.prettierrc.js @@ -0,0 +1,5 @@ +module.exports = { + arrowParens: 'avoid', + singleQuote: true, + trailingComma: 'all', +}; diff --git a/app/.vscode/mcp.json b/app/.vscode/mcp.json new file mode 100644 index 0000000..d08cd55 --- /dev/null +++ b/app/.vscode/mcp.json @@ -0,0 +1,19 @@ +{ + "servers": { + "chrome-devtools": { + "type": "stdio", + "command": "npx", + "args": ["chrome-devtools-mcp@latest"] + }, + "context7": { + "type": "stdio", + "command": "npx", + "args": ["-y", "@upstash/context7-mcp"] + }, + "ui-expert": { + "type": "stdio", + "command": "npx", + "args": ["-y", "@reallygood83/ui-expert-mcp"] + } + } +} \ No newline at end of file diff --git a/app/.watchmanconfig b/app/.watchmanconfig new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/app/.watchmanconfig @@ -0,0 +1 @@ +{} diff --git a/app/App.tsx b/app/App.tsx new file mode 100644 index 0000000..b7f1138 --- /dev/null +++ b/app/App.tsx @@ -0,0 +1,103 @@ +/** + * Sample React Native App + * https://github.com/facebook/react-native + * + * @format + */ + +import React, { useEffect, useState } from 'react'; +import { ActivityIndicator, StatusBar, View, StyleSheet } from 'react-native'; +import { SafeAreaProvider } from 'react-native-safe-area-context'; +import { NavigationContainer, DefaultTheme, DarkTheme } from '@react-navigation/native'; +import { Provider, useDispatch } from 'react-redux'; + +import store, { type AppDispatch } from './src/store'; +import AppNavigator from './src/navigation'; +import { ThemeProvider, useTheme } from './src/theme/ThemeProvider'; +import { loadAgents } from './src/store/agentsSlice'; +import { rehydrateChat } from './src/store/chatSlice'; +import { loadChatState } from './src/store/persistence'; + +function InnerApp() { + const { scheme, colors } = useTheme(); + const isDarkMode = scheme === 'dark'; + const dispatch = useDispatch(); + const [ready, setReady] = useState(false); + + useEffect(() => { + const init = async () => { + // Run both in parallel — neither depends on the other. + const [persisted] = await Promise.all([ + loadChatState(), + dispatch(loadAgents()), + ]); + if (persisted) { + dispatch(rehydrateChat(persisted)); + } + setReady(true); + }; + init(); + }, [dispatch]); + + const navTheme = isDarkMode + ? { + ...DarkTheme, + colors: { + ...DarkTheme.colors, + background: colors.background, + card: colors.card, + }, + } + : { + ...DefaultTheme, + colors: { + ...DefaultTheme.colors, + background: colors.background, + card: colors.card, + }, + }; + + const containerStyle = { backgroundColor: colors.background }; + + if (!ready) { + return ( + + + + ); + } + + return ( + <> +