├── .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 | 
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 |
151 |
152 |
153 | {messages.map((message, index) => (
154 |
158 |
159 |
160 |
161 |
162 |
{message.content}
163 |
164 |
{message.timestamp}
165 |
166 | ))}
167 | {isLoading && (
168 |
173 | )}
174 |
175 |
176 |
177 |
178 |
179 |
187 |
194 |
195 |
196 |
197 |
198 |
199 | );
200 | }
201 |
202 | export default App;
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --primary: #6366f1;
3 | --primary-dark: #4f46e5;
4 | --primary-light: #818cf8;
5 | --secondary: #8b5cf6;
6 | --accent: #a78bfa;
7 | --text-light: #ffffff;
8 | --text-dark: #1f2937;
9 | --text-muted: #6b7280;
10 | --bg-light: #ffffff;
11 | --bg-light-secondary: #f9fafb;
12 | --bg-dark: #0f172a;
13 | --bg-dark-secondary: #1e293b;
14 | --bg-dark-tertiary: #334155;
15 | --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
16 | --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
17 | --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
18 | --shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
19 | --shadow-primary: 0 10px 25px -5px rgba(99, 102, 241, 0.3);
20 | --border-radius: 16px;
21 | --border-radius-sm: 12px;
22 | --border-radius-lg: 24px;
23 | }
24 |
25 | * {
26 | margin: 0;
27 | padding: 0;
28 | box-sizing: border-box;
29 | }
30 |
31 | body, html {
32 | height: 100vh;
33 | width: 100vw;
34 | margin: 0;
35 | padding: 0;
36 | overflow: hidden;
37 | font-family: 'Inter', 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', sans-serif;
38 | -webkit-font-smoothing: antialiased;
39 | -moz-osx-font-smoothing: grayscale;
40 | }
41 |
42 | #root {
43 | height: 100vh;
44 | width: 100vw;
45 | }
46 |
47 | .app-container {
48 | width: 100vw;
49 | height: 100vh;
50 | display: flex;
51 | position: relative;
52 | overflow: hidden;
53 | }
54 |
55 | .app-container::before {
56 | content: '';
57 | position: absolute;
58 | top: 0;
59 | left: 0;
60 | right: 0;
61 | bottom: 0;
62 | background:
63 | radial-gradient(circle at 20% 50%, rgba(99, 102, 241, 0.1) 0%, transparent 50%),
64 | radial-gradient(circle at 80% 80%, rgba(139, 92, 246, 0.1) 0%, transparent 50%);
65 | pointer-events: none;
66 | z-index: 0;
67 | }
68 |
69 | .chat-container {
70 | flex: 1;
71 | display: flex;
72 | flex-direction: column;
73 | overflow: hidden;
74 | background: var(--bg-light);
75 | position: relative;
76 | z-index: 1;
77 | box-shadow: var(--shadow-xl);
78 | }
79 |
80 | .header {
81 | padding: 20px 24px;
82 | background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
83 | color: var(--text-light);
84 | display: flex;
85 | justify-content: space-between;
86 | align-items: center;
87 | box-shadow: var(--shadow-md);
88 | position: relative;
89 | z-index: 10;
90 | }
91 |
92 | .header::after {
93 | content: '';
94 | position: absolute;
95 | bottom: 0;
96 | left: 0;
97 | right: 0;
98 | height: 1px;
99 | background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
100 | }
101 |
102 | .logo {
103 | display: flex;
104 | align-items: center;
105 | gap: 14px;
106 | font-weight: 600;
107 | font-size: 1.25rem;
108 | letter-spacing: -0.02em;
109 | }
110 |
111 | .logo i {
112 | font-size: 28px;
113 | background: rgba(255, 255, 255, 0.2);
114 | width: 44px;
115 | height: 44px;
116 | border-radius: 12px;
117 | display: flex;
118 | align-items: center;
119 | justify-content: center;
120 | backdrop-filter: blur(10px);
121 | box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
122 | }
123 |
124 | .logo h1 {
125 | font-weight: 700;
126 | font-size: 1.5rem;
127 | background: linear-gradient(135deg, #ffffff 0%, rgba(255, 255, 255, 0.9) 100%);
128 | -webkit-background-clip: text;
129 | -webkit-text-fill-color: transparent;
130 | background-clip: text;
131 | }
132 |
133 | .theme-toggle {
134 | cursor: pointer;
135 | padding: 10px;
136 | border-radius: 12px;
137 | border: none;
138 | background: rgba(255, 255, 255, 0.15);
139 | color: white;
140 | transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
141 | width: 44px;
142 | height: 44px;
143 | display: flex;
144 | align-items: center;
145 | justify-content: center;
146 | backdrop-filter: blur(10px);
147 | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
148 | }
149 |
150 | .theme-toggle:hover {
151 | background: rgba(255, 255, 255, 0.25);
152 | transform: translateY(-2px);
153 | box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
154 | }
155 |
156 | .theme-toggle:active {
157 | transform: translateY(0);
158 | }
159 |
160 | .theme-toggle i {
161 | font-size: 18px;
162 | }
163 |
164 | .messages {
165 | flex: 1;
166 | overflow-y: auto;
167 | padding: 24px;
168 | display: flex;
169 | flex-direction: column;
170 | gap: 24px;
171 | background: var(--bg-light);
172 | scroll-behavior: smooth;
173 | }
174 |
175 | .messages::-webkit-scrollbar {
176 | width: 8px;
177 | }
178 |
179 | .messages::-webkit-scrollbar-track {
180 | background: transparent;
181 | }
182 |
183 | .messages::-webkit-scrollbar-thumb {
184 | background: rgba(0, 0, 0, 0.1);
185 | border-radius: 4px;
186 | }
187 |
188 | .messages::-webkit-scrollbar-thumb:hover {
189 | background: rgba(0, 0, 0, 0.2);
190 | }
191 |
192 | .message {
193 | display: flex;
194 | flex-direction: column;
195 | gap: 8px;
196 | max-width: 75%;
197 | animation: messageAppear 0.4s cubic-bezier(0.16, 1, 0.3, 1);
198 | }
199 |
200 | .message.bot {
201 | align-self: flex-start;
202 | }
203 |
204 | .message.user {
205 | align-self: flex-end;
206 | }
207 |
208 | .message-content {
209 | display: flex;
210 | gap: 12px;
211 | padding: 14px 18px;
212 | border-radius: var(--border-radius);
213 | position: relative;
214 | backdrop-filter: blur(10px);
215 | word-wrap: break-word;
216 | line-height: 1.6;
217 | }
218 |
219 | .message.bot .message-content {
220 | background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
221 | color: var(--text-light);
222 | border-bottom-left-radius: 4px;
223 | box-shadow: var(--shadow-primary);
224 | }
225 |
226 | .message.user .message-content {
227 | background: linear-gradient(135deg, var(--bg-dark-secondary) 0%, var(--bg-dark-tertiary) 100%);
228 | color: var(--text-light);
229 | border-bottom-right-radius: 4px;
230 | flex-direction: row-reverse;
231 | box-shadow: var(--shadow-lg);
232 | }
233 |
234 | .avatar {
235 | width: 40px;
236 | height: 40px;
237 | border-radius: 12px;
238 | background: rgba(255, 255, 255, 0.2);
239 | display: flex;
240 | align-items: center;
241 | justify-content: center;
242 | flex-shrink: 0;
243 | backdrop-filter: blur(10px);
244 | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
245 | }
246 |
247 | .avatar i {
248 | font-size: 18px;
249 | }
250 |
251 | .message .text {
252 | flex: 1;
253 | font-size: 0.95rem;
254 | line-height: 1.6;
255 | white-space: pre-wrap;
256 | word-wrap: break-word;
257 | overflow-wrap: break-word;
258 | }
259 |
260 | .timestamp {
261 | font-size: 0.7rem;
262 | color: var(--text-muted);
263 | opacity: 0.8;
264 | margin: 0 14px;
265 | font-weight: 500;
266 | letter-spacing: 0.02em;
267 | }
268 |
269 | .message.user .timestamp {
270 | text-align: right;
271 | }
272 |
273 | .input-container {
274 | padding: 20px 24px;
275 | background: var(--bg-light);
276 | border-top: 1px solid rgba(0, 0, 0, 0.06);
277 | position: relative;
278 | z-index: 10;
279 | }
280 |
281 | .input-wrapper {
282 | display: flex;
283 | gap: 12px;
284 | align-items: flex-end;
285 | background: var(--bg-light);
286 | border-radius: var(--border-radius-lg);
287 | padding: 12px 16px;
288 | box-shadow: var(--shadow-lg);
289 | border: 1px solid rgba(0, 0, 0, 0.05);
290 | transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
291 | }
292 |
293 | .input-wrapper:focus-within {
294 | box-shadow: var(--shadow-primary);
295 | border-color: var(--primary);
296 | transform: translateY(-2px);
297 | }
298 |
299 | textarea {
300 | flex: 1;
301 | border: none;
302 | outline: none;
303 | background: transparent;
304 | resize: none;
305 | max-height: 120px;
306 | font-size: 0.95rem;
307 | padding: 4px 0;
308 | min-height: 24px;
309 | line-height: 1.5;
310 | color: var(--text-dark);
311 | font-family: inherit;
312 | }
313 |
314 | textarea::placeholder {
315 | color: var(--text-muted);
316 | opacity: 0.6;
317 | }
318 |
319 | .send-button {
320 | background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
321 | color: white;
322 | border: none;
323 | border-radius: 12px;
324 | width: 44px;
325 | height: 44px;
326 | cursor: pointer;
327 | transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
328 | display: flex;
329 | align-items: center;
330 | justify-content: center;
331 | box-shadow: var(--shadow-md);
332 | flex-shrink: 0;
333 | }
334 |
335 | .send-button:hover:not(:disabled) {
336 | background: linear-gradient(135deg, var(--primary-dark) 0%, var(--secondary) 100%);
337 | transform: translateY(-2px) scale(1.05);
338 | box-shadow: var(--shadow-primary);
339 | }
340 |
341 | .send-button:active:not(:disabled) {
342 | transform: translateY(0) scale(0.98);
343 | }
344 |
345 | .send-button:disabled {
346 | opacity: 0.5;
347 | cursor: not-allowed;
348 | }
349 |
350 | .send-button i {
351 | font-size: 16px;
352 | }
353 |
354 | .loading-indicator {
355 | display: flex;
356 | gap: 6px;
357 | padding: 14px 18px;
358 | align-self: flex-start;
359 | background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
360 | border-radius: var(--border-radius);
361 | border-bottom-left-radius: 4px;
362 | box-shadow: var(--shadow-md);
363 | }
364 |
365 | .loading-dot {
366 | width: 10px;
367 | height: 10px;
368 | background: rgba(255, 255, 255, 0.9);
369 | border-radius: 50%;
370 | animation: bounce 1.4s infinite ease-in-out;
371 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
372 | }
373 |
374 | .loading-dot:nth-child(2) {
375 | animation-delay: 0.2s;
376 | }
377 |
378 | .loading-dot:nth-child(3) {
379 | animation-delay: 0.4s;
380 | }
381 |
382 | @keyframes bounce {
383 | 0%, 100% {
384 | transform: translateY(0) scale(1);
385 | opacity: 1;
386 | }
387 | 50% {
388 | transform: translateY(-8px) scale(1.1);
389 | opacity: 0.8;
390 | }
391 | }
392 |
393 | /* Dark Theme */
394 | .dark-theme {
395 | background: var(--bg-dark);
396 | height: 100vh;
397 | width: 100vw;
398 | }
399 |
400 | .dark-theme::before {
401 | background:
402 | radial-gradient(circle at 20% 50%, rgba(99, 102, 241, 0.15) 0%, transparent 50%),
403 | radial-gradient(circle at 80% 80%, rgba(139, 92, 246, 0.15) 0%, transparent 50%);
404 | }
405 |
406 | .dark-theme .chat-container {
407 | background: var(--bg-dark);
408 | }
409 |
410 | .dark-theme .messages {
411 | background: var(--bg-dark);
412 | }
413 |
414 | .dark-theme .messages::-webkit-scrollbar-thumb {
415 | background: rgba(255, 255, 255, 0.1);
416 | }
417 |
418 | .dark-theme .messages::-webkit-scrollbar-thumb:hover {
419 | background: rgba(255, 255, 255, 0.2);
420 | }
421 |
422 | .dark-theme .input-container {
423 | background: var(--bg-dark-secondary);
424 | border-top: 1px solid rgba(255, 255, 255, 0.1);
425 | }
426 |
427 | .dark-theme .input-wrapper {
428 | background: var(--bg-dark-tertiary);
429 | border-color: rgba(255, 255, 255, 0.1);
430 | }
431 |
432 | .dark-theme .input-wrapper:focus-within {
433 | border-color: var(--primary-light);
434 | }
435 |
436 | .dark-theme textarea {
437 | color: var(--text-light);
438 | }
439 |
440 | .dark-theme textarea::placeholder {
441 | color: rgba(255, 255, 255, 0.4);
442 | }
443 |
444 | .dark-theme .timestamp {
445 | color: rgba(255, 255, 255, 0.6);
446 | }
447 |
448 | .dark-theme .message.user .message-content {
449 | background: linear-gradient(135deg, var(--bg-dark-tertiary) 0%, var(--bg-dark-secondary) 100%);
450 | }
451 |
452 | /* Animations */
453 | @keyframes messageAppear {
454 | from {
455 | opacity: 0;
456 | transform: translateY(20px) scale(0.95);
457 | }
458 | to {
459 | opacity: 1;
460 | transform: translateY(0) scale(1);
461 | }
462 | }
463 |
464 | /* Responsive Design */
465 | @media (max-width: 768px) {
466 | .header {
467 | padding: 16px 20px;
468 | }
469 |
470 | .logo h1 {
471 | font-size: 1.25rem;
472 | }
473 |
474 | .logo i {
475 | width: 40px;
476 | height: 40px;
477 | font-size: 24px;
478 | }
479 |
480 | .messages {
481 | padding: 20px 16px;
482 | gap: 20px;
483 | }
484 |
485 | .message {
486 | max-width: 85%;
487 | }
488 |
489 | .message-content {
490 | padding: 12px 16px;
491 | }
492 |
493 | .input-container {
494 | padding: 16px 20px;
495 | }
496 |
497 | .input-wrapper {
498 | padding: 10px 14px;
499 | }
500 | }
--------------------------------------------------------------------------------