Krish-05 commited on
Commit
638c726
·
unverified ·
1 Parent(s): 26263e6

added all css in one file

Browse files
frontend/src/App.css CHANGED
@@ -1,23 +0,0 @@
1
- /* frontend/src/App.css */
2
- @tailwind base;
3
- @tailwind components;
4
- @tailwind utilities;
5
-
6
- /* Optional: Basic body styling */
7
- body {
8
- margin: 0;
9
- font-family: 'Inter', "Noto Sans", sans-serif;
10
- -webkit-font-smoothing: antialiased;
11
- -moz-osx-font-smoothing: grayscale;
12
- }
13
-
14
- /* Hide scrollbar for webkit browsers */
15
- .hide-scrollbar::-webkit-scrollbar {
16
- display: none;
17
- }
18
-
19
- /* For IE, Edge and Firefox */
20
- .hide-scrollbar {
21
- -ms-overflow-style: none; /* IE and Edge */
22
- scrollbar-width: none; /* Firefox */
23
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/src/components/chat/ChatInputArea.jsx CHANGED
@@ -1,11 +1,11 @@
1
- // frontend/src/components/chat/ChatInputArea.jsx
2
  import React, { useState, useEffect } from 'react';
3
  import { VoiceVisualizer, useVoiceVisualizer } from 'react-voice-visualizer';
4
 
5
  const ChatInputArea = ({ onSendMessage, onSendVoiceMessage, isLoading }) => {
6
  const [message, setMessage] = useState('');
7
 
8
- const BASE_URL = '/api';
 
9
 
10
  const voiceVisualizerHook = useVoiceVisualizer({});
11
 
@@ -36,7 +36,7 @@ const ChatInputArea = ({ onSendMessage, onSendVoiceMessage, isLoading }) => {
36
  };
37
 
38
  const handleStartRecording = () => {
39
- if (isLoading) return;
40
  startRecording();
41
  console.log("Recording started...");
42
  };
@@ -83,12 +83,15 @@ const ChatInputArea = ({ onSendMessage, onSendVoiceMessage, isLoading }) => {
83
  </svg>
84
  )}
85
  </button>
86
- <VoiceVisualizer
87
- className="sound-wave"
88
- mainBarColor="#4CAF50"
89
- secondaryBarColor="#81C784"
90
- hook={voiceVisualizerHook}
91
- />
 
 
 
92
  </div>
93
  </form>
94
  </div>
 
 
1
  import React, { useState, useEffect } from 'react';
2
  import { VoiceVisualizer, useVoiceVisualizer } from 'react-voice-visualizer';
3
 
4
  const ChatInputArea = ({ onSendMessage, onSendVoiceMessage, isLoading }) => {
5
  const [message, setMessage] = useState('');
6
 
7
+ // BASE_URL is not used here, so it can be removed if not needed elsewhere
8
+ // const BASE_URL = '/api';
9
 
10
  const voiceVisualizerHook = useVoiceVisualizer({});
11
 
 
36
  };
37
 
38
  const handleStartRecording = () => {
39
+ if (isLoading) return; // Prevent recording if an action is already in progress
40
  startRecording();
41
  console.log("Recording started...");
42
  };
 
83
  </svg>
84
  )}
85
  </button>
86
+ {/* Only show visualizer if recording or if there's audio data to show */}
87
+ {isRecording && (
88
+ <VoiceVisualizer
89
+ className="sound-wave"
90
+ mainBarColor="#4CAF50"
91
+ secondaryBarColor="#81C784"
92
+ hook={voiceVisualizerHook}
93
+ />
94
+ )}
95
  </div>
96
  </form>
97
  </div>
frontend/src/components/chat/ChatInterface.css DELETED
@@ -1,172 +0,0 @@
1
- /* General Layout */
2
- .chat-layout {
3
- display: flex;
4
- height: 100vh;
5
- overflow: hidden; /* Prevent body scroll if content overflows */
6
- }
7
-
8
- .chat-main {
9
- flex-grow: 1;
10
- display: flex;
11
- flex-direction: column;
12
- justify-content: space-between;
13
- background-color: #f9f9f9;
14
- }
15
-
16
- /* Chat Messages Area */
17
- .chat-messages {
18
- flex-grow: 1;
19
- overflow-y: auto; /* Enable scrolling for messages */
20
- padding: 20px;
21
- display: flex;
22
- flex-direction: column;
23
- gap: 15px; /* Spacing between message wrappers */
24
- }
25
-
26
- /* Empty Chat Placeholder */
27
- .empty-chat-placeholder {
28
- text-align: center;
29
- padding: 50px;
30
- color: #666;
31
- flex-grow: 1; /* Pushes content to the bottom if few messages */
32
- display: flex;
33
- flex-direction: column;
34
- justify-content: center;
35
- align-items: center;
36
- }
37
- .empty-chat-placeholder h1 {
38
- font-size: 2.5em;
39
- margin-bottom: 10px;
40
- color: #333;
41
- }
42
- .empty-chat-placeholder p {
43
- font-size: 1.1em;
44
- line-height: 1.6;
45
- }
46
-
47
- /* Message Wrapper (contains avatar and bubble) */
48
- .message-wrapper {
49
- display: flex; /* Use flexbox for layout of avatar and bubble */
50
- align-items: flex-start; /* Align items to the top */
51
- max-width: 100%; /* Ensure it doesn't overflow */
52
- }
53
-
54
- /* Specific styles for user messages (align to right) */
55
- .message-wrapper.user {
56
- justify-content: flex-end; /* Push user messages to the right */
57
- flex-direction: row-reverse; /* Put avatar on the right for user */
58
- }
59
-
60
- /* Chat Avatar Styling */
61
- .chat-avatar {
62
- font-size: 24px; /* Still useful for user emoji */
63
- padding: 8px;
64
- border-radius: 50%;
65
- background-color: #e0e0e0; /* Default background for avatar circle */
66
- min-width: 40px;
67
- height: 40px;
68
- display: flex;
69
- justify-content: center;
70
- align-items: center;
71
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
72
- /* Margins for spacing */
73
- margin-right: 10px; /* Default for assistant avatar */
74
- overflow: hidden; /* Crucial to clip image if it's larger than the circle */
75
- }
76
-
77
- /* Adjust margin for user avatar (since row-reverse) */
78
- .message-wrapper.user .chat-avatar {
79
- margin-left: 10px;
80
- margin-right: 0;
81
- }
82
-
83
- /* Style for the actual image inside the avatar div */
84
- .chat-avatar .avatar-image {
85
- width: 100%; /* Make image fill the avatar div */
86
- height: 100%; /* Make image fill the avatar div */
87
- object-fit: cover; /* Crop and cover the area without distortion */
88
- border-radius: 50%; /* Ensure the image itself is also rounded */
89
- }
90
-
91
- /* Message Bubble Styling */
92
- .message-bubble {
93
- background-color: #f0f0f0;
94
- padding: 12px 18px;
95
- border-radius: 20px;
96
- max-width: 70%; /* Limit message width */
97
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
98
-
99
- /* --- THE KEY CSS PROPERTY FOR NEWLINES --- */
100
- white-space: pre-wrap; /* This will make '\n' characters create new lines */
101
- /* --- END KEY CSS PROPERTY --- */
102
-
103
- word-break: break-word; /* Ensure long words break within the bubble */
104
- overflow-wrap: break-word; /* Modern equivalent */
105
- }
106
-
107
- /* Specific styling for assistant bubbles */
108
- .message-wrapper.assistant .message-bubble {
109
- background-color: #e6f7ff; /* Light blue for assistant */
110
- border-bottom-left-radius: 5px; /* Pointy bottom-left for assistant */
111
- margin-right: auto; /* Push to left, next to sidebar */
112
- }
113
-
114
- /* Specific styling for user bubbles */
115
- .message-wrapper.user .message-bubble {
116
- background-color: #d1e7dd; /* Light green for user */
117
- border-bottom-right-radius: 5px; /* Pointy bottom-right for user */
118
- margin-left: auto; /* Push to right */
119
- }
120
-
121
- /* Chat Input Area */
122
- .chat-input-area {
123
- padding: 20px;
124
- border-top: 1px solid #eee;
125
- background-color: #fff;
126
- }
127
-
128
- .chat-form {
129
- display: flex;
130
- gap: 10px;
131
- }
132
-
133
- .chat-form input[type="text"] {
134
- flex-grow: 1;
135
- padding: 12px;
136
- border: 1px solid #ccc;
137
- border-radius: 25px;
138
- font-size: 16px;
139
- outline: none;
140
- }
141
-
142
- .chat-form button {
143
- padding: 12px 20px;
144
- background-color: #007bff;
145
- color: white;
146
- border: none;
147
- border-radius: 25px;
148
- cursor: pointer;
149
- font-size: 16px;
150
- transition: background-color 0.2s;
151
- }
152
-
153
- .chat-form button:hover:not(:disabled) {
154
- background-color: #0056b3;
155
- }
156
-
157
- .chat-form button:disabled {
158
- background-color: #a0a0a0;
159
- cursor: not-allowed;
160
- }
161
-
162
- /* Blinking Cursor */
163
- .blinking-cursor {
164
- animation: blink 1s step-end infinite;
165
- display: inline-block; /* Essential for it to be visible next to text */
166
- width: 0.5em; /* Give it a small width */
167
- }
168
-
169
- @keyframes blink {
170
- from, to { opacity: 1; }
171
- 50% { opacity: 0; }
172
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/src/components/chat/ChatInterface.jsx CHANGED
@@ -15,20 +15,24 @@ const ChatInterface = () => {
15
  const FASTAPI_TRANSCRIBE_URL = '/api/transcribe-audio'; // Endpoint for audio transcription
16
 
17
  useEffect(() => {
 
18
  chatEndRef.current?.scrollIntoView({ behavior: 'smooth' });
19
  }, [chatHistory]);
20
 
21
  const handleNewChat = () => {
 
22
  if (auth.currentUser && chatHistory.length > 0) {
23
  saveChatHistory(chatHistory);
24
  }
25
- setChatHistory([]);
26
  };
27
 
28
  const handleSendMessage = async (userPrompt) => {
29
- if (!userPrompt.trim() || isSending) return;
30
 
31
  const userMessage = { role: 'user', message: userPrompt.trim(), timestamp: new Date() };
 
 
32
  setChatHistory(prev => [
33
  ...prev,
34
  userMessage,
@@ -40,9 +44,11 @@ const ChatInterface = () => {
40
  let assistantMessageTimestamp = new Date();
41
  let assistantMessageIndex = -1;
42
 
 
43
  setChatHistory(prev => {
44
  const updated = [...prev];
45
  assistantMessageIndex = updated.findIndex(msg => msg.role === 'assistant' && msg.streaming);
 
46
  if (assistantMessageIndex === -1) {
47
  assistantMessageIndex = updated.length - 1;
48
  }
@@ -69,19 +75,22 @@ const ChatInterface = () => {
69
  const { value, done } = await reader.read();
70
  if (done) {
71
  console.log("Stream finished.");
72
- processPartialBuffer(buffer);
 
73
  break;
74
  }
75
  const decodedChunk = decoder.decode(value, { stream: true });
76
  buffer += decodedChunk;
77
 
 
78
  const events = buffer.split('\n\n');
79
- buffer = events.pop() || '';
80
 
81
  for (const eventString of events) {
82
  processPartialBuffer(eventString);
83
  }
84
 
 
85
  setChatHistory(prev => {
86
  const updated = [...prev];
87
  const targetMessage = updated[assistantMessageIndex];
@@ -115,28 +124,32 @@ const ChatInterface = () => {
115
  } catch (err) {
116
  console.error('General Fetch/Stream Error:', err);
117
  currentFullResponse += `\nSorry, something went wrong. Please try again. [Error: ${err.message}]`;
 
118
  setChatHistory(prev => {
119
  const updated = [...prev];
120
  const targetMessage = updated[assistantMessageIndex];
121
  if (targetMessage) {
122
  targetMessage.message = currentFullResponse;
123
- targetMessage.timestamp = new Date();
124
  targetMessage.streaming = false;
125
  } else {
 
126
  updated.push({ role: 'assistant', message: currentFullResponse, timestamp: new Date(), streaming: false });
127
  }
128
  return updated;
129
  });
130
  } finally {
131
  setIsSending(false);
 
132
  setChatHistory(prev => {
133
  const updated = [...prev];
134
  const targetMessage = updated[assistantMessageIndex];
135
  if (targetMessage && targetMessage.role === 'assistant') {
136
  targetMessage.message = currentFullResponse;
137
- targetMessage.timestamp = assistantMessageTimestamp;
138
  targetMessage.streaming = false;
139
  }
 
140
  if (!targetMessage || targetMessage.message === '') {
141
  const fallbackMessage = "Sorry, an unexpected error occurred.";
142
  updated.push({ role: 'assistant', message: fallbackMessage, timestamp: new Date(), streaming: false });
@@ -144,15 +157,17 @@ const ChatInterface = () => {
144
  return updated;
145
  });
146
 
 
147
  if (auth.currentUser) {
148
  setChatHistory(finalChatState => {
149
  const chatToSave = [...finalChatState];
150
  const lastMessageForSave = chatToSave.at(-1);
 
151
  if (lastMessageForSave && lastMessageForSave.role === 'assistant' && lastMessageForSave.message.endsWith('▌')) {
152
  lastMessageForSave.message = lastMessageForSave.message.slice(0, -1);
153
  }
154
  saveChatHistory(chatToSave);
155
- return finalChatState;
156
  });
157
  }
158
  }
@@ -186,24 +201,30 @@ const ChatInterface = () => {
186
  if (transcriptionResult) {
187
  // Update the user's "Recording audio..." message with the actual transcription
188
  setChatHistory(prev => {
189
- const updated = prev.filter(msg => msg.message !== '(Recording audio...)'); // Remove placeholder
190
- return [...updated, { role: 'user', message: transcriptionResult, timestamp: new Date() }];
 
 
 
 
 
191
  });
 
192
  // Now, send the transcribed text to your LLM
193
  await handleSendMessage(transcriptionResult);
194
  } else {
195
  alert('Could not transcribe audio. Please try speaking clearer.');
196
  // Remove transcription messages if empty
197
- setChatHistory(prev => prev.filter(msg => msg.message !== '(Recording audio...)' && msg.message !== 'Transcribing...'));
198
  }
199
  } catch (error) {
200
  console.error('Error during voice transcription:', error);
201
  alert(`Failed to transcribe audio. Error: ${error.message || 'Unknown error'}`);
202
  // Remove transcription messages if error
203
- setChatHistory(prev => prev.filter(msg => msg.message !== '(Recording audio...)' && msg.message !== 'Transcribing...'));
204
  } finally {
205
  setIsSending(false);
206
- // Ensure the "Transcribing..." message is removed
207
  setChatHistory(prev => prev.filter(msg => !(msg.role === 'assistant' && msg.message === 'Transcribing...')));
208
  }
209
  };
@@ -239,7 +260,7 @@ const ChatInterface = () => {
239
  </div>
240
  ))
241
  )}
242
- <div ref={chatEndRef} />
243
  </div>
244
  <ChatInputArea
245
  onSendMessage={handleSendMessage}
 
15
  const FASTAPI_TRANSCRIBE_URL = '/api/transcribe-audio'; // Endpoint for audio transcription
16
 
17
  useEffect(() => {
18
+ // Scroll to bottom when chat history changes
19
  chatEndRef.current?.scrollIntoView({ behavior: 'smooth' });
20
  }, [chatHistory]);
21
 
22
  const handleNewChat = () => {
23
+ // Save current chat history before starting a new one, if user is logged in
24
  if (auth.currentUser && chatHistory.length > 0) {
25
  saveChatHistory(chatHistory);
26
  }
27
+ setChatHistory([]); // Clear chat history for a new conversation
28
  };
29
 
30
  const handleSendMessage = async (userPrompt) => {
31
+ if (!userPrompt.trim() || isSending) return; // Prevent empty messages or sending when busy
32
 
33
  const userMessage = { role: 'user', message: userPrompt.trim(), timestamp: new Date() };
34
+
35
+ // Add user message and a placeholder for assistant's response
36
  setChatHistory(prev => [
37
  ...prev,
38
  userMessage,
 
44
  let assistantMessageTimestamp = new Date();
45
  let assistantMessageIndex = -1;
46
 
47
+ // Find the index of the streaming assistant message to update it
48
  setChatHistory(prev => {
49
  const updated = [...prev];
50
  assistantMessageIndex = updated.findIndex(msg => msg.role === 'assistant' && msg.streaming);
51
+ // Fallback in case the initial streaming message isn't found (shouldn't happen with the prev logic)
52
  if (assistantMessageIndex === -1) {
53
  assistantMessageIndex = updated.length - 1;
54
  }
 
75
  const { value, done } = await reader.read();
76
  if (done) {
77
  console.log("Stream finished.");
78
+ // Process any remaining buffer content
79
+ processPartialBuffer(buffer);
80
  break;
81
  }
82
  const decodedChunk = decoder.decode(value, { stream: true });
83
  buffer += decodedChunk;
84
 
85
+ // Process SSE events (split by \n\n)
86
  const events = buffer.split('\n\n');
87
+ buffer = events.pop() || ''; // Keep incomplete event in buffer
88
 
89
  for (const eventString of events) {
90
  processPartialBuffer(eventString);
91
  }
92
 
93
+ // Update UI with current streamed response
94
  setChatHistory(prev => {
95
  const updated = [...prev];
96
  const targetMessage = updated[assistantMessageIndex];
 
124
  } catch (err) {
125
  console.error('General Fetch/Stream Error:', err);
126
  currentFullResponse += `\nSorry, something went wrong. Please try again. [Error: ${err.message}]`;
127
+ // Update the message immediately on error
128
  setChatHistory(prev => {
129
  const updated = [...prev];
130
  const targetMessage = updated[assistantMessageIndex];
131
  if (targetMessage) {
132
  targetMessage.message = currentFullResponse;
133
+ targetMessage.timestamp = new Date(); // Update timestamp for error message
134
  targetMessage.streaming = false;
135
  } else {
136
+ // Add a new message if the streaming placeholder wasn't correctly identified
137
  updated.push({ role: 'assistant', message: currentFullResponse, timestamp: new Date(), streaming: false });
138
  }
139
  return updated;
140
  });
141
  } finally {
142
  setIsSending(false);
143
+ // Final update to remove blinking cursor and ensure timestamp is set
144
  setChatHistory(prev => {
145
  const updated = [...prev];
146
  const targetMessage = updated[assistantMessageIndex];
147
  if (targetMessage && targetMessage.role === 'assistant') {
148
  targetMessage.message = currentFullResponse;
149
+ targetMessage.timestamp = assistantMessageTimestamp; // Use the initial timestamp
150
  targetMessage.streaming = false;
151
  }
152
+ // If for some reason the message is empty after streaming, add a fallback
153
  if (!targetMessage || targetMessage.message === '') {
154
  const fallbackMessage = "Sorry, an unexpected error occurred.";
155
  updated.push({ role: 'assistant', message: fallbackMessage, timestamp: new Date(), streaming: false });
 
157
  return updated;
158
  });
159
 
160
+ // Save chat history after the conversation round is complete
161
  if (auth.currentUser) {
162
  setChatHistory(finalChatState => {
163
  const chatToSave = [...finalChatState];
164
  const lastMessageForSave = chatToSave.at(-1);
165
+ // Remove the blinking cursor if it somehow persists
166
  if (lastMessageForSave && lastMessageForSave.role === 'assistant' && lastMessageForSave.message.endsWith('▌')) {
167
  lastMessageForSave.message = lastMessageForSave.message.slice(0, -1);
168
  }
169
  saveChatHistory(chatToSave);
170
+ return finalChatState; // Return original state to avoid re-rendering
171
  });
172
  }
173
  }
 
201
  if (transcriptionResult) {
202
  // Update the user's "Recording audio..." message with the actual transcription
203
  setChatHistory(prev => {
204
+ // Filter out the "Recording audio..." placeholder message
205
+ const updated = prev.filter(msg => !(msg.role === 'user' && msg.message === '(Recording audio...)'));
206
+ // Add the actual transcribed user message
207
+ const userMsg = { role: 'user', message: transcriptionResult, timestamp: new Date() };
208
+ // Filter out the "Transcribing..." assistant message
209
+ const filteredAssistantMsgs = updated.filter(msg => !(msg.role === 'assistant' && msg.message === 'Transcribing...'));
210
+ return [...filteredAssistantMsgs, userMsg];
211
  });
212
+
213
  // Now, send the transcribed text to your LLM
214
  await handleSendMessage(transcriptionResult);
215
  } else {
216
  alert('Could not transcribe audio. Please try speaking clearer.');
217
  // Remove transcription messages if empty
218
+ setChatHistory(prev => prev.filter(msg => !(msg.role === 'user' && msg.message === '(Recording audio...)') && !(msg.role === 'assistant' && msg.message === 'Transcribing...')));
219
  }
220
  } catch (error) {
221
  console.error('Error during voice transcription:', error);
222
  alert(`Failed to transcribe audio. Error: ${error.message || 'Unknown error'}`);
223
  // Remove transcription messages if error
224
+ setChatHistory(prev => prev.filter(msg => !(msg.role === 'user' && msg.message === '(Recording audio...)') && !(msg.role === 'assistant' && msg.message === 'Transcribing...')));
225
  } finally {
226
  setIsSending(false);
227
+ // Ensure the "Transcribing..." message is always removed when done
228
  setChatHistory(prev => prev.filter(msg => !(msg.role === 'assistant' && msg.message === 'Transcribing...')));
229
  }
230
  };
 
260
  </div>
261
  ))
262
  )}
263
+ <div ref={chatEndRef} /> {/* Ref for scrolling to the bottom */}
264
  </div>
265
  <ChatInputArea
266
  onSendMessage={handleSendMessage}
frontend/src/components/chat/History.css DELETED
@@ -1,152 +0,0 @@
1
- /* History.css */
2
-
3
- .chat-layout {
4
- display: flex;
5
- height: 100vh;
6
- /* Assuming chat-layout takes full viewport height */
7
- }
8
-
9
- .chat-main {
10
- flex-grow: 1;
11
- padding: 20px;
12
- background-color: #f0f2f5;
13
- overflow-y: auto; /* Enable scrolling for main content */
14
- }
15
-
16
- .history-title {
17
- color: #333;
18
- margin-bottom: 20px;
19
- font-size: 24px;
20
- }
21
-
22
- .history-list {
23
- list-style: none;
24
- padding: 0;
25
- }
26
-
27
- .history-item {
28
- background-color: #fff;
29
- border-radius: 8px;
30
- padding: 15px 20px;
31
- margin-bottom: 10px;
32
- display: flex;
33
- align-items: center;
34
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
35
- cursor: pointer;
36
- transition: background-color 0.2s ease-in-out;
37
- }
38
-
39
- .history-item:hover {
40
- background-color: #e9ecef;
41
- }
42
-
43
- .history-icon {
44
- font-size: 24px;
45
- margin-right: 15px;
46
- }
47
-
48
- .history-details {
49
- flex-grow: 1;
50
- }
51
-
52
- .history-prompt {
53
- font-weight: bold;
54
- color: #555;
55
- margin-bottom: 5px;
56
- }
57
-
58
- .history-date {
59
- font-size: 0.85em;
60
- color: #888;
61
- }
62
-
63
- /* Conversation View Specific Styles */
64
- .conversation-view {
65
- background-color: #fff;
66
- border-radius: 8px;
67
- padding: 20px;
68
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
69
- }
70
-
71
- .back-button {
72
- background-color: #007bff;
73
- color: white;
74
- border: none;
75
- padding: 10px 15px;
76
- border-radius: 5px;
77
- cursor: pointer;
78
- margin-bottom: 15px;
79
- font-size: 16px;
80
- transition: background-color 0.2s ease-in-out;
81
- }
82
-
83
- .back-button:hover {
84
- background-color: #0056b3;
85
- }
86
-
87
- .conversation-title {
88
- color: #333;
89
- margin-bottom: 20px;
90
- font-size: 20px;
91
- border-bottom: 1px solid #eee;
92
- padding-bottom: 10px;
93
- }
94
-
95
- .messages-list {
96
- max-height: 500px; /* Limit height and enable scrolling for messages */
97
- overflow-y: auto;
98
- padding-right: 10px; /* For scrollbar spacing */
99
- }
100
-
101
- .message-item {
102
- margin-bottom: 15px;
103
- padding: 10px 15px;
104
- border-radius: 8px;
105
- max-width: 80%;
106
- clear: both; /* Clear floats for alignment */
107
- }
108
-
109
- .message-item.user {
110
- background-color: #dcf8c6; /* Light green for user messages */
111
- align-self: flex-end; /* Align to the right in a flex container */
112
- margin-left: auto; /* Push to the right */
113
- }
114
-
115
- .message-item.chatbot {
116
- background-color: #e2e2e2; /* Light grey for chatbot messages */
117
- align-self: flex-start; /* Align to the left */
118
- margin-right: auto; /* Push to the left */
119
- }
120
-
121
- .message-sender {
122
- font-weight: bold;
123
- margin-bottom: 5px;
124
- font-size: 0.9em;
125
- }
126
-
127
- .message-item.user .message-sender {
128
- color: #3d8c1c; /* Darker green for user sender */
129
- }
130
-
131
- .message-item.chatbot .message-sender {
132
- color: #555;
133
- }
134
-
135
- .message-content {
136
- font-size: 1em;
137
- line-height: 1.4;
138
- word-wrap: break-word; /* Ensure long words wrap */
139
- }
140
-
141
- .message-timestamp {
142
- font-size: 0.75em;
143
- color: #999;
144
- margin-top: 5px;
145
- text-align: right;
146
- }
147
-
148
- /* Ensure messages-list items align correctly */
149
- .messages-list {
150
- display: flex;
151
- flex-direction: column;
152
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/src/components/chat/History.jsx CHANGED
@@ -1,4 +1,3 @@
1
-
2
  import React, { useState, useEffect } from 'react';
3
  import { getChatHistory } from '../../services/firestore.js';
4
  import Sidebar from '../layout/Sidebar';
@@ -9,6 +8,7 @@ const History = () => {
9
 
10
  useEffect(() => {
11
  const fetchHistory = async () => {
 
12
  const userChats = await getChatHistory();
13
  setChats(userChats);
14
  setLoading(false);
@@ -18,24 +18,28 @@ const History = () => {
18
 
19
  return (
20
  <div className="chat-layout">
21
- <Sidebar onNewChat={() => {}} />
22
  <div className="chat-main">
23
  <h2 className="history-title">History</h2>
24
  {loading ? (
25
  <p>Loading history...</p>
26
  ) : (
27
  <ul className="history-list">
28
- {chats.map(chat => (
29
- <li key={chat.id} className="history-item">
30
- <span className="history-icon">💬</span>
31
- <div className="history-details">
32
- <p className="history-prompt">{chat.title}</p>
33
- <span className="history-date">
34
- {new Date(chat.createdAt?.toDate()).toLocaleString()}
35
- </span>
36
- </div>
37
- </li>
38
- ))}
 
 
 
 
39
  </ul>
40
  )}
41
  </div>
 
 
1
  import React, { useState, useEffect } from 'react';
2
  import { getChatHistory } from '../../services/firestore.js';
3
  import Sidebar from '../layout/Sidebar';
 
8
 
9
  useEffect(() => {
10
  const fetchHistory = async () => {
11
+ setLoading(true); // Set loading to true before fetching
12
  const userChats = await getChatHistory();
13
  setChats(userChats);
14
  setLoading(false);
 
18
 
19
  return (
20
  <div className="chat-layout">
21
+ <Sidebar onNewChat={() => {}} /> {/* Sidebar for navigation */}
22
  <div className="chat-main">
23
  <h2 className="history-title">History</h2>
24
  {loading ? (
25
  <p>Loading history...</p>
26
  ) : (
27
  <ul className="history-list">
28
+ {chats.length === 0 ? (
29
+ <p className="no-history-message">No chat history available. Start a new conversation!</p>
30
+ ) : (
31
+ chats.map(chat => (
32
+ <li key={chat.id} className="history-item">
33
+ <span className="history-icon">💬</span>
34
+ <div className="history-details">
35
+ <p className="history-prompt">{chat.title}</p>
36
+ <span className="history-date">
37
+ {new Date(chat.createdAt?.toDate()).toLocaleString()}
38
+ </span>
39
+ </div>
40
+ </li>
41
+ ))
42
+ )}
43
  </ul>
44
  )}
45
  </div>
frontend/src/components/layout/Sidebar.jsx CHANGED
@@ -1,20 +1,11 @@
1
  import React from 'react';
2
- import { Link, useNavigate } from 'react-router-dom';
3
- import { signOut } from "firebase/auth";
4
- import { auth } from '../../services/firebase.js';
5
 
6
  const Sidebar = ({ onNewChat }) => {
7
- const navigate = useNavigate();
8
 
9
- // No longer needed for sidebar, but keeping if still used elsewhere
10
- const handleLogout = async () => {
11
- try {
12
- await signOut(auth);
13
- navigate('/login');
14
- } catch (error) {
15
- console.error("Error signing out: ", error);
16
- }
17
- };
18
 
19
  return (
20
  <div className="sidebar">
 
1
  import React from 'react';
2
+ import { Link } from 'react-router-dom';
3
+
 
4
 
5
  const Sidebar = ({ onNewChat }) => {
6
+
7
 
8
+
 
 
 
 
 
 
 
 
9
 
10
  return (
11
  <div className="sidebar">
frontend/src/index.css CHANGED
@@ -1,88 +1,89 @@
1
  /* frontend/src/index.css */
 
 
2
  @tailwind base;
3
  @tailwind components;
4
  @tailwind utilities;
5
 
6
  /* Define custom CSS Variables */
7
  :root {
8
- --primary-color: #22c55e;
9
- --background-color: #fff6d7;
10
- --text-color: #333;
11
- --white-color: #ffffff;
12
- --border-color: #e5e7eb;
13
- --green-button-bg: #4CAF50; /* Specific green for buttons */
14
- --green-button-hover-bg: #45a049; /* Darker green on hover */
15
- --dark-green-text: #0dba4b; /* Specific dark green for history/settings text */
16
- --fontFamily: system-ui; /* Added a fallback for --fontFamily if not defined */
17
- --header-height: 65px; /* Define a variable for consistent header height if it's fixed */
18
- }
19
-
20
- /* Global Styles for no full-page scrolling, and font-family */
21
  html, body, #root, .app-wrapper {
22
- height: 100%;
23
- margin: 0;
24
- padding: 0;
25
- /* overflow: hidden is critical for entire page */
26
- overflow: hidden;
27
  }
28
 
29
  body {
30
- font-family: var(--fontFamily), -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
31
- -webkit-font-smoothing: antialiased;
32
- -moz-osx-font-smoothing: grayscale; /* Keep this for Firefox/Mozilla */
33
- background-color: var(--white-color);
34
- color: var(--text-color);
35
  }
36
 
37
  /* Hide scrollbar for webkit browsers */
38
  .hide-scrollbar::-webkit-scrollbar {
39
- display: none;
40
  }
41
  /* For IE, Edge and Firefox */
42
  .hide-scrollbar {
43
- -ms-overflow-style: none;
44
- scrollbar-width: none;
45
  }
46
 
47
  /* --- App Wrapper (main application container) --- */
48
  .app-wrapper {
49
- display: flex;
50
- flex-direction: column;
51
- height: 100%; /* Ensure app-wrapper takes full height */
52
  }
53
 
54
  main {
55
- flex-grow: 1;
56
- display: flex;
57
- justify-content: center;
58
- align-items: center;
59
  }
60
 
61
  /* --- Header --- */
62
  .app-header {
63
- display: flex;
64
- justify-content: space-between;
65
- align-items: center;
66
- padding: 1rem 2rem;
67
- background-color: var(--white-color);
68
- border-bottom: 1px solid var(--border-color);
69
- /* position: sticky and top: 0 ensure it stays visible */
70
- position: sticky;
71
- top: 0;
72
- z-index: 1000;
73
- height: var(--header-height); /* Set fixed height for header if known */
74
- box-sizing: border-box; /* Include padding in height calculation */
75
  }
76
 
77
  .logo {
78
- display: flex;
79
- align-items: center;
80
- gap: 10px;
81
  }
82
 
83
  .logo img {
84
- height: 40px;
85
- width: auto;
86
  }
87
 
88
  .logo .brand-names {
@@ -103,37 +104,36 @@ main {
103
  }
104
 
105
  .app-header nav {
106
- display: flex;
107
- align-items: center;
108
- gap: 1rem;
109
  }
110
 
111
  .app-header nav a {
112
- text-decoration: none;
113
- color: var(--text-color);
114
- font-weight: 500;
115
- transition: color 0.3s ease;
116
  }
117
 
118
  .app-header nav a:hover {
119
- color: var(--primary-color);
120
  }
121
 
122
  /* Base button styles from root vars */
123
  .btn-primary {
124
- background-color: var(--primary-color);
125
- color: var(--white-color);
126
- border: none;
127
- padding: 0.5rem 1rem;
128
- border-radius: 6px;
129
- cursor: pointer;
130
- transition: background-color 0.3s ease;
131
  }
132
  .btn-primary:hover {
133
- background-color: #1e9d4e;
134
  }
135
 
136
-
137
  /* Specific Green Text Link (for Login) */
138
  .link-green {
139
  color: var(--green-button-bg) !important;
@@ -169,18 +169,18 @@ main {
169
  }
170
 
171
  .btn-secondary {
172
- background-color: transparent;
173
- border: 1px solid var(--border-color);
174
- color: var(--text-color);
175
- padding: 0.5rem 1rem;
176
- border-radius: 6px;
177
- cursor: pointer;
178
- transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease;
179
  }
180
  .btn-secondary:hover {
181
- background-color: #f0f0f0;
182
- border-color: #c0c0c0;
183
- color: #555;
184
  }
185
 
186
 
@@ -191,37 +191,38 @@ main {
191
  .form-group { margin-bottom: 1.5rem; }
192
  .form-group label { display: block; margin-bottom: 0.5rem; }
193
  .form-group input {
194
- width: 100%;
195
- padding: 0.75rem;
196
- border: 1px solid var(--border-color);
197
- border-radius: 6px;
198
- background-color: #fafafa;
199
- box-sizing: border-box;
200
  }
201
  .forgot-password { text-align: right; margin-bottom: 1rem; font-size: 0.9em;}
202
  .error-message { color: red; text-align: center; margin-bottom: 1rem; }
203
  .auth-form .btn-primary { width: 100%; padding: 0.75rem; }
204
 
205
- /* --- Chat Layout --- */
206
  .chat-layout {
207
- display: flex;
208
- width: 100%;
209
- /* This is crucial: take remaining height after header */
210
- height: calc(100% - var(--header-height));
 
211
  }
212
 
213
  /* Sidebar Styling */
214
  .sidebar {
215
- width: 260px;
216
- background-color: #f9fafb;
217
- padding: 1rem;
218
- border-right: 1px solid var(--border-color);
219
- display: flex;
220
- flex-direction: column;
221
- justify-content: space-between; /* Pushes content to top and bottom */
222
- flex-shrink: 0;
223
- height: 100%; /* Make sidebar take full height of chat-layout */
224
- box-sizing: border-box;
225
  }
226
 
227
  /* sidebar-top now contains only the new chat button */
@@ -232,22 +233,22 @@ main {
232
 
233
  /* New Chat Button Styling */
234
  .new-chat-btn {
235
- width: 100%;
236
- padding: 0.75rem;
237
- border: none;
238
- border-radius: 6px;
239
- background-color: var(--dark-green-text);
240
- color: var(--white-color);
241
- cursor: pointer;
242
- text-align: left;
243
- font-weight: 500;
244
- font-size: 1rem;
245
- line-height: 1.5rem;
246
- display: flex;
247
- align-items: center;
248
- gap: 0.5rem;
249
- transition: background-color 0.3s ease;
250
- margin-bottom: 1rem; /* Space below the button */
251
  }
252
  .new-chat-btn:hover {
253
  background-color: var(--green-button-hover-bg);
@@ -268,20 +269,20 @@ main {
268
 
269
  /* Styling for History and Settings links */
270
  .sidebar-nav-link {
271
- display: flex;
272
- align-items: center;
273
- gap: 0.5rem;
274
- padding: 0.75rem;
275
- text-decoration: none;
276
- color: var(--dark-green-text);
277
- border-radius: 6px;
278
- background: transparent; /* Explicitly remove background */
279
- border: none;
280
- width: 100%;
281
- font-weight: 500;
282
- font-size: 1rem;
283
- line-height: 1.5rem;
284
- transition: background-color 0.3s ease, color 0.3s ease;
285
  }
286
  .sidebar-nav-link:hover {
287
  background-color: transparent; /* Ensure no background on hover */
@@ -292,97 +293,136 @@ main {
292
  color: var(--dark-green-text); /* Keep dark green text when active */
293
  }
294
 
295
- /* Main chat content area */
296
  .chat-main {
297
- flex-grow: 1;
298
- display: flex;
299
- flex-direction: column;
300
- background-color: var(--white-color);
301
- height: 100%;
302
- box-sizing: border-box;
 
 
303
  }
304
 
 
305
  .chat-messages {
306
- flex-grow: 1;
307
- padding: 2rem;
308
- overflow-y: auto; /* Keep scrollbar ONLY for chat conversation */
309
- display: flex;
310
- flex-direction: column;
311
- gap: 1rem;
312
  }
313
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
314
  .message-wrapper {
315
- display: flex;
316
- align-items: flex-start;
317
- max-width: 100%;
318
  }
 
 
319
  .message-wrapper.user {
320
- justify-content: flex-end;
321
- flex-direction: row-reverse;
322
  }
323
- .message-wrapper.assistant { justify-content: flex-start; }
324
 
325
- /* Avatar specific styles */
326
  .chat-avatar {
327
- width: 40px;
328
- height: 40px;
329
- min-width: 40px;
330
  border-radius: 50%;
 
 
 
331
  display: flex;
332
  justify-content: center;
333
  align-items: center;
334
- background-color: #e0e0e0;
335
- box-shadow: 0 1px 3px rgba(0,0,0,0.1);
336
- overflow: hidden;
337
- flex-shrink: 0;
338
  }
339
 
340
- .user-icon-placeholder {
341
- font-size: 24px;
342
- color: #666;
343
- }
344
-
345
- .message-wrapper.assistant .chat-avatar {
346
- margin-right: 10px;
347
- }
348
  .message-wrapper.user .chat-avatar {
349
  margin-left: 10px;
 
350
  }
351
 
 
352
  .chat-avatar .avatar-image {
353
- width: 100%;
354
- height: 100%;
355
- object-fit: cover;
356
- border-radius: 50%;
357
  }
358
 
 
359
  .message-bubble {
360
- padding: 0.75rem 1rem;
361
- border-radius: 12px;
362
- max-width: 70%;
363
- word-wrap: break-word;
364
- overflow-wrap: break-word;
365
- white-space: pre-wrap;
366
- box-shadow: 0 1px 3px rgba(0,0,0,0.1);
367
- }
368
 
369
- .message-wrapper.user .message-bubble {
370
- background-color: var(--primary-color);
371
- color: var(--white-color);
372
- border-bottom-right-radius: 4px;
 
 
373
  }
 
 
374
  .message-wrapper.assistant .message-bubble {
375
- background-color: #f3f4f6;
376
- color: var(--text-color);
377
- border-bottom-left-radius: 4px;
 
 
 
 
 
 
 
378
  }
379
 
380
- /* Blinking cursor for streaming */
381
  .blinking-cursor {
382
- display: inline-block;
383
- width: 0.5em;
384
- background-color: var(--text-color);
385
  animation: blink 1s step-end infinite;
 
 
 
386
  }
387
 
388
  @keyframes blink {
@@ -391,71 +431,236 @@ main {
391
  }
392
 
393
 
 
394
  .chat-input-area {
395
- padding: 1rem 2rem;
396
- border-top: 1px solid var(--border-color);
397
- background-color: var(--white-color);
398
- }
399
- .chat-form { display: flex; gap: 0.5rem; }
400
- .chat-form input {
401
- flex-grow: 1;
402
- padding: 0.75rem;
403
- border-radius: 6px;
404
- border: 1px solid var(--border-color);
405
- background-color: #fafafa;
406
- box-sizing: border-box;
407
- outline: none;
408
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
409
  .chat-form button {
410
- background-color: var(--primary-color);
411
- color: var(--white-color);
412
- border: none;
413
- border-radius: 6px;
414
- padding: 0.5rem 1rem;
415
- cursor: pointer;
416
- transition: background-color 0.3s ease;
 
 
 
 
 
 
417
  }
418
- .chat-form button:hover {
419
- background-color: #1e9d4e;
 
 
 
420
  }
421
 
422
- .empty-chat-placeholder {
423
- text-align: center;
424
- margin-top: 20%;
425
- color: #9ca3af;
426
- flex-grow: 1;
427
- display: flex;
428
- flex-direction: column;
429
- justify-content: center;
430
- align-items: center;
431
  }
432
- .empty-chat-placeholder h1 {
433
- font-size: 2.5em;
434
- margin-bottom: 10px;
435
- color: #333;
 
 
436
  }
437
- .empty-chat-placeholder p {
438
- font-size: 1.1em;
439
- line-height: 1.6;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
440
  }
441
 
442
- /* --- History Page --- */
443
- .history-title { padding: 0 2rem; margin-top: 2rem; margin-bottom: 1.5rem; color: var(--text-color); }
444
- .history-list { list-style: none; padding: 0 2rem; }
445
  .history-item {
446
- display: flex;
447
- align-items: center;
448
- gap: 1rem;
449
- padding: 1rem;
450
- border-bottom: 1px solid var(--border-color);
451
- cursor: pointer;
452
- transition: background-color 0.3s ease;
 
 
453
  }
 
454
  .history-item:hover {
455
- background-color: #f9f9f9;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
456
  }
457
- .history-prompt { font-weight: 500; flex-grow: 1; }
458
- .history-date { font-size: 0.8rem; color: #6b7280; }
459
 
460
  /* Responsive adjustments */
461
  @media (max-width: 768px) {
@@ -464,6 +669,7 @@ main {
464
  align-items: flex-start;
465
  padding: 1rem;
466
  gap: 10px;
 
467
  }
468
  .app-header nav {
469
  flex-wrap: wrap;
@@ -473,8 +679,8 @@ main {
473
  }
474
  .chat-layout {
475
  flex-direction: column;
476
- /* On mobile, chat-layout height needs to account for header and horizontal sidebar */
477
- height: calc(100% - var(--header-height) - var(--sidebar-height-mobile, 60px));
478
  }
479
  .sidebar {
480
  width: 100%;
@@ -482,27 +688,46 @@ main {
482
  border-bottom: 1px solid var(--border-color);
483
  padding: 10px;
484
  flex-direction: row; /* Sidebar items in a row for mobile */
485
- justify-content: center;
486
- gap: 10px;
487
  height: auto; /* Allow sidebar height to be determined by content */
488
- overflow-y: hidden; /* Hide sidebar scrollbar on mobile if it has one */
 
 
 
 
 
 
 
 
 
489
  }
490
  .new-chat-btn {
491
  width: auto;
492
  font-size: 0.9em;
493
  padding: 0.5rem 0.8rem;
 
494
  }
495
- .sidebar-nav {
496
- margin-top: 0;
 
 
 
 
 
 
497
  display: flex;
 
498
  gap: 10px;
499
  }
500
- .sidebar-nav a, .sidebar-link {
501
  padding: 0.5rem;
502
  font-size: 0.9em;
 
503
  }
504
  .chat-main {
505
  height: 100%; /* Take remaining height from parent chat-layout */
 
506
  }
507
  .chat-messages {
508
  padding: 1rem;
@@ -513,4 +738,51 @@ main {
513
  .chat-input-area {
514
  padding: 1rem;
515
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
516
  }
 
1
  /* frontend/src/index.css */
2
+
3
+ /* Tailwind CSS directives - MUST be at the very top */
4
  @tailwind base;
5
  @tailwind components;
6
  @tailwind utilities;
7
 
8
  /* Define custom CSS Variables */
9
  :root {
10
+ --primary-color: #22c55e;
11
+ --background-color: #fff6d7;
12
+ --text-color: #333;
13
+ --white-color: #ffffff;
14
+ --border-color: #e5e7eb;
15
+ --green-button-bg: #4CAF50; /* Specific green for buttons */
16
+ --green-button-hover-bg: #45a049; /* Darker green on hover */
17
+ --dark-green-text: #0dba4b; /* Specific dark green for history/settings text */
18
+ --fontFamily: 'Inter', "Noto Sans", system-ui; /* Consolidated font families */
19
+ --header-height: 65px; /* Define a variable for consistent header height if it's fixed */
20
+ }
21
+
22
+ /* Global Styles for full-page layout and font-family */
23
  html, body, #root, .app-wrapper {
24
+ height: 100%;
25
+ margin: 0;
26
+ padding: 0;
27
+ /* overflow: hidden is critical for entire page to prevent unwanted scrolling */
28
+ overflow: hidden;
29
  }
30
 
31
  body {
32
+ font-family: var(--fontFamily), -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
33
+ -webkit-font-smoothing: antialiased;
34
+ -moz-osx-font-smoothing: grayscale;
35
+ background-color: var(--white-color); /* Changed to use white-color variable */
36
+ color: var(--text-color);
37
  }
38
 
39
  /* Hide scrollbar for webkit browsers */
40
  .hide-scrollbar::-webkit-scrollbar {
41
+ display: none;
42
  }
43
  /* For IE, Edge and Firefox */
44
  .hide-scrollbar {
45
+ -ms-overflow-style: none; /* IE and Edge */
46
+ scrollbar-width: none; /* Firefox */
47
  }
48
 
49
  /* --- App Wrapper (main application container) --- */
50
  .app-wrapper {
51
+ display: flex;
52
+ flex-direction: column;
53
+ height: 100%; /* Ensure app-wrapper takes full height */
54
  }
55
 
56
  main {
57
+ flex-grow: 1;
58
+ display: flex;
59
+ justify-content: center;
60
+ align-items: center;
61
  }
62
 
63
  /* --- Header --- */
64
  .app-header {
65
+ display: flex;
66
+ justify-content: space-between;
67
+ align-items: center;
68
+ padding: 1rem 2rem;
69
+ background-color: var(--white-color);
70
+ border-bottom: 1px solid var(--border-color);
71
+ position: sticky;
72
+ top: 0;
73
+ z-index: 1000;
74
+ height: var(--header-height); /* Set fixed height for header if known */
75
+ box-sizing: border-box; /* Include padding in height calculation */
 
76
  }
77
 
78
  .logo {
79
+ display: flex;
80
+ align-items: center;
81
+ gap: 10px;
82
  }
83
 
84
  .logo img {
85
+ height: 40px;
86
+ width: auto;
87
  }
88
 
89
  .logo .brand-names {
 
104
  }
105
 
106
  .app-header nav {
107
+ display: flex;
108
+ align-items: center;
109
+ gap: 1rem;
110
  }
111
 
112
  .app-header nav a {
113
+ text-decoration: none;
114
+ color: var(--text-color);
115
+ font-weight: 500;
116
+ transition: color 0.3s ease;
117
  }
118
 
119
  .app-header nav a:hover {
120
+ color: var(--primary-color);
121
  }
122
 
123
  /* Base button styles from root vars */
124
  .btn-primary {
125
+ background-color: var(--primary-color);
126
+ color: var(--white-color);
127
+ border: none;
128
+ padding: 0.5rem 1rem;
129
+ border-radius: 6px;
130
+ cursor: pointer;
131
+ transition: background-color 0.3s ease;
132
  }
133
  .btn-primary:hover {
134
+ background-color: #1e9d4e;
135
  }
136
 
 
137
  /* Specific Green Text Link (for Login) */
138
  .link-green {
139
  color: var(--green-button-bg) !important;
 
169
  }
170
 
171
  .btn-secondary {
172
+ background-color: transparent;
173
+ border: 1px solid var(--border-color);
174
+ color: var(--text-color);
175
+ padding: 0.5rem 1rem;
176
+ border-radius: 6px;
177
+ cursor: pointer;
178
+ transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease;
179
  }
180
  .btn-secondary:hover {
181
+ background-color: #f0f0f0;
182
+ border-color: #c0c0c0;
183
+ color: #555;
184
  }
185
 
186
 
 
191
  .form-group { margin-bottom: 1.5rem; }
192
  .form-group label { display: block; margin-bottom: 0.5rem; }
193
  .form-group input {
194
+ width: 100%;
195
+ padding: 0.75rem;
196
+ border: 1px solid var(--border-color);
197
+ border-radius: 6px;
198
+ background-color: #fafafa;
199
+ box-sizing: border-box;
200
  }
201
  .forgot-password { text-align: right; margin-bottom: 1rem; font-size: 0.9em;}
202
  .error-message { color: red; text-align: center; margin-bottom: 1rem; }
203
  .auth-form .btn-primary { width: 100%; padding: 0.75rem; }
204
 
205
+ /* --- Chat Layout (from chatinterface.css, merged and adapted) --- */
206
  .chat-layout {
207
+ display: flex;
208
+ width: 100%;
209
+ /* Use calc to account for header height, as body has overflow:hidden */
210
+ height: calc(100% - var(--header-height));
211
+ overflow: hidden; /* Important for containing sidebar and main chat content */
212
  }
213
 
214
  /* Sidebar Styling */
215
  .sidebar {
216
+ width: 260px;
217
+ background-color: #f9fafb;
218
+ padding: 1rem;
219
+ border-right: 1px solid var(--border-color);
220
+ display: flex;
221
+ flex-direction: column;
222
+ justify-content: space-between; /* Pushes content to top and bottom */
223
+ flex-shrink: 0;
224
+ height: 100%; /* Make sidebar take full height of chat-layout */
225
+ box-sizing: border-box;
226
  }
227
 
228
  /* sidebar-top now contains only the new chat button */
 
233
 
234
  /* New Chat Button Styling */
235
  .new-chat-btn {
236
+ width: 100%;
237
+ padding: 0.75rem;
238
+ border: none;
239
+ border-radius: 6px;
240
+ background-color: var(--dark-green-text);
241
+ color: var(--white-color);
242
+ cursor: pointer;
243
+ text-align: left;
244
+ font-weight: 500;
245
+ font-size: 1rem;
246
+ line-height: 1.5rem;
247
+ display: flex;
248
+ align-items: center;
249
+ gap: 0.5rem;
250
+ transition: background-color 0.3s ease;
251
+ margin-bottom: 1rem; /* Space below the button */
252
  }
253
  .new-chat-btn:hover {
254
  background-color: var(--green-button-hover-bg);
 
269
 
270
  /* Styling for History and Settings links */
271
  .sidebar-nav-link {
272
+ display: flex;
273
+ align-items: center;
274
+ gap: 0.5rem;
275
+ padding: 0.75rem;
276
+ text-decoration: none;
277
+ color: var(--dark-green-text);
278
+ border-radius: 6px;
279
+ background: transparent; /* Explicitly remove background */
280
+ border: none;
281
+ width: 100%;
282
+ font-weight: 500;
283
+ font-size: 1rem;
284
+ line-height: 1.5rem;
285
+ transition: background-color 0.3s ease, color 0.3s ease;
286
  }
287
  .sidebar-nav-link:hover {
288
  background-color: transparent; /* Ensure no background on hover */
 
293
  color: var(--dark-green-text); /* Keep dark green text when active */
294
  }
295
 
296
+ /* Main chat content area (from chatinterface.css, merged) */
297
  .chat-main {
298
+ flex-grow: 1;
299
+ display: flex;
300
+ flex-direction: column;
301
+ justify-content: space-between; /* From chatinterface.css */
302
+ background-color: #f9f9f9; /* From chatinterface.css */
303
+ height: 100%; /* Ensures it takes full height of parent */
304
+ box-sizing: border-box; /* Include padding in height calculation */
305
+ overflow-y: auto; /* Enable scrolling for main content like history page */
306
  }
307
 
308
+ /* Chat Messages Area (from chatinterface.css, merged) */
309
  .chat-messages {
310
+ flex-grow: 1;
311
+ overflow-y: auto; /* Enable scrolling for messages */
312
+ padding: 20px; /* From chatinterface.css */
313
+ display: flex;
314
+ flex-direction: column;
315
+ gap: 15px; /* Spacing between message wrappers from chatinterface.css */
316
  }
317
 
318
+ /* Empty Chat Placeholder (from chatinterface.css, merged) */
319
+ .empty-chat-placeholder {
320
+ text-align: center;
321
+ padding: 50px; /* From chatinterface.css */
322
+ color: #666; /* From chatinterface.css */
323
+ flex-grow: 1; /* Pushes content to the bottom if few messages */
324
+ display: flex;
325
+ flex-direction: column;
326
+ justify-content: center;
327
+ align-items: center;
328
+ }
329
+ .empty-chat-placeholder h1 {
330
+ font-size: 2.5em;
331
+ margin-bottom: 10px;
332
+ color: #333;
333
+ }
334
+ .empty-chat-placeholder p {
335
+ font-size: 1.1em;
336
+ line-height: 1.6;
337
+ }
338
+ .no-history-message { /* New style for when history is empty */
339
+ text-align: center;
340
+ color: #999;
341
+ font-style: italic;
342
+ margin-top: 20px;
343
+ }
344
+
345
+
346
+ /* Message Wrapper (contains avatar and bubble) (from chatinterface.css, merged) */
347
  .message-wrapper {
348
+ display: flex; /* Use flexbox for layout of avatar and bubble */
349
+ align-items: flex-start; /* Align items to the top */
350
+ max-width: 100%; /* Ensure it doesn't overflow */
351
  }
352
+
353
+ /* Specific styles for user messages (align to right) */
354
  .message-wrapper.user {
355
+ justify-content: flex-end; /* Push user messages to the right */
356
+ flex-direction: row-reverse; /* Put avatar on the right for user */
357
  }
 
358
 
359
+ /* Chat Avatar Styling (from chatinterface.css, merged) */
360
  .chat-avatar {
361
+ font-size: 24px; /* Still useful for user emoji */
362
+ padding: 8px;
 
363
  border-radius: 50%;
364
+ background-color: #e0e0e0; /* Default background for avatar circle */
365
+ min-width: 40px;
366
+ height: 40px;
367
  display: flex;
368
  justify-content: center;
369
  align-items: center;
370
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
371
+ /* Margins for spacing */
372
+ margin-right: 10px; /* Default for assistant avatar */
373
+ overflow: hidden; /* Crucial to clip image if it's larger than the circle */
374
  }
375
 
376
+ /* Adjust margin for user avatar (since row-reverse) */
 
 
 
 
 
 
 
377
  .message-wrapper.user .chat-avatar {
378
  margin-left: 10px;
379
+ margin-right: 0;
380
  }
381
 
382
+ /* Style for the actual image inside the avatar div */
383
  .chat-avatar .avatar-image {
384
+ width: 100%; /* Make image fill the avatar div */
385
+ height: 100%; /* Make image fill the avatar div */
386
+ object-fit: cover; /* Crop and cover the area without distortion */
387
+ border-radius: 50%; /* Ensure the image itself is also rounded */
388
  }
389
 
390
+ /* Message Bubble Styling (from chatinterface.css, merged) */
391
  .message-bubble {
392
+ background-color: #f0f0f0;
393
+ padding: 12px 18px;
394
+ border-radius: 20px;
395
+ max-width: 70%; /* Limit message width */
396
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
 
 
 
397
 
398
+ /* --- THE KEY CSS PROPERTY FOR NEWLINES --- */
399
+ white-space: pre-wrap; /* This will make '\n' characters create new lines */
400
+ /* --- END KEY CSS PROPERTY --- */
401
+
402
+ word-break: break-word; /* Ensure long words break within the bubble */
403
+ overflow-wrap: break-word; /* Modern equivalent */
404
  }
405
+
406
+ /* Specific styling for assistant bubbles */
407
  .message-wrapper.assistant .message-bubble {
408
+ background-color: #e6f7ff; /* Light blue for assistant */
409
+ border-bottom-left-radius: 5px; /* Pointy bottom-left for assistant */
410
+ margin-right: auto; /* Push to left, next to sidebar */
411
+ }
412
+
413
+ /* Specific styling for user bubbles */
414
+ .message-wrapper.user .message-bubble {
415
+ background-color: #d1e7dd; /* Light green for user */
416
+ border-bottom-right-radius: 5px; /* Pointy bottom-right for user */
417
+ margin-left: auto; /* Push to right */
418
  }
419
 
420
+ /* Blinking Cursor */
421
  .blinking-cursor {
 
 
 
422
  animation: blink 1s step-end infinite;
423
+ display: inline-block; /* Essential for it to be visible next to text */
424
+ width: 0.5em; /* Give it a small width */
425
+ background-color: var(--text-color); /* From original index.css */
426
  }
427
 
428
  @keyframes blink {
 
431
  }
432
 
433
 
434
+ /* Chat Input Area (from chatinterface.css, merged) */
435
  .chat-input-area {
436
+ padding: 20px; /* From chatinterface.css */
437
+ border-top: 1px solid #eee; /* From chatinterface.css */
438
+ background-color: #fff; /* From chatinterface.css */
 
 
 
 
 
 
 
 
 
 
439
  }
440
+
441
+ .chat-form {
442
+ display: flex;
443
+ gap: 10px; /* From chatinterface.css */
444
+ }
445
+
446
+ .chat-form input[type="text"] {
447
+ flex-grow: 1;
448
+ padding: 12px; /* From chatinterface.css */
449
+ border: 1px solid #ccc; /* From chatinterface.css */
450
+ border-radius: 25px; /* From chatinterface.css */
451
+ font-size: 16px; /* From chatinterface.css */
452
+ outline: none;
453
+ background-color: #fafafa; /* Using existing variable if possible, else direct value */
454
+ }
455
+
456
  .chat-form button {
457
+ padding: 12px 20px; /* From chatinterface.css */
458
+ background-color: #007bff; /* From chatinterface.css */
459
+ color: white; /* From chatinterface.css */
460
+ border: none;
461
+ border-radius: 25px; /* From chatinterface.css */
462
+ cursor: pointer;
463
+ font-size: 16px; /* From chatinterface.css */
464
+ transition: background-color 0.2s;
465
+ /* Using primary-color for consistency */
466
+ background-color: var(--primary-color);
467
+ color: var(--white-color);
468
+ border-radius: 6px; /* Adjusted to match other buttons */
469
+ padding: 0.75rem 1rem; /* Adjusted to match other buttons */
470
  }
471
+
472
+ .chat-form button:hover:not(:disabled) {
473
+ background-color: #0056b3; /* Darker blue from chatinterface.css */
474
+ /* Using hover color from primary-color for consistency */
475
+ background-color: #1e9d4e;
476
  }
477
 
478
+ .chat-form button:disabled {
479
+ background-color: #a0a0a0; /* From chatinterface.css */
480
+ cursor: not-allowed;
 
 
 
 
 
 
481
  }
482
+
483
+ /* Voice input specific styles */
484
+ .voice-input-container {
485
+ display: flex;
486
+ align-items: center;
487
+ gap: 0.5rem;
488
  }
489
+
490
+ .voice-record-btn {
491
+ padding: 0.5rem;
492
+ border-radius: 50%;
493
+ background-color: #ef4444; /* Red color for record button */
494
+ color: white;
495
+ border: none;
496
+ cursor: pointer;
497
+ display: flex;
498
+ align-items: center;
499
+ justify-content: center;
500
+ width: 40px; /* Make it a perfect circle */
501
+ height: 40px;
502
+ transition: background-color 0.2s;
503
+ }
504
+
505
+ .voice-record-btn:hover:not(:disabled) {
506
+ background-color: #dc2626; /* Darker red on hover */
507
+ }
508
+
509
+ .voice-record-btn.recording {
510
+ background-color: #f87171; /* Lighter red when recording */
511
+ animation: pulse 1.5s infinite; /* Add a pulse animation when recording */
512
+ }
513
+
514
+ @keyframes pulse {
515
+ 0% { transform: scale(1); box-shadow: 0 0 0 0 rgba(248, 113, 113, 0.7); }
516
+ 70% { transform: scale(1.1); box-shadow: 0 0 0 10px rgba(248, 113, 113, 0); }
517
+ 100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(248, 113, 113, 0); }
518
+ }
519
+
520
+ .sound-wave {
521
+ flex-grow: 1; /* Allow visualizer to take available space */
522
+ height: 40px; /* Adjust height as needed */
523
+ margin-left: 0.5rem;
524
+ }
525
+
526
+
527
+ /* --- History Page Styles (from history.css, merged and adapted) --- */
528
+ .history-title {
529
+ color: var(--text-color); /* From history.css, using variable */
530
+ margin-bottom: 20px;
531
+ font-size: 24px; /* From history.css */
532
+ padding: 0 20px; /* Added padding to align with chat-messages */
533
+ margin-top: 20px; /* Added margin-top */
534
+ }
535
+
536
+ .history-list {
537
+ list-style: none;
538
+ padding: 0 20px; /* Added padding to align with chat-messages */
539
  }
540
 
 
 
 
541
  .history-item {
542
+ background-color: #fff;
543
+ border-radius: 8px;
544
+ padding: 15px 20px; /* From history.css */
545
+ margin-bottom: 10px;
546
+ display: flex;
547
+ align-items: center;
548
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
549
+ cursor: pointer;
550
+ transition: background-color 0.2s ease-in-out;
551
  }
552
+
553
  .history-item:hover {
554
+ background-color: #e9ecef; /* From history.css */
555
+ }
556
+
557
+ .history-icon {
558
+ font-size: 24px;
559
+ margin-right: 15px;
560
+ }
561
+
562
+ .history-details {
563
+ flex-grow: 1;
564
+ }
565
+
566
+ .history-prompt {
567
+ font-weight: bold; /* From history.css */
568
+ color: #555; /* From history.css */
569
+ margin-bottom: 5px;
570
+ }
571
+
572
+ .history-date {
573
+ font-size: 0.85em; /* From history.css */
574
+ color: #888; /* From history.css */
575
+ }
576
+
577
+ /* Conversation View Specific Styles (from history.css, merged) */
578
+ .conversation-view {
579
+ background-color: #fff;
580
+ border-radius: 8px;
581
+ padding: 20px;
582
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
583
+ }
584
+
585
+ .back-button {
586
+ background-color: #007bff;
587
+ color: white;
588
+ border: none;
589
+ padding: 10px 15px;
590
+ border-radius: 5px;
591
+ cursor: pointer;
592
+ margin-bottom: 15px;
593
+ font-size: 16px;
594
+ transition: background-color 0.2s ease-in-out;
595
+ }
596
+
597
+ .back-button:hover {
598
+ background-color: #0056b3;
599
+ }
600
+
601
+ .conversation-title {
602
+ color: #333;
603
+ margin-bottom: 20px;
604
+ font-size: 20px;
605
+ border-bottom: 1px solid #eee;
606
+ padding-bottom: 10px;
607
+ }
608
+
609
+ .messages-list {
610
+ max-height: 500px; /* Limit height and enable scrolling for messages */
611
+ overflow-y: auto;
612
+ padding-right: 10px; /* For scrollbar spacing */
613
+ display: flex;
614
+ flex-direction: column;
615
+ }
616
+
617
+ .message-item {
618
+ margin-bottom: 15px;
619
+ padding: 10px 15px;
620
+ border-radius: 8px;
621
+ max-width: 80%;
622
+ clear: both; /* Good for floating elements, though flexbox often handles this */
623
+ }
624
+
625
+ .message-item.user {
626
+ background-color: #dcf8c6; /* Light green for user messages */
627
+ align-self: flex-end; /* Align to the right in a flex container */
628
+ margin-left: auto; /* Push to the right */
629
+ }
630
+
631
+ .message-item.chatbot { /* Using .chatbot from history.css, convert to .assistant where needed in JSX */
632
+ background-color: #e2e2e2; /* Light grey for chatbot messages */
633
+ align-self: flex-start; /* Align to the left */
634
+ margin-right: auto; /* Push to the left */
635
+ }
636
+
637
+ .message-sender {
638
+ font-weight: bold;
639
+ margin-bottom: 5px;
640
+ font-size: 0.9em;
641
+ }
642
+
643
+ .message-item.user .message-sender {
644
+ color: #3d8c1c; /* Darker green for user sender */
645
+ }
646
+
647
+ .message-item.chatbot .message-sender {
648
+ color: #555;
649
+ }
650
+
651
+ .message-content {
652
+ font-size: 1em;
653
+ line-height: 1.4;
654
+ word-wrap: break-word; /* Ensure long words wrap */
655
+ white-space: pre-wrap; /* Ensure newlines are respected in history view */
656
+ }
657
+
658
+ .message-timestamp {
659
+ font-size: 0.75em;
660
+ color: #999;
661
+ margin-top: 5px;
662
+ text-align: right;
663
  }
 
 
664
 
665
  /* Responsive adjustments */
666
  @media (max-width: 768px) {
 
669
  align-items: flex-start;
670
  padding: 1rem;
671
  gap: 10px;
672
+ height: auto; /* Allow header to expand if content wraps */
673
  }
674
  .app-header nav {
675
  flex-wrap: wrap;
 
679
  }
680
  .chat-layout {
681
  flex-direction: column;
682
+ /* On mobile, sidebar goes horizontal, its height becomes auto */
683
+ height: calc(100% - var(--header-height)); /* No fixed sidebar height subtraction here */
684
  }
685
  .sidebar {
686
  width: 100%;
 
688
  border-bottom: 1px solid var(--border-color);
689
  padding: 10px;
690
  flex-direction: row; /* Sidebar items in a row for mobile */
691
+ justify-content: space-around; /* Distribute items evenly */
692
+ align-items: center;
693
  height: auto; /* Allow sidebar height to be determined by content */
694
+ overflow-x: auto; /* Allow horizontal scrolling if items overflow */
695
+ overflow-y: hidden; /* Hide sidebar vertical scrollbar on mobile */
696
+ }
697
+ .sidebar-top {
698
+ flex-direction: row; /* Align new chat button correctly */
699
+ align-items: center;
700
+ margin-bottom: 0; /* No margin-bottom on mobile to keep things compact */
701
+ flex-grow: 1; /* Allow to take space for justify-content */
702
+ justify-content: flex-start; /* Push new chat button to left */
703
+ gap: 10px; /* Space between new chat and nav */
704
  }
705
  .new-chat-btn {
706
  width: auto;
707
  font-size: 0.9em;
708
  padding: 0.5rem 0.8rem;
709
+ margin-bottom: 0; /* Remove margin-bottom on mobile */
710
  }
711
+ .sidebar-bottom {
712
+ margin-top: 0; /* Remove auto margin on mobile */
713
+ flex-direction: row; /* Nav links in a row */
714
+ gap: 10px;
715
+ flex-grow: 1; /* Allow sidebar-bottom to take space */
716
+ justify-content: flex-end; /* Push nav links to the right */
717
+ }
718
+ .sidebar-nav { /* Added this for mobile layout */
719
  display: flex;
720
+ flex-direction: row;
721
  gap: 10px;
722
  }
723
+ .sidebar-nav-link {
724
  padding: 0.5rem;
725
  font-size: 0.9em;
726
+ flex-shrink: 0; /* Prevent links from shrinking too much */
727
  }
728
  .chat-main {
729
  height: 100%; /* Take remaining height from parent chat-layout */
730
+ overflow-y: auto; /* Allow chat content to scroll */
731
  }
732
  .chat-messages {
733
  padding: 1rem;
 
738
  .chat-input-area {
739
  padding: 1rem;
740
  }
741
+
742
+ /* Adjustments for voice input on mobile if needed */
743
+ .chat-form {
744
+ flex-wrap: wrap; /* Allow input and buttons to wrap if screen is very small */
745
+ }
746
+ .chat-form input {
747
+ width: 100%; /* Take full width */
748
+ margin-bottom: 0.5rem; /* Space below input if it wraps */
749
+ }
750
+ .chat-form button {
751
+ flex-grow: 1; /* Allow send button to take available space */
752
+ }
753
+ .voice-input-container {
754
+ width: 100%;
755
+ justify-content: center;
756
+ }
757
+ .voice-record-btn {
758
+ flex-shrink: 0;
759
+ }
760
+ .sound-wave {
761
+ flex-grow: 1;
762
+ max-width: calc(100% - 60px); /* Adjust based on button size */
763
+ }
764
+
765
+ /* Mobile adjustments for History Page */
766
+ .history-title {
767
+ padding: 0 1rem;
768
+ }
769
+ .history-list {
770
+ padding: 0 1rem;
771
+ }
772
+ .history-item {
773
+ flex-direction: column; /* Stack details vertically on mobile */
774
+ align-items: flex-start;
775
+ padding: 0.8rem;
776
+ }
777
+ .history-details {
778
+ width: 100%; /* Take full width when stacked */
779
+ }
780
+ .history-icon {
781
+ margin-right: 0;
782
+ margin-bottom: 0.5rem; /* Space below icon */
783
+ }
784
+ .history-date {
785
+ text-align: left; /* Align date to left when stacked */
786
+ }
787
+
788
  }