NitinBot001 commited on
Commit
79f451f
·
verified ·
1 Parent(s): 66fd13c

Update static/script.js

Browse files
Files changed (1) hide show
  1. static/script.js +745 -232
static/script.js CHANGED
@@ -1,233 +1,746 @@
1
- document.addEventListener('DOMContentLoaded', () => {
2
-
3
- // --- DOM Elements ---
4
- const chatArea = document.getElementById('chat-area');
5
- const closeSidebarBtn = document.getElementById('close-sidebar-btn');
6
- const messageInput = document.getElementById('message-input');
7
- const sendBtn = document.getElementById('send-btn');
8
- const newChatBtn = document.getElementById('new-chat-btn');
9
- const chatMessages = document.getElementById('chat-messages');
10
- const chatHistoryList = document.getElementById('chat-history-list');
11
- const menuToggle = document.getElementById('menu-toggle');
12
- const appContainer = document.getElementById('app-container');
13
- const chatTitle = document.getElementById('chat-title');
14
- const imageUploadBtn = document.getElementById('image-upload-btn');
15
- const imageUploadInput = document.getElementById('image-upload-input');
16
- const imagePreviewContainer = document.getElementById('image-preview-container');
17
- const imagePreview = document.getElementById('image-preview');
18
- const removeImageBtn = document.getElementById('remove-image-btn');
19
-
20
- // --- State ---
21
- let currentSessionId = null;
22
- let conversationsCache = {};
23
- let selectedImageFile = null;
24
- let imgbbApiKey = '';
25
-
26
- // --- Initialization ---
27
- const init = async () => {
28
- if (!messageInput || !sendBtn || !imageUploadBtn || !imagePreviewContainer) {
29
- console.error("Critical UI elements not found. Check HTML IDs.");
30
- return;
31
- }
32
-
33
- await fetchConfig();
34
- loadCache();
35
- await renderChatHistoryFromAPI();
36
-
37
- // Event Listeners
38
- sendBtn.addEventListener('click', sendMessage);
39
- messageInput.addEventListener('keydown', e => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); } });
40
-
41
- imageUploadBtn.addEventListener('click', () => imageUploadInput.click());
42
- imageUploadInput.addEventListener('change', handleImageSelect);
43
- removeImageBtn.addEventListener('click', removeSelectedImage);
44
-
45
- // Sidebar Listeners
46
- newChatBtn.addEventListener('click', () => { startNewChat(); closeSidebar(); });
47
- menuToggle.addEventListener('click', (event) => {
48
- event.stopPropagation();
49
- appContainer.classList.toggle('sidebar-visible');
50
- });
51
- closeSidebarBtn.addEventListener('click', (event) => {
52
- event.stopPropagation();
53
- closeSidebar();
54
- });
55
- chatArea.addEventListener('click', () => {
56
- if (appContainer.classList.contains('sidebar-visible')) {
57
- closeSidebar();
58
- }
59
- });
60
- };
61
-
62
- const fetchConfig = async () => {
63
- try {
64
- const response = await fetch('/config');
65
- const config = await response.json();
66
- imgbbApiKey = config.imgbb_api_key;
67
- if (!imgbbApiKey) { console.error("ImgBB API Key is missing."); }
68
- } catch (error) { console.error('Failed to fetch config:', error); }
69
- };
70
-
71
- // --- Core Chat Functions ---
72
- const sendMessage = async () => {
73
- const messageText = messageInput.value.trim();
74
- if (!messageText && !selectedImageFile) return;
75
- displayMessage({ role: 'user', content: messageText, imageUrl: selectedImageFile });
76
- const loadingIndicator = displayMessage({ role: 'assistant', content: 'Thinking...', isLoading: true });
77
- try {
78
- let permanentImageUrl = null;
79
- if (selectedImageFile) { permanentImageUrl = await uploadImageToImgBB(selectedImageFile); }
80
- const formData = new FormData();
81
- formData.append('message', messageText);
82
- formData.append('session_id', currentSessionId);
83
- if (permanentImageUrl) { formData.append('image_url', permanentImageUrl); }
84
- const response = await fetch('/chat', { method: 'POST', body: formData });
85
- if (!response.ok) throw new Error('Network response was not ok.');
86
- const data = await response.json();
87
- chatMessages.removeChild(loadingIndicator);
88
- displayMessage({ role: 'assistant', content: data.response });
89
- updateCache(data.session_id, { content: messageText, imageUrl: permanentImageUrl }, { content: data.response });
90
- } catch (error) {
91
- console.error('Error sending message:', error);
92
- loadingIndicator.innerHTML = marked.parse("Sorry, something went wrong.");
93
- loadingIndicator.classList.remove('loading');
94
- } finally {
95
- messageInput.value = ''; messageInput.style.height = 'auto'; removeSelectedImage();
96
- }
97
- };
98
-
99
- const uploadImageToImgBB = async (imageFile) => {
100
- if (!imgbbApiKey) throw new Error("ImgBB API Key not configured.");
101
- const formData = new FormData();
102
- formData.append('image', imageFile);
103
- formData.append('key', imgbbApiKey);
104
- const response = await fetch('https://api.imgbb.com/1/upload', { method: 'POST', body: formData });
105
- const result = await response.json();
106
- if (result.success) { return result.data.url; }
107
- else { throw new Error(result.error.message || 'Image upload failed.'); }
108
- };
109
-
110
- // --- Caching and State Management ---
111
- const startNewChat = () => {
112
- currentSessionId = null;
113
- chatMessages.innerHTML = `<div class="welcome-message"><h1>EasyFarms Assistant</h1></div>`;
114
- chatTitle.textContent = "New Chat";
115
- updateActiveChatItem();
116
- };
117
-
118
- const switchChat = async (sessionId) => {
119
- currentSessionId = sessionId;
120
- chatMessages.innerHTML = '';
121
- if (conversationsCache[sessionId] && conversationsCache[sessionId].messages) {
122
- conversationsCache[sessionId].messages.forEach(displayMessage);
123
- } else {
124
- const loading = displayMessage({ role: 'assistant', content: 'Loading chat history...', isLoading: true });
125
- try {
126
- const response = await fetch(`/history/messages/${sessionId}`);
127
- const messages = await response.json();
128
- if (!conversationsCache[sessionId]) conversationsCache[sessionId] = {};
129
- conversationsCache[sessionId].messages = messages;
130
- saveCache();
131
- chatMessages.removeChild(loading);
132
- messages.forEach(displayMessage);
133
- } catch (error) {
134
- loading.innerHTML = marked.parse("Failed to load chat history.");
135
- }
136
- }
137
- chatTitle.textContent = conversationsCache[sessionId]?.title || "Chat";
138
- updateActiveChatItem();
139
- closeSidebar();
140
- };
141
-
142
- const updateCache = (sessionId, userTurn, assistantTurn) => {
143
- const isNewChat = !currentSessionId;
144
- currentSessionId = sessionId;
145
- if (isNewChat) {
146
- const title = (userTurn.content || "Image Query").substring(0, 30) + '...';
147
- conversationsCache[sessionId] = { title, messages: [] };
148
- const item = document.createElement('div');
149
- item.className = 'chat-history-item';
150
- item.textContent = title;
151
- item.dataset.sessionId = sessionId;
152
- item.addEventListener('click', () => switchChat(sessionId));
153
- chatHistoryList.prepend(item);
154
- }
155
- const userMessage = { role: 'user', content: userTurn.content };
156
- if (userTurn.imageUrl) userMessage.imageUrl = userTurn.imageUrl;
157
- const assistantMessage = { role: 'assistant', content: assistantTurn.content };
158
- conversationsCache[sessionId].messages.push(userMessage, assistantMessage);
159
- saveCache();
160
- updateActiveChatItem();
161
- };
162
-
163
- const displayMessage = (message) => {
164
- const { role, content, imageUrl, isLoading } = message;
165
- const sender = role || message.sender;
166
- const messageDiv = document.createElement('div');
167
- messageDiv.classList.add('message', `${sender}-message`);
168
- let htmlContent = '';
169
- const imageSrc = (typeof imageUrl === 'object' && imageUrl instanceof File) ? URL.createObjectURL(imageUrl) : imageUrl;
170
- if (imageSrc) { htmlContent += `<img src="${imageSrc}" alt="User upload" class="user-upload">`; }
171
- if (content) { htmlContent += marked.parse(content); }
172
- messageDiv.innerHTML = htmlContent || (isLoading ? '...' : '');
173
- if (isLoading) messageDiv.classList.add('loading');
174
- chatMessages.appendChild(messageDiv);
175
- chatMessages.scrollTop = chatMessages.scrollHeight;
176
- return messageDiv;
177
- };
178
-
179
- // --- Image Preview Handling ---
180
- const handleImageSelect = (event) => {
181
- const file = event.target.files[0];
182
- if (file) {
183
- selectedImageFile = file;
184
- imagePreview.src = URL.createObjectURL(file);
185
- imagePreviewContainer.style.display = 'block';
186
- }
187
- };
188
-
189
- const removeSelectedImage = () => {
190
- selectedImageFile = null;
191
- imageUploadInput.value = '';
192
- imagePreviewContainer.style.display = 'none';
193
- imagePreview.src = '#';
194
- };
195
-
196
- // --- LocalStorage Cache & Sidebar Rendering ---
197
- const saveCache = () => localStorage.setItem('easyfarms_cache', JSON.stringify(conversationsCache));
198
- const loadCache = () => {
199
- const saved = localStorage.getItem('easyfarms_cache');
200
- if (saved) conversationsCache = JSON.parse(saved);
201
- };
202
-
203
- const renderChatHistoryFromAPI = async () => {
204
- try {
205
- const response = await fetch('/history/sessions');
206
- const sessions = await response.json();
207
- chatHistoryList.innerHTML = '';
208
- sessions.reverse().forEach(session => {
209
- if (!conversationsCache[session.session_id]) conversationsCache[session.session_id] = {};
210
- conversationsCache[session.session_id].title = session.title;
211
- const item = document.createElement('div');
212
- item.className = 'chat-history-item';
213
- item.textContent = session.title;
214
- item.dataset.sessionId = session.session_id;
215
- item.addEventListener('click', () => switchChat(session.session_id));
216
- chatHistoryList.appendChild(item);
217
- });
218
- saveCache();
219
- updateActiveChatItem();
220
- } catch (error) { console.error("Failed to render chat history from API:", error); }
221
- };
222
-
223
- const updateActiveChatItem = () => {
224
- document.querySelectorAll('.chat-history-item').forEach(item => {
225
- item.classList.toggle('active', item.dataset.sessionId === currentSessionId);
226
- });
227
- };
228
-
229
- const closeSidebar = () => appContainer.classList.remove('sidebar-visible');
230
-
231
- // --- Start the Application ---
232
- init();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
233
  });
 
1
+ document.addEventListener('DOMContentLoaded', () => {
2
+
3
+ // --- DOM Elements ---
4
+ const chatArea = document.getElementById('chat-area');
5
+ const closeSidebarBtn = document.getElementById('close-sidebar-btn');
6
+ const messageInput = document.getElementById('message-input');
7
+ const sendBtn = document.getElementById('send-btn');
8
+ const newChatBtn = document.getElementById('new-chat-btn');
9
+ const chatMessages = document.getElementById('chat-messages');
10
+ const chatHistoryList = document.getElementById('chat-history-list');
11
+ const menuToggle = document.getElementById('menu-toggle');
12
+ const appContainer = document.getElementById('app-container');
13
+ const chatTitle = document.getElementById('chat-title');
14
+ const imageUploadBtn = document.getElementById('image-upload-btn');
15
+ const imageUploadInput = document.getElementById('image-upload-input');
16
+ const imagePreviewContainer = document.getElementById('image-preview-container');
17
+ const imagePreview = document.getElementById('image-preview');
18
+ const removeImageBtn = document.getElementById('remove-image-btn');
19
+
20
+ // Add search and filter elements (assuming they exist in HTML or create them)
21
+ let searchInput, clearAllBtn;
22
+
23
+ // --- State ---
24
+ let currentChatId = null;
25
+ let conversationsCache = {};
26
+ let selectedImageFile = null;
27
+ let imgbbApiKey = '';
28
+ let firebase = null;
29
+ let firebaseDB = null;
30
+ let useFirebase = false;
31
+
32
+ // --- Firebase Configuration ---
33
+ const initializeFirebase = async () => {
34
+ try {
35
+ // Check if Firebase is available and configured
36
+ if (typeof firebase !== 'undefined' && window.firebaseConfig) {
37
+ const { initializeApp } = await import('https://www.gstatic.com/firebasejs/9.0.0/firebase-app.js');
38
+ const { getFirestore, collection, doc, setDoc, getDoc, getDocs, deleteDoc } = await import('https://www.gstatic.com/firebasejs/9.0.0/firebase-firestore.js');
39
+
40
+ const app = initializeApp(window.firebaseConfig);
41
+ firebaseDB = getFirestore(app);
42
+ useFirebase = true;
43
+
44
+ console.log('Firebase initialized successfully');
45
+ return { setDoc, getDoc, getDocs, deleteDoc, collection, doc };
46
+ } else {
47
+ console.log('Firebase not configured, using localStorage');
48
+ return null;
49
+ }
50
+ } catch (error) {
51
+ console.error('Firebase initialization failed, falling back to localStorage:', error);
52
+ return null;
53
+ }
54
+ };
55
+
56
+ // --- Storage Management ---
57
+ const saveSessionData = async (chatId, data) => {
58
+ try {
59
+ if (useFirebase && firebaseDB) {
60
+ const { setDoc, doc } = await import('https://www.gstatic.com/firebasejs/9.0.0/firebase-firestore.js');
61
+ await setDoc(doc(firebaseDB, 'chat_sessions', chatId), {
62
+ ...data,
63
+ updatedAt: new Date().toISOString()
64
+ });
65
+ console.log('Session saved to Firebase:', chatId);
66
+ } else {
67
+ // Fallback to localStorage
68
+ const sessions = JSON.parse(localStorage.getItem('easyfarms_sessions') || '{}');
69
+ sessions[chatId] = {
70
+ ...data,
71
+ updatedAt: new Date().toISOString()
72
+ };
73
+ localStorage.setItem('easyfarms_sessions', JSON.stringify(sessions));
74
+ console.log('Session saved to localStorage:', chatId);
75
+ }
76
+ } catch (error) {
77
+ console.error('Failed to save session data:', error);
78
+ // Fallback to localStorage
79
+ const sessions = JSON.parse(localStorage.getItem('easyfarms_sessions') || '{}');
80
+ sessions[chatId] = data;
81
+ localStorage.setItem('easyfarms_sessions', JSON.stringify(sessions));
82
+ }
83
+ };
84
+
85
+ const loadSessionData = async (chatId) => {
86
+ try {
87
+ if (useFirebase && firebaseDB) {
88
+ const { getDoc, doc } = await import('https://www.gstatic.com/firebasejs/9.0.0/firebase-firestore.js');
89
+ const docRef = doc(firebaseDB, 'chat_sessions', chatId);
90
+ const docSnap = await getDoc(docRef);
91
+ if (docSnap.exists()) {
92
+ console.log('Session loaded from Firebase:', chatId);
93
+ return docSnap.data();
94
+ }
95
+ } else {
96
+ // Fallback to localStorage
97
+ const sessions = JSON.parse(localStorage.getItem('easyfarms_sessions') || '{}');
98
+ if (sessions[chatId]) {
99
+ console.log('Session loaded from localStorage:', chatId);
100
+ return sessions[chatId];
101
+ }
102
+ }
103
+ return null;
104
+ } catch (error) {
105
+ console.error('Failed to load session data:', error);
106
+ // Fallback to localStorage
107
+ const sessions = JSON.parse(localStorage.getItem('easyfarms_sessions') || '{}');
108
+ return sessions[chatId] || null;
109
+ }
110
+ };
111
+
112
+ const loadAllSessions = async () => {
113
+ try {
114
+ if (useFirebase && firebaseDB) {
115
+ const { getDocs, collection } = await import('https://www.gstatic.com/firebasejs/9.0.0/firebase-firestore.js');
116
+ const querySnapshot = await getDocs(collection(firebaseDB, 'chat_sessions'));
117
+ const sessions = {};
118
+ querySnapshot.forEach((doc) => {
119
+ sessions[doc.id] = doc.data();
120
+ });
121
+ console.log('All sessions loaded from Firebase');
122
+ return sessions;
123
+ } else {
124
+ // Fallback to localStorage
125
+ const sessions = JSON.parse(localStorage.getItem('easyfarms_sessions') || '{}');
126
+ console.log('All sessions loaded from localStorage');
127
+ return sessions;
128
+ }
129
+ } catch (error) {
130
+ console.error('Failed to load all sessions:', error);
131
+ // Fallback to localStorage
132
+ return JSON.parse(localStorage.getItem('easyfarms_sessions') || '{}');
133
+ }
134
+ };
135
+
136
+ const deleteSessionData = async (chatId) => {
137
+ try {
138
+ if (useFirebase && firebaseDB) {
139
+ const { deleteDoc, doc } = await import('https://www.gstatic.com/firebasejs/9.0.0/firebase-firestore.js');
140
+ await deleteDoc(doc(firebaseDB, 'chat_sessions', chatId));
141
+ console.log('Session deleted from Firebase:', chatId);
142
+ } else {
143
+ // Fallback to localStorage
144
+ const sessions = JSON.parse(localStorage.getItem('easyfarms_sessions') || '{}');
145
+ delete sessions[chatId];
146
+ localStorage.setItem('easyfarms_sessions', JSON.stringify(sessions));
147
+ console.log('Session deleted from localStorage:', chatId);
148
+ }
149
+ } catch (error) {
150
+ console.error('Failed to delete session data:', error);
151
+ }
152
+ };
153
+
154
+ // --- Initialization ---
155
+ const init = async () => {
156
+ if (!messageInput || !sendBtn || !imageUploadBtn || !imagePreviewContainer) {
157
+ console.error("Critical UI elements not found. Check HTML IDs.");
158
+ return;
159
+ }
160
+
161
+ // Initialize Firebase
162
+ firebase = await initializeFirebase();
163
+
164
+ await fetchConfig();
165
+ await loadCachedSessions();
166
+ await renderChatHistoryFromAPI();
167
+ setupSearchAndFilter();
168
+
169
+ // Event Listeners
170
+ sendBtn.addEventListener('click', sendMessage);
171
+ messageInput.addEventListener('keydown', e => {
172
+ if (e.key === 'Enter' && !e.shiftKey) {
173
+ e.preventDefault();
174
+ sendMessage();
175
+ }
176
+ });
177
+
178
+ imageUploadBtn.addEventListener('click', () => imageUploadInput.click());
179
+ imageUploadInput.addEventListener('change', handleImageSelect);
180
+ removeImageBtn.addEventListener('click', removeSelectedImage);
181
+
182
+ // Sidebar Listeners
183
+ newChatBtn.addEventListener('click', () => { startNewChat(); closeSidebar(); });
184
+ menuToggle.addEventListener('click', (event) => {
185
+ event.stopPropagation();
186
+ appContainer.classList.toggle('sidebar-visible');
187
+ });
188
+ closeSidebarBtn.addEventListener('click', (event) => {
189
+ event.stopPropagation();
190
+ closeSidebar();
191
+ });
192
+ chatArea.addEventListener('click', () => {
193
+ if (appContainer.classList.contains('sidebar-visible')) {
194
+ closeSidebar();
195
+ }
196
+ });
197
+
198
+ console.log('EasyFarms Chat initialized with new backend integration');
199
+ };
200
+
201
+ const fetchConfig = async () => {
202
+ try {
203
+ const response = await fetch('/config');
204
+ const config = await response.json();
205
+ imgbbApiKey = config.imgbb_api_key;
206
+ if (!imgbbApiKey) {
207
+ console.error("ImgBB API Key is missing.");
208
+ }
209
+ } catch (error) {
210
+ console.error('Failed to fetch config:', error);
211
+ }
212
+ };
213
+
214
+ const setupSearchAndFilter = () => {
215
+ // Create search input if it doesn't exist
216
+ if (!searchInput) {
217
+ searchInput = document.createElement('input');
218
+ searchInput.type = 'text';
219
+ searchInput.placeholder = 'Search chats...';
220
+ searchInput.className = 'search-input';
221
+ searchInput.style.cssText = `
222
+ width: 100%;
223
+ padding: 8px;
224
+ margin: 10px 0;
225
+ border: 1px solid #ddd;
226
+ border-radius: 4px;
227
+ font-size: 14px;
228
+ `;
229
+
230
+ // Insert before chat history list
231
+ chatHistoryList.parentNode.insertBefore(searchInput, chatHistoryList);
232
+ }
233
+
234
+ // Create clear all button if it doesn't exist
235
+ if (!clearAllBtn) {
236
+ clearAllBtn = document.createElement('button');
237
+ clearAllBtn.textContent = 'Clear All Chats';
238
+ clearAllBtn.className = 'clear-all-btn';
239
+ clearAllBtn.style.cssText = `
240
+ width: 100%;
241
+ padding: 8px;
242
+ margin: 5px 0;
243
+ background: #ff4444;
244
+ color: white;
245
+ border: none;
246
+ border-radius: 4px;
247
+ cursor: pointer;
248
+ font-size: 12px;
249
+ `;
250
+
251
+ // Insert after search input
252
+ searchInput.parentNode.insertBefore(clearAllBtn, chatHistoryList);
253
+ }
254
+
255
+ // Add event listeners
256
+ searchInput.addEventListener('input', filterChats);
257
+ clearAllBtn.addEventListener('click', confirmClearAllChats);
258
+ };
259
+
260
+ const filterChats = () => {
261
+ const searchTerm = searchInput.value.toLowerCase();
262
+ const chatItems = chatHistoryList.querySelectorAll('.chat-history-item');
263
+
264
+ chatItems.forEach(item => {
265
+ const title = item.textContent.toLowerCase();
266
+ const shouldShow = title.includes(searchTerm);
267
+ item.style.display = shouldShow ? 'block' : 'none';
268
+ });
269
+
270
+ console.log('Filtered chats with term:', searchTerm);
271
+ };
272
+
273
+ const confirmClearAllChats = () => {
274
+ if (confirm('Are you sure you want to delete all chats? This action cannot be undone.')) {
275
+ clearAllChats();
276
+ }
277
+ };
278
+
279
+ const clearAllChats = async () => {
280
+ try {
281
+ // Get all chat IDs
282
+ const allSessions = await loadAllSessions();
283
+ const chatIds = Object.keys(allSessions);
284
+
285
+ // Delete each chat from backend
286
+ for (const chatId of chatIds) {
287
+ try {
288
+ const response = await fetch(`/chat/${chatId}`, {
289
+ method: 'DELETE'
290
+ });
291
+
292
+ if (response.ok) {
293
+ console.log('Deleted chat from backend:', chatId);
294
+ }
295
+ } catch (error) {
296
+ console.error('Failed to delete chat from backend:', chatId, error);
297
+ }
298
+
299
+ // Delete from local storage
300
+ await deleteSessionData(chatId);
301
+ }
302
+
303
+ // Clear UI and cache
304
+ conversationsCache = {};
305
+ chatHistoryList.innerHTML = '';
306
+ startNewChat();
307
+
308
+ console.log('All chats cleared successfully');
309
+ } catch (error) {
310
+ console.error('Failed to clear all chats:', error);
311
+ }
312
+ };
313
+
314
+ // --- Core Chat Functions ---
315
+ const sendMessage = async () => {
316
+ const messageText = messageInput.value.trim();
317
+ if (!messageText && !selectedImageFile) return;
318
+
319
+ // Display user message immediately
320
+ displayMessage({
321
+ role: 'user',
322
+ content: messageText,
323
+ imageUrl: selectedImageFile
324
+ });
325
+
326
+ const loadingIndicator = displayMessage({
327
+ role: 'assistant',
328
+ content: 'Thinking...',
329
+ isLoading: true
330
+ });
331
+
332
+ try {
333
+ let permanentImageUrl = null;
334
+ if (selectedImageFile) {
335
+ permanentImageUrl = await uploadImageToImgBB(selectedImageFile);
336
+ }
337
+
338
+ // Prepare request data
339
+ const requestData = {
340
+ query: messageText,
341
+ session_id: currentChatId,
342
+ };
343
+
344
+ if (permanentImageUrl) {
345
+ requestData.image_url = permanentImageUrl;
346
+ }
347
+
348
+ // Send to new API endpoint
349
+ const response = await fetch('/chat', {
350
+ method: 'POST',
351
+ headers: {
352
+ 'Content-Type': 'application/json',
353
+ },
354
+ body: JSON.stringify(requestData)
355
+ });
356
+
357
+ if (!response.ok) {
358
+ throw new Error(`HTTP error! status: ${response.status}`);
359
+ }
360
+
361
+ const data = await response.json();
362
+ console.log('Chat response:', data);
363
+
364
+ // Remove loading indicator
365
+ chatMessages.removeChild(loadingIndicator);
366
+
367
+ // Display assistant response
368
+ displayMessage({
369
+ role: 'assistant',
370
+ content: data.response,
371
+ message_id: data.assistant_message_id
372
+ });
373
+
374
+ // Update cache and UI
375
+ await updateCacheWithNewSystem(
376
+ data.chat_id,
377
+ {
378
+ content: messageText,
379
+ imageUrl: permanentImageUrl,
380
+ message_id: data.user_message_id
381
+ },
382
+ {
383
+ content: data.response,
384
+ message_id: data.assistant_message_id
385
+ },
386
+ data.is_new_chat
387
+ );
388
+
389
+ } catch (error) {
390
+ console.error('Error sending message:', error);
391
+
392
+ // Remove loading indicator and show error
393
+ chatMessages.removeChild(loadingIndicator);
394
+
395
+ // Display mock error response (don't save to database)
396
+ displayMessage({
397
+ role: 'assistant',
398
+ content: "I apologize, but I'm having trouble connecting right now. Please check your internet connection and try again. Your chat session is still active.",
399
+ isError: true
400
+ });
401
+ } finally {
402
+ messageInput.value = '';
403
+ messageInput.style.height = 'auto';
404
+ removeSelectedImage();
405
+ }
406
+ };
407
+
408
+ const uploadImageToImgBB = async (imageFile) => {
409
+ if (!imgbbApiKey) throw new Error("ImgBB API Key not configured.");
410
+
411
+ const formData = new FormData();
412
+ formData.append('image', imageFile);
413
+ formData.append('key', imgbbApiKey);
414
+
415
+ const response = await fetch('https://api.imgbb.com/1/upload', {
416
+ method: 'POST',
417
+ body: formData
418
+ });
419
+
420
+ const result = await response.json();
421
+ if (result.success) {
422
+ return result.data.url;
423
+ } else {
424
+ throw new Error(result.error.message || 'Image upload failed.');
425
+ }
426
+ };
427
+
428
+ // --- Chat Management ---
429
+ const startNewChat = () => {
430
+ currentChatId = null;
431
+ chatMessages.innerHTML = `<div class="welcome-message"><h1>EasyFarms Assistant</h1></div>`;
432
+ chatTitle.textContent = "New Chat";
433
+ updateActiveChatItem();
434
+ console.log('Started new chat');
435
+ };
436
+
437
+ const switchChat = async (chatId) => {
438
+ console.log('Switching to chat:', chatId);
439
+ currentChatId = chatId;
440
+ chatMessages.innerHTML = '';
441
+
442
+ // Check cache first
443
+ if (conversationsCache[chatId] && conversationsCache[chatId].messages) {
444
+ conversationsCache[chatId].messages.forEach(displayMessage);
445
+ chatTitle.textContent = conversationsCache[chatId].title || "Chat";
446
+ } else {
447
+ // Load from API
448
+ const loading = displayMessage({
449
+ role: 'assistant',
450
+ content: 'Loading chat history...',
451
+ isLoading: true
452
+ });
453
+
454
+ try {
455
+ const response = await fetch(`/chat/${chatId}/messages`);
456
+
457
+ if (!response.ok) {
458
+ throw new Error(`HTTP error! status: ${response.status}`);
459
+ }
460
+
461
+ const messages = await response.json();
462
+ console.log('Loaded messages for chat:', chatId, messages);
463
+
464
+ // Update cache
465
+ if (!conversationsCache[chatId]) conversationsCache[chatId] = {};
466
+ conversationsCache[chatId].messages = messages;
467
+
468
+ // Save to storage
469
+ await saveSessionData(chatId, conversationsCache[chatId]);
470
+
471
+ // Remove loading and display messages
472
+ chatMessages.removeChild(loading);
473
+ messages.forEach(displayMessage);
474
+
475
+ // Set title from first user message or use cached title
476
+ const sessionData = await loadSessionData(chatId);
477
+ chatTitle.textContent = sessionData?.title || conversationsCache[chatId]?.title || "Chat";
478
+
479
+ } catch (error) {
480
+ console.error('Failed to load chat history:', error);
481
+ chatMessages.removeChild(loading);
482
+ displayMessage({
483
+ role: 'assistant',
484
+ content: "Failed to load chat history. Please try again.",
485
+ isError: true
486
+ });
487
+ }
488
+ }
489
+
490
+ updateActiveChatItem();
491
+ closeSidebar();
492
+ };
493
+
494
+ const deleteChat = async (chatId, chatItem) => {
495
+ if (!confirm('Are you sure you want to delete this chat?')) return;
496
+
497
+ try {
498
+ // Delete from backend
499
+ const response = await fetch(`/chat/${chatId}`, {
500
+ method: 'DELETE'
501
+ });
502
+
503
+ if (response.ok) {
504
+ console.log('Chat deleted from backend:', chatId);
505
+ } else {
506
+ console.error('Failed to delete chat from backend:', chatId);
507
+ }
508
+
509
+ // Delete from storage
510
+ await deleteSessionData(chatId);
511
+
512
+ // Remove from cache
513
+ delete conversationsCache[chatId];
514
+
515
+ // Remove from UI
516
+ chatItem.remove();
517
+
518
+ // If this was the current chat, start a new one
519
+ if (currentChatId === chatId) {
520
+ startNewChat();
521
+ }
522
+
523
+ console.log('Chat deleted successfully:', chatId);
524
+
525
+ } catch (error) {
526
+ console.error('Error deleting chat:', error);
527
+ alert('Failed to delete chat. Please try again.');
528
+ }
529
+ };
530
+
531
+ const updateCacheWithNewSystem = async (chatId, userTurn, assistantTurn, isNewChat) => {
532
+ const previousChatId = currentChatId;
533
+ currentChatId = chatId;
534
+
535
+ if (isNewChat || !conversationsCache[chatId]) {
536
+ const title = (userTurn.content || "Image Query").substring(0, 30) + '...';
537
+ conversationsCache[chatId] = {
538
+ title,
539
+ messages: [],
540
+ created_at: new Date().toISOString()
541
+ };
542
+
543
+ // Add to UI
544
+ const item = createChatHistoryItem(chatId, title);
545
+ chatHistoryList.prepend(item);
546
+
547
+ console.log('Created new chat:', chatId);
548
+ }
549
+
550
+ // Create message objects with IDs
551
+ const userMessage = {
552
+ role: 'user',
553
+ content: userTurn.content,
554
+ message_id: userTurn.message_id,
555
+ timestamp: new Date().toISOString()
556
+ };
557
+ if (userTurn.imageUrl) userMessage.imageUrl = userTurn.imageUrl;
558
+
559
+ const assistantMessage = {
560
+ role: 'assistant',
561
+ content: assistantTurn.content,
562
+ message_id: assistantTurn.message_id,
563
+ timestamp: new Date().toISOString()
564
+ };
565
+
566
+ // Add to cache
567
+ conversationsCache[chatId].messages.push(userMessage, assistantMessage);
568
+ conversationsCache[chatId].updated_at = new Date().toISOString();
569
+
570
+ // Save to storage
571
+ await saveSessionData(chatId, conversationsCache[chatId]);
572
+
573
+ updateActiveChatItem();
574
+ chatTitle.textContent = conversationsCache[chatId].title;
575
+
576
+ console.log('Updated cache for chat:', chatId);
577
+ };
578
+
579
+ const createChatHistoryItem = (chatId, title) => {
580
+ const item = document.createElement('div');
581
+ item.className = 'chat-history-item';
582
+ item.dataset.sessionId = chatId;
583
+
584
+ // Create title element
585
+ const titleElement = document.createElement('span');
586
+ titleElement.textContent = title;
587
+ titleElement.className = 'chat-title';
588
+
589
+ // Create delete button
590
+ const deleteBtn = document.createElement('button');
591
+ deleteBtn.innerHTML = '×';
592
+ deleteBtn.className = 'delete-chat-btn';
593
+ deleteBtn.style.cssText = `
594
+ float: right;
595
+ background: none;
596
+ border: none;
597
+ color: #999;
598
+ cursor: pointer;
599
+ font-size: 18px;
600
+ padding: 0;
601
+ width: 20px;
602
+ height: 20px;
603
+ line-height: 18px;
604
+ `;
605
+ deleteBtn.title = 'Delete chat';
606
+
607
+ // Add event listeners
608
+ titleElement.addEventListener('click', () => switchChat(chatId));
609
+ deleteBtn.addEventListener('click', (e) => {
610
+ e.stopPropagation();
611
+ deleteChat(chatId, item);
612
+ });
613
+
614
+ item.appendChild(titleElement);
615
+ item.appendChild(deleteBtn);
616
+
617
+ return item;
618
+ };
619
+
620
+ const loadCachedSessions = async () => {
621
+ try {
622
+ conversationsCache = await loadAllSessions();
623
+ console.log('Loaded cached sessions:', Object.keys(conversationsCache).length);
624
+ } catch (error) {
625
+ console.error('Failed to load cached sessions:', error);
626
+ conversationsCache = {};
627
+ }
628
+ };
629
+
630
+ const displayMessage = (message) => {
631
+ const { role, content, imageUrl, isLoading, isError, message_id } = message;
632
+ const sender = role || message.sender;
633
+ const messageDiv = document.createElement('div');
634
+ messageDiv.classList.add('message', `${sender}-message`);
635
+
636
+ if (message_id) {
637
+ messageDiv.dataset.messageId = message_id;
638
+ console.log('Displayed message with ID:', message_id);
639
+ }
640
+
641
+ let htmlContent = '';
642
+ const imageSrc = (typeof imageUrl === 'object' && imageUrl instanceof File)
643
+ ? URL.createObjectURL(imageUrl)
644
+ : imageUrl;
645
+
646
+ if (imageSrc) {
647
+ htmlContent += `<img src="${imageSrc}" alt="User upload" class="user-upload">`;
648
+ }
649
+ if (content) {
650
+ htmlContent += marked.parse(content);
651
+ }
652
+
653
+ messageDiv.innerHTML = htmlContent || (isLoading ? '...' : '');
654
+
655
+ if (isLoading) messageDiv.classList.add('loading');
656
+ if (isError) messageDiv.classList.add('error');
657
+
658
+ chatMessages.appendChild(messageDiv);
659
+ chatMessages.scrollTop = chatMessages.scrollHeight;
660
+
661
+ return messageDiv;
662
+ };
663
+
664
+ // --- Image Preview Handling ---
665
+ const handleImageSelect = (event) => {
666
+ const file = event.target.files[0];
667
+ if (file) {
668
+ selectedImageFile = file;
669
+ imagePreview.src = URL.createObjectURL(file);
670
+ imagePreviewContainer.style.display = 'block';
671
+ console.log('Image selected:', file.name);
672
+ }
673
+ };
674
+
675
+ const removeSelectedImage = () => {
676
+ selectedImageFile = null;
677
+ imageUploadInput.value = '';
678
+ imagePreviewContainer.style.display = 'none';
679
+ imagePreview.src = '#';
680
+ console.log('Image removed');
681
+ };
682
+
683
+ // --- API Integration ---
684
+ const renderChatHistoryFromAPI = async () => {
685
+ try {
686
+ const response = await fetch('/chats');
687
+
688
+ if (!response.ok) {
689
+ throw new Error(`HTTP error! status: ${response.status}`);
690
+ }
691
+
692
+ const sessions = await response.json();
693
+ console.log('Loaded chat sessions from API:', sessions.length);
694
+
695
+ chatHistoryList.innerHTML = '';
696
+
697
+ // Sort by updated_at (most recent first)
698
+ sessions.sort((a, b) => new Date(b.updated_at) - new Date(a.updated_at));
699
+
700
+ sessions.forEach(session => {
701
+ // Update cache
702
+ if (!conversationsCache[session.session_id]) {
703
+ conversationsCache[session.session_id] = {};
704
+ }
705
+ conversationsCache[session.session_id].title = session.title;
706
+ conversationsCache[session.session_id].message_count = session.message_count;
707
+ conversationsCache[session.session_id].created_at = session.created_at;
708
+ conversationsCache[session.session_id].updated_at = session.updated_at;
709
+
710
+ // Create UI item
711
+ const item = createChatHistoryItem(session.session_id, session.title);
712
+ chatHistoryList.appendChild(item);
713
+ });
714
+
715
+ // Save updated cache
716
+ for (const [chatId, data] of Object.entries(conversationsCache)) {
717
+ await saveSessionData(chatId, data);
718
+ }
719
+
720
+ updateActiveChatItem();
721
+
722
+ } catch (error) {
723
+ console.error("Failed to render chat history from API:", error);
724
+
725
+ // Fallback to local cache
726
+ console.log('Falling back to local cache...');
727
+ for (const [chatId, data] of Object.entries(conversationsCache)) {
728
+ if (data.title) {
729
+ const item = createChatHistoryItem(chatId, data.title);
730
+ chatHistoryList.appendChild(item);
731
+ }
732
+ }
733
+ }
734
+ };
735
+
736
+ const updateActiveChatItem = () => {
737
+ document.querySelectorAll('.chat-history-item').forEach(item => {
738
+ item.classList.toggle('active', item.dataset.sessionId === currentChatId);
739
+ });
740
+ };
741
+
742
+ const closeSidebar = () => appContainer.classList.remove('sidebar-visible');
743
+
744
+ // --- Start the Application ---
745
+ init();
746
  });