├── .env.example ├── .gitattributes ├── app.js ├── src ├── index.js ├── index.css ├── App.js └── App.css ├── .gitignore ├── public └── index.html ├── package.json ├── LICENSE ├── README.md └── style.css /.env.example: -------------------------------------------------------------------------------- 1 | REACT_APP_GEMINI_API_KEY=your_gemini_api_key_here -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | // App bileşeni önceden tanımlandı 2 | const container = document.getElementById('root'); 3 | const root = ReactDOM.createRoot(container); 4 | root.render(); -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import './index.css'; 4 | import App from './App'; 5 | 6 | const root = ReactDOM.createRoot(document.getElementById('root')); 7 | root.render( 8 | 9 | 10 | 11 | ); -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | /node_modules 3 | /.pnp 4 | .pnp.js 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | .env.local 16 | .env.development.local 17 | .env.test.local 18 | .env.production.local 19 | 20 | npm-debug.log* 21 | yarn-debug.log* 22 | yarn-error.log* -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | box-sizing: border-box; 5 | } 6 | 7 | body { 8 | height: 100vh; 9 | width: 100vw; 10 | margin: 0; 11 | padding: 0; 12 | font-family: 'Inter', 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', sans-serif; 13 | -webkit-font-smoothing: antialiased; 14 | -moz-osx-font-smoothing: grayscale; 15 | overflow: hidden; 16 | } 17 | 18 | #root { 19 | height: 100vh; 20 | width: 100vw; 21 | display: flex; 22 | } -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Recta AI - AI Chatbot 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ai-chatbot-react", 3 | "version": "1.0.0", 4 | "description": "Recta AI - AI Chatbot React Version", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "react-scripts start", 8 | "build": "react-scripts build", 9 | "test": "react-scripts test", 10 | "eject": "react-scripts eject" 11 | }, 12 | "dependencies": { 13 | "react": "^18.2.0", 14 | "react-dom": "^18.2.0", 15 | "react-scripts": "5.0.1" 16 | }, 17 | "devDependencies": { 18 | "@babel/plugin-proposal-private-property-in-object": "^7.21.11" 19 | }, 20 | "browserslist": { 21 | "production": [ 22 | ">0.2%", 23 | "not dead", 24 | "not op_mini all" 25 | ], 26 | "development": [ 27 | "last 1 chrome version", 28 | "last 1 firefox version", 29 | "last 1 safari version" 30 | ] 31 | } 32 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Recta AI - Intelligent Chatbot Interface 2 | 3 | A sophisticated, modern AI chatbot application built with React and powered by Google's Gemini AI. Recta AI delivers an elegant user experience with a polished interface, seamless real-time conversations, and comprehensive theme customization. 4 | 5 | ![Recta AI Screenshot](https://github.com/user-attachments/assets/94f4b236-3a38-45ed-8aef-e7c8da082d3a) 6 | 7 | ## ✨ Key Features 8 | 9 | - 🤖 **Advanced AI Integration** - Seamlessly powered by Google's Gemini AI for intelligent conversations 10 | - 🌓 **Theme Customization** - Elegant dark and light mode support with smooth transitions 11 | - 💬 **Real-Time Chat** - Instant messaging interface with fluid user interactions 12 | - ⌨️ **Smart Input Field** - Auto-resizing text area that adapts to your content 13 | - 📱 **Responsive Design** - Optimized for all devices, from mobile to desktop 14 | - ⚡ **High Performance** - Lightweight and fast, ensuring smooth user experience 15 | - ⌛ **Visual Feedback** - Elegant loading indicators for better user engagement 16 | - 🕐 **Message History** - Timestamp tracking for all conversations 17 | 18 | ## 📋 Prerequisites 19 | 20 | To run this application, you'll need: 21 | 22 | - **Node.js** - Version 14 or higher 23 | - **npm** - Node Package Manager (comes with Node.js) 24 | - **Gemini API Key** - Obtain from [Google AI Studio](https://makersuite.google.com/app/apikey) 25 | 26 | ## 🚀 Getting Started 27 | 28 | Follow these steps to set up and run Recta AI on your local machine: 29 | 30 | ### 1. Clone the Repository 31 | 32 | ```bash 33 | git clone https://github.com/hasan4adnan/Reacta-AI 34 | cd Reacta-AI 35 | ``` 36 | 37 | ### 2. Install Dependencies 38 | 39 | ```bash 40 | npm install 41 | ``` 42 | 43 | ### 3. Configure API Key 44 | 45 | 1. Visit [Google AI Studio](https://makersuite.google.com/app/apikey) to generate your API key 46 | 2. Create a `.env` file in the root directory (you may need to copy from `.env.example` if available) 47 | 3. Add your API key to the `.env` file: 48 | 49 | ```env 50 | REACT_APP_GEMINI_API_KEY=your_api_key_here 51 | ``` 52 | 53 | ### 4. Launch the Application 54 | 55 | ```bash 56 | npm start 57 | ``` 58 | 59 | The application will automatically open in your default browser at `http://localhost:3000`. 60 | 61 | ## 📄 License 62 | 63 | This project is licensed under the MIT License - see the LICENSE file for details. 64 | 65 | ## 👨‍💻 Developer 66 | 67 | **Hasan Adnan** 68 | 69 | - 💼 LinkedIn: [linkedin.com/in/hasan-adnan-6091292bb](https://www.linkedin.com/in/hasan-adnan-6091292bb) 70 | - 📧 Email: hassanmoaid44@gmail.com 71 | 72 | --- 73 | 74 | Built with ❤️ using React and Google Gemini AI 75 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --primary: #8B5DFF; 3 | --secondary: #6A42C2; 4 | --accent: #563A9C; 5 | --text-light: #FFF7D1; 6 | --text-dark: #2C2C2C; 7 | --bg-light: #FFFFFF; 8 | --bg-dark: #1a1a2e; 9 | --shadow: rgba(106, 66, 194, 0.1); 10 | } 11 | 12 | * { 13 | margin: 0; 14 | padding: 0; 15 | box-sizing: border-box; 16 | font-family: 'Poppins', sans-serif; 17 | } 18 | 19 | body { 20 | height: 100vh; 21 | display: flex; 22 | } 23 | 24 | #root { 25 | flex: 1; 26 | display: flex; 27 | } 28 | 29 | .app-container { 30 | flex: 1; 31 | display: flex; 32 | background-color: var(--bg-light); 33 | } 34 | 35 | .chat-container { 36 | flex: 1; 37 | display: flex; 38 | flex-direction: column; 39 | overflow: hidden; 40 | } 41 | 42 | .header { 43 | padding: 20px; 44 | background: var(--primary); 45 | color: var(--text-light); 46 | display: flex; 47 | justify-content: space-between; 48 | align-items: center; 49 | } 50 | 51 | .logo { 52 | display: flex; 53 | align-items: center; 54 | gap: 12px; 55 | } 56 | 57 | .logo i { 58 | font-size: 24px; 59 | } 60 | 61 | .theme-toggle { 62 | cursor: pointer; 63 | padding: 8px; 64 | border-radius: 50%; 65 | transition: background-color 0.3s; 66 | } 67 | 68 | .theme-toggle:hover { 69 | background-color: rgba(255, 255, 255, 0.1); 70 | } 71 | 72 | .messages { 73 | flex: 1; 74 | overflow-y: auto; 75 | padding: 20px; 76 | display: flex; 77 | flex-direction: column; 78 | gap: 20px; 79 | } 80 | 81 | .message { 82 | display: flex; 83 | flex-direction: column; 84 | gap: 5px; 85 | max-width: 80%; 86 | animation: messageAppear 0.3s ease-out; 87 | } 88 | 89 | .message.bot { 90 | align-self: flex-start; 91 | } 92 | 93 | .message.user { 94 | align-self: flex-end; 95 | } 96 | 97 | .message-content { 98 | display: flex; 99 | gap: 12px; 100 | padding: 12px 16px; 101 | border-radius: 15px; 102 | position: relative; 103 | } 104 | 105 | .message.bot .message-content { 106 | background: var(--primary); 107 | color: var(--text-light); 108 | border-bottom-left-radius: 5px; 109 | } 110 | 111 | .message.user .message-content { 112 | background: var(--secondary); 113 | color: var(--text-light); 114 | border-bottom-right-radius: 5px; 115 | flex-direction: row-reverse; 116 | } 117 | 118 | .avatar { 119 | width: 35px; 120 | height: 35px; 121 | border-radius: 50%; 122 | background: rgba(255, 255, 255, 0.1); 123 | display: flex; 124 | align-items: center; 125 | justify-content: center; 126 | } 127 | 128 | .timestamp { 129 | font-size: 0.75rem; 130 | color: var(--text-dark); 131 | opacity: 0.7; 132 | margin: 0 12px; 133 | } 134 | 135 | .user .timestamp { 136 | text-align: right; 137 | } 138 | 139 | .input-container { 140 | padding: 20px; 141 | background: var(--bg-light); 142 | border-top: 1px solid rgba(0, 0, 0, 0.1); 143 | } 144 | 145 | .input-wrapper { 146 | display: flex; 147 | gap: 10px; 148 | align-items: center; 149 | background: var(--bg-light); 150 | border-radius: 15px; 151 | padding: 8px 16px; 152 | box-shadow: 0 2px 6px var(--shadow); 153 | } 154 | 155 | .input-wrapper textarea { 156 | flex: 1; 157 | border: none; 158 | outline: none; 159 | background: transparent; 160 | resize: none; 161 | max-height: 100px; 162 | font-size: 1rem; 163 | padding: 5px 0; 164 | } 165 | 166 | .send-button { 167 | background: var(--primary); 168 | color: var(--text-light); 169 | border: none; 170 | border-radius: 50%; 171 | width: 40px; 172 | height: 40px; 173 | cursor: pointer; 174 | transition: all 0.2s; 175 | display: flex; 176 | align-items: center; 177 | justify-content: center; 178 | } 179 | 180 | .send-button:hover { 181 | background: var(--secondary); 182 | transform: scale(1.05); 183 | } 184 | 185 | .send-button:active { 186 | transform: scale(0.95); 187 | } 188 | 189 | .loading-indicator { 190 | display: flex; 191 | gap: 5px; 192 | padding: 8px; 193 | align-self: flex-start; 194 | } 195 | 196 | .loading-dot { 197 | width: 8px; 198 | height: 8px; 199 | background: var(--primary); 200 | border-radius: 50%; 201 | animation: bounce 1s infinite; 202 | } 203 | 204 | .loading-dot:nth-child(2) { 205 | animation-delay: 0.2s; 206 | } 207 | 208 | .loading-dot:nth-child(3) { 209 | animation-delay: 0.4s; 210 | } 211 | 212 | @keyframes bounce { 213 | 0%, 100% { 214 | transform: translateY(0); 215 | } 216 | 50% { 217 | transform: translateY(-5px); 218 | } 219 | } 220 | 221 | @keyframes messageAppear { 222 | from { 223 | opacity: 0; 224 | transform: translateY(20px); 225 | } 226 | to { 227 | opacity: 1; 228 | transform: translateY(0); 229 | } 230 | } 231 | 232 | /* Dark Theme */ 233 | .dark-theme { 234 | background: var(--bg-dark); 235 | color: var(--text-light); 236 | } 237 | 238 | .dark-theme .chat-container { 239 | background: var(--bg-dark); 240 | } 241 | 242 | .dark-theme .input-container { 243 | background: var(--bg-dark); 244 | border-top-color: rgba(255, 255, 255, 0.1); 245 | } 246 | 247 | .dark-theme .input-wrapper { 248 | background: rgba(255, 255, 255, 0.05); 249 | } 250 | 251 | .dark-theme textarea { 252 | color: var(--text-light); 253 | } 254 | 255 | .dark-theme .timestamp { 256 | color: rgba(255, 255, 255, 0.7); 257 | } -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useRef, useEffect } from 'react'; 2 | import './App.css'; 3 | 4 | function App() { 5 | const [messages, setMessages] = useState([{ 6 | type: 'bot', 7 | content: 'Hello! I am Recta, your AI companion. How can I assist you today? 😊', 8 | timestamp: new Date().toLocaleTimeString('tr-TR', { 9 | hour: '2-digit', 10 | minute: '2-digit' 11 | }) 12 | }]); 13 | const [inputMessage, setInputMessage] = useState(''); 14 | const [isDarkMode, setIsDarkMode] = useState(false); 15 | const [isLoading, setIsLoading] = useState(false); 16 | const messagesEndRef = useRef(null); 17 | const textareaRef = useRef(null); 18 | 19 | useEffect(() => { 20 | scrollToBottom(); 21 | }, [messages]); 22 | 23 | useEffect(() => { 24 | textareaRef.current?.focus(); 25 | }, []); 26 | 27 | const scrollToBottom = () => { 28 | messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); 29 | }; 30 | 31 | const adjustTextareaHeight = () => { 32 | const textarea = textareaRef.current; 33 | if (textarea) { 34 | textarea.style.height = 'auto'; 35 | textarea.style.height = Math.min(textarea.scrollHeight, 100) + 'px'; 36 | } 37 | }; 38 | 39 | const handleInputChange = (e) => { 40 | setInputMessage(e.target.value); 41 | adjustTextareaHeight(); 42 | }; 43 | 44 | const handleKeyPress = (e) => { 45 | if (e.key === 'Enter' && !e.shiftKey) { 46 | e.preventDefault(); 47 | handleSendMessage(); 48 | } 49 | }; 50 | 51 | const toggleTheme = () => { 52 | setIsDarkMode(!isDarkMode); 53 | }; 54 | 55 | const handleSendMessage = async () => { 56 | if (!inputMessage.trim()) return; 57 | 58 | const newMessage = { 59 | type: 'user', 60 | content: inputMessage.trim(), 61 | timestamp: new Date().toLocaleTimeString('tr-TR', { 62 | hour: '2-digit', 63 | minute: '2-digit' 64 | }) 65 | }; 66 | 67 | setMessages(prev => [...prev, newMessage]); 68 | setInputMessage(''); 69 | setIsLoading(true); 70 | 71 | if (textareaRef.current) { 72 | textareaRef.current.style.height = 'auto'; 73 | } 74 | 75 | if (!process.env.REACT_APP_GEMINI_API_KEY) { 76 | setTimeout(() => { 77 | setIsLoading(false); 78 | const botResponse = { 79 | type: 'bot', 80 | content: 'API key is not configured. Please follow the steps in the README file to add your API key.', 81 | timestamp: new Date().toLocaleTimeString('tr-TR', { 82 | hour: '2-digit', 83 | minute: '2-digit' 84 | }) 85 | }; 86 | setMessages(prev => [...prev, botResponse]); 87 | }, 1000); 88 | return; 89 | } 90 | 91 | try { 92 | const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key=${process.env.REACT_APP_GEMINI_API_KEY}`, { 93 | method: 'POST', 94 | headers: { 95 | 'Content-Type': 'application/json', 96 | }, 97 | body: JSON.stringify({ 98 | contents: [{ 99 | parts: [{ 100 | text: inputMessage.trim() 101 | }] 102 | }] 103 | }) 104 | }); 105 | 106 | const data = await response.json(); 107 | setIsLoading(false); 108 | 109 | if (data.candidates && data.candidates[0].content.parts[0].text) { 110 | const botResponse = { 111 | type: 'bot', 112 | content: data.candidates[0].content.parts[0].text, 113 | timestamp: new Date().toLocaleTimeString('tr-TR', { 114 | hour: '2-digit', 115 | minute: '2-digit' 116 | }) 117 | }; 118 | setMessages(prev => [...prev, botResponse]); 119 | } 120 | } catch (error) { 121 | console.error('Error:', error); 122 | setIsLoading(false); 123 | const errorMessage = { 124 | type: 'bot', 125 | content: 'Sorry, an error occurred. Please try again.', 126 | timestamp: new Date().toLocaleTimeString('tr-TR', { 127 | hour: '2-digit', 128 | minute: '2-digit' 129 | }) 130 | }; 131 | setMessages(prev => [...prev, errorMessage]); 132 | } 133 | }; 134 | 135 | return ( 136 |
137 |
138 |
139 |
140 |
141 | 142 |

Recta AI

143 |
144 | 150 |
151 | 152 |
153 | {messages.map((message, index) => ( 154 |
158 |
159 |
160 | 161 |
162 |
{message.content}
163 |
164 |
{message.timestamp}
165 |
166 | ))} 167 | {isLoading && ( 168 |
169 |
170 |
171 |
172 |
173 | )} 174 |
175 |
176 | 177 |
178 |
179 |