Mohit0199 commited on
Commit
0d2c178
Β·
verified Β·
1 Parent(s): bdae63f

Upgrade my existing ChatRouter project to include a full ChatGPT-style chat sidebar on the left side of the screen.

Browse files

The sidebar should include:
- A **β€œNew Chat”** button at the top to start a fresh conversation.
- A scrollable list of past chat titles (for the logged-in user).
- Each chat title should show the first few words of the first user message.
- When a chat title is clicked, it loads that conversation into the main chat area.
- Ability to **rename** or **delete** a chat (optional with small icons beside titles).
- The sidebar should collapse/expand on smaller screens with a hamburger icon.

---

### πŸ’¬ Functional Details
- Each chat should be saved in `localStorage` as part of the logged-in user’s data, using keys like:
`chatHistory_<username>_<chatId>`
- When the user clicks β€œNew Chat”, a new conversation with a unique ID should be created and displayed.
- The sidebar should update dynamically to show the new chat entry.
- Maintain support for multiple chats per user.
- When the user logs out, sidebar and chats should reset.

Files changed (1) hide show
  1. index.html +204 -33
index.html CHANGED
@@ -8,8 +8,24 @@
8
  <script src="https://cdn.tailwindcss.com"></script>
9
  <script src="https://unpkg.com/feather-icons"></script>
10
  <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
11
- <style>
12
- .chat-height {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  height: calc(100vh - 200px);
14
  }
15
  .message-bubble {
@@ -42,9 +58,14 @@
42
  </style>
43
  </head>
44
  <body class="bg-gradient-to-br from-indigo-900 to-purple-900 text-white">
45
- <div class="container mx-auto px-4 py-8 max-w-5xl">
46
- <header class="flex justify-between items-center mb-8">
47
- <div class="flex items-center space-x-3">
 
 
 
 
 
48
  <i data-feather="cpu" class="w-8 h-8 text-indigo-300"></i>
49
  <h1 class="text-2xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-indigo-300 to-purple-300">ChatRouter</h1>
50
  </div>
@@ -132,10 +153,24 @@
132
  </div>
133
  </div>
134
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135
 
136
- <!-- Chat Container -->
137
- <div class="bg-gray-800 bg-opacity-60 backdrop-blur-lg rounded-2xl shadow-xl overflow-hidden">
138
- <!-- Chat Messages -->
139
  <div id="chatContainer" class="chat-height overflow-y-auto p-4 space-y-4">
140
  <div class="flex justify-center">
141
  <div class="bg-gray-700 bg-opacity-50 px-4 py-2 rounded-lg text-sm text-gray-300">
@@ -166,17 +201,24 @@
166
  </div>
167
  </div>
168
  </div>
 
169
 
170
  <footer class="mt-8 text-center text-sm text-gray-400">
171
- <p>ChatRouter connects you to the best AI models via OpenRouter</p>
172
  <p class="mt-1">Your conversations stay private and secure</p>
173
  </footer>
174
  </div>
175
 
176
  <script>
177
  feather.replace();
178
- // DOM Elements
179
- const authBtn = document.getElementById('authBtn');
 
 
 
 
 
 
180
  const authModal = document.getElementById('authModal');
181
  const authModalTitle = document.getElementById('authModalTitle');
182
  const closeAuth = document.getElementById('closeAuth');
@@ -230,6 +272,12 @@ const settingsBtn = document.getElementById('settingsBtn');
230
  registerFields.classList.toggle('hidden', !register);
231
  authModal.classList.remove('hidden');
232
  }
 
 
 
 
 
 
233
 
234
  function loginUser(username, password) {
235
  // Simple auth - store user in localStorage
@@ -237,45 +285,148 @@ const settingsBtn = document.getElementById('settingsBtn');
237
  localStorage.setItem(`chatRouterPass_${username}`, password); // Not secure for production!
238
  updateAuthUI();
239
 
240
- // Load user's chat history
241
- loadChatHistory(username);
242
 
243
- // Show welcome message
244
- addMessage('system', `Welcome back, ${username}!`);
245
  }
246
-
247
  function logoutUser() {
248
  const username = localStorage.getItem('chatRouterUser');
249
  localStorage.removeItem('chatRouterUser');
250
  updateAuthUI();
251
 
252
- // Clear current chat
253
  chatContainer.innerHTML = '';
 
 
254
  addMessage('system', 'Please log in to start chatting.');
255
  }
 
 
 
 
 
 
 
 
 
 
256
 
257
- function loadChatHistory(username) {
258
- const history = localStorage.getItem(`chatHistory_${username}`);
259
  if (history) {
260
  chatContainer.innerHTML = history;
261
  chatContainer.scrollTop = chatContainer.scrollHeight;
 
262
  }
263
  }
264
 
265
- function saveChatHistory(username) {
266
- localStorage.setItem(`chatHistory_${username}`, chatContainer.innerHTML);
 
 
 
 
 
 
 
 
 
267
  }
268
 
269
- function clearCurrentChat() {
270
- const username = localStorage.getItem('chatRouterUser');
271
- if (username) {
272
- chatContainer.innerHTML = '';
273
- addMessage('system', 'πŸ†• New chat started.');
274
- saveChatHistory(username);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
275
  }
276
  }
277
 
278
- // Load saved settings
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
279
  async function loadSettings() {
280
  const savedApiKey = localStorage.getItem('chatRouterApiKey');
281
  const savedModel = localStorage.getItem('chatRouterModel');
@@ -538,27 +689,47 @@ async function sendMessage() {
538
  }
539
  authModal.classList.add('hidden');
540
  });
541
-
542
- newChatBtn.addEventListener('click', clearCurrentChat);
 
 
 
 
 
543
  messageInput.addEventListener('keydown', (e) => {
544
  if (e.key === 'Enter' && !e.shiftKey) {
545
  e.preventDefault();
546
  sendMessage();
547
  }
548
  });
549
-
550
- sendBtn.addEventListener('click', sendMessage);
 
 
 
 
 
551
  // Initialize
552
  loadSettings();
553
  updateAuthUI();
 
554
  // Welcome message
555
  setTimeout(() => {
556
  if (!localStorage.getItem('chatRouterUser')) {
557
  addMessage('system', 'Welcome to ChatRouter! Please log in to start chatting.');
558
  } else if (!localStorage.getItem('chatRouterModel')) {
559
  addMessage('system', 'Please select a free model from settings to begin.');
560
- }
 
 
561
  }, 1000);
 
 
 
 
 
 
 
562
  </script>
563
  </body>
564
  </html>
 
8
  <script src="https://cdn.tailwindcss.com"></script>
9
  <script src="https://unpkg.com/feather-icons"></script>
10
  <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
11
+ <style>
12
+ /* Mobile sidebar positioning */
13
+ @media (max-width: 767px) {
14
+ #sidebar {
15
+ position: fixed;
16
+ top: 0;
17
+ left: 0;
18
+ height: 100vh;
19
+ width: 280px;
20
+ z-index: 30;
21
+ transform: translateX(-100%);
22
+ transition: transform 0.3s ease;
23
+ }
24
+ #sidebar:not(.hidden) {
25
+ transform: translateX(0);
26
+ }
27
+ }
28
+ .chat-height {
29
  height: calc(100vh - 200px);
30
  }
31
  .message-bubble {
 
58
  </style>
59
  </head>
60
  <body class="bg-gradient-to-br from-indigo-900 to-purple-900 text-white">
61
+ <div class="container mx-auto px-4 py-8 max-w-7xl">
62
+ <!-- Sidebar Toggle for Mobile -->
63
+ <button id="sidebarToggle" class="fixed left-4 top-4 z-40 md:hidden bg-gray-800 p-2 rounded-lg">
64
+ <i data-feather="menu"></i>
65
+ </button>
66
+
67
+ <header class="flex justify-between items-center mb-8 ml-12 md:ml-0">
68
+ <div class="flex items-center space-x-3">
69
  <i data-feather="cpu" class="w-8 h-8 text-indigo-300"></i>
70
  <h1 class="text-2xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-indigo-300 to-purple-300">ChatRouter</h1>
71
  </div>
 
153
  </div>
154
  </div>
155
  </div>
156
+ <div class="flex flex-col md:flex-row gap-6">
157
+ <!-- Sidebar -->
158
+ <div id="sidebar" class="hidden md:block w-64 bg-gray-800 bg-opacity-60 backdrop-blur-lg rounded-2xl shadow-xl h-[80vh] p-4 overflow-y-auto">
159
+ <div class="flex flex-col h-full">
160
+ <button id="newSidebarChat" class="flex items-center space-x-2 bg-indigo-600 hover:bg-indigo-500 text-white px-4 py-2 rounded-lg mb-4">
161
+ <i data-feather="plus" class="w-4 h-4"></i>
162
+ <span>New Chat</span>
163
+ </button>
164
+
165
+ <div id="chatList" class="flex-1 overflow-y-auto space-y-1">
166
+ <!-- Chat items will be dynamically added here -->
167
+ </div>
168
+ </div>
169
+ </div>
170
 
171
+ <!-- Main Chat Container -->
172
+ <div class="flex-1 bg-gray-800 bg-opacity-60 backdrop-blur-lg rounded-2xl shadow-xl overflow-hidden">
173
+ <!-- Chat Messages -->
174
  <div id="chatContainer" class="chat-height overflow-y-auto p-4 space-y-4">
175
  <div class="flex justify-center">
176
  <div class="bg-gray-700 bg-opacity-50 px-4 py-2 rounded-lg text-sm text-gray-300">
 
201
  </div>
202
  </div>
203
  </div>
204
+ </div>
205
 
206
  <footer class="mt-8 text-center text-sm text-gray-400">
207
+ <p>ChatRouter connects you to the best AI models via OpenRouter</p>
208
  <p class="mt-1">Your conversations stay private and secure</p>
209
  </footer>
210
  </div>
211
 
212
  <script>
213
  feather.replace();
214
+ // DOM Elements - New Sidebar Elements
215
+ const sidebarToggle = document.getElementById('sidebarToggle');
216
+ const sidebar = document.getElementById('sidebar');
217
+ const newSidebarChat = document.getElementById('newSidebarChat');
218
+ const chatList = document.getElementById('chatList');
219
+
220
+ // DOM Elements - Original
221
+ const authBtn = document.getElementById('authBtn');
222
  const authModal = document.getElementById('authModal');
223
  const authModalTitle = document.getElementById('authModalTitle');
224
  const closeAuth = document.getElementById('closeAuth');
 
272
  registerFields.classList.toggle('hidden', !register);
273
  authModal.classList.remove('hidden');
274
  }
275
+ // Chat management
276
+ let currentChatId = null;
277
+
278
+ function generateChatId() {
279
+ return 'chat_' + Date.now().toString(36) + Math.random().toString(36).substr(2, 5);
280
+ }
281
 
282
  function loginUser(username, password) {
283
  // Simple auth - store user in localStorage
 
285
  localStorage.setItem(`chatRouterPass_${username}`, password); // Not secure for production!
286
  updateAuthUI();
287
 
288
+ // Create a new chat session
289
+ createNewChat();
290
 
291
+ // Load user's chat list
292
+ loadChatList(username);
293
  }
 
294
  function logoutUser() {
295
  const username = localStorage.getItem('chatRouterUser');
296
  localStorage.removeItem('chatRouterUser');
297
  updateAuthUI();
298
 
299
+ // Clear current chat and sidebar
300
  chatContainer.innerHTML = '';
301
+ chatList.innerHTML = '';
302
+ currentChatId = null;
303
  addMessage('system', 'Please log in to start chatting.');
304
  }
305
+ function createNewChat() {
306
+ const username = localStorage.getItem('chatRouterUser');
307
+ if (!username) return;
308
+
309
+ currentChatId = generateChatId();
310
+ chatContainer.innerHTML = '';
311
+ addMessage('system', 'πŸ†• New chat started.');
312
+ saveChatHistory(username, currentChatId);
313
+ addChatToList(username, currentChatId, 'New Chat');
314
+ }
315
 
316
+ function loadChatHistory(username, chatId) {
317
+ const history = localStorage.getItem(`chatHistory_${username}_${chatId}`);
318
  if (history) {
319
  chatContainer.innerHTML = history;
320
  chatContainer.scrollTop = chatContainer.scrollHeight;
321
+ currentChatId = chatId;
322
  }
323
  }
324
 
325
+ function saveChatHistory(username, chatId) {
326
+ if (!chatId) chatId = currentChatId;
327
+ if (!username || !chatId) return;
328
+
329
+ localStorage.setItem(`chatHistory_${username}_${chatId}`, chatContainer.innerHTML);
330
+
331
+ // Update the chat title if it's the first message
332
+ const messages = chatContainer.querySelectorAll('.message-bubble');
333
+ if (messages.length === 1) {
334
+ updateChatTitle(username, chatId, messages[0].textContent.substring(0, 30));
335
+ }
336
  }
337
 
338
+ function loadChatList(username) {
339
+ chatList.innerHTML = '';
340
+ const chats = [];
341
+
342
+ // Find all chats for this user
343
+ for (let i = 0; i < localStorage.length; i++) {
344
+ const key = localStorage.key(i);
345
+ if (key.startsWith(`chatHistory_${username}_`)) {
346
+ const chatId = key.split('_')[2];
347
+ const title = localStorage.getItem(`chatTitle_${username}_${chatId}`) || 'New Chat';
348
+ chats.push({ id: chatId, title });
349
+ }
350
+ }
351
+
352
+ // Sort by most recent first
353
+ chats.sort((a, b) => b.id.localeCompare(a.id));
354
+
355
+ // Add to sidebar
356
+ chats.forEach(chat => {
357
+ addChatToList(username, chat.id, chat.title);
358
+ });
359
+ }
360
+
361
+ function addChatToList(username, chatId, title) {
362
+ const chatItem = document.createElement('div');
363
+ chatItem.className = 'flex justify-between items-center p-2 hover:bg-gray-700 rounded-lg cursor-pointer';
364
+ chatItem.dataset.chatId = chatId;
365
+
366
+ const titleSpan = document.createElement('span');
367
+ titleSpan.className = 'truncate flex-1';
368
+ titleSpan.textContent = title;
369
+
370
+ const actionsDiv = document.createElement('div');
371
+ actionsDiv.className = 'flex space-x-2';
372
+
373
+ const renameBtn = document.createElement('button');
374
+ renameBtn.className = 'text-gray-400 hover:text-white';
375
+ renameBtn.innerHTML = '<i data-feather="edit-2" class="w-4 h-4"></i>';
376
+ renameBtn.onclick = (e) => {
377
+ e.stopPropagation();
378
+ renameChat(username, chatId, titleSpan);
379
+ };
380
+
381
+ const deleteBtn = document.createElement('button');
382
+ deleteBtn.className = 'text-gray-400 hover:text-white';
383
+ deleteBtn.innerHTML = '<i data-feather="trash-2" class="w-4 h-4"></i>';
384
+ deleteBtn.onclick = (e) => {
385
+ e.stopPropagation();
386
+ deleteChat(username, chatId, chatItem);
387
+ };
388
+
389
+ actionsDiv.appendChild(renameBtn);
390
+ actionsDiv.appendChild(deleteBtn);
391
+ chatItem.appendChild(titleSpan);
392
+ chatItem.appendChild(actionsDiv);
393
+
394
+ chatItem.addEventListener('click', () => {
395
+ loadChatHistory(username, chatId);
396
+ });
397
+
398
+ chatList.prepend(chatItem);
399
+ feather.replace();
400
+ }
401
+
402
+ function updateChatTitle(username, chatId, title) {
403
+ localStorage.setItem(`chatTitle_${username}_${chatId}`, title);
404
+ const chatItem = chatList.querySelector(`[data-chat-id="${chatId}"]`);
405
+ if (chatItem) {
406
+ chatItem.querySelector('span').textContent = title;
407
  }
408
  }
409
 
410
+ function renameChat(username, chatId, titleElement) {
411
+ const newTitle = prompt('Enter new chat title:', titleElement.textContent);
412
+ if (newTitle && newTitle.trim()) {
413
+ localStorage.setItem(`chatTitle_${username}_${chatId}`, newTitle.trim());
414
+ titleElement.textContent = newTitle.trim();
415
+ }
416
+ }
417
+
418
+ function deleteChat(username, chatId, element) {
419
+ if (confirm('Are you sure you want to delete this chat?')) {
420
+ localStorage.removeItem(`chatHistory_${username}_${chatId}`);
421
+ localStorage.removeItem(`chatTitle_${username}_${chatId}`);
422
+ element.remove();
423
+
424
+ if (currentChatId === chatId) {
425
+ createNewChat();
426
+ }
427
+ }
428
+ }
429
+ // Load saved settings
430
  async function loadSettings() {
431
  const savedApiKey = localStorage.getItem('chatRouterApiKey');
432
  const savedModel = localStorage.getItem('chatRouterModel');
 
689
  }
690
  authModal.classList.add('hidden');
691
  });
692
+ newChatBtn.addEventListener('click', createNewChat);
693
+ newSidebarChat.addEventListener('click', createNewChat);
694
+
695
+ // Sidebar toggle for mobile
696
+ sidebarToggle.addEventListener('click', () => {
697
+ sidebar.classList.toggle('hidden');
698
+ });
699
  messageInput.addEventListener('keydown', (e) => {
700
  if (e.key === 'Enter' && !e.shiftKey) {
701
  e.preventDefault();
702
  sendMessage();
703
  }
704
  });
705
+ sendBtn.addEventListener('click', () => {
706
+ const username = localStorage.getItem('chatRouterUser');
707
+ if (username) {
708
+ sendMessage();
709
+ saveChatHistory(username, currentChatId);
710
+ }
711
+ });
712
  // Initialize
713
  loadSettings();
714
  updateAuthUI();
715
+
716
  // Welcome message
717
  setTimeout(() => {
718
  if (!localStorage.getItem('chatRouterUser')) {
719
  addMessage('system', 'Welcome to ChatRouter! Please log in to start chatting.');
720
  } else if (!localStorage.getItem('chatRouterModel')) {
721
  addMessage('system', 'Please select a free model from settings to begin.');
722
+ } else if (!currentChatId) {
723
+ createNewChat();
724
+ }
725
  }, 1000);
726
+
727
+ // Close sidebar when clicking outside on mobile
728
+ document.addEventListener('click', (e) => {
729
+ if (window.innerWidth < 768 && !sidebar.contains(e.target) && e.target !== sidebarToggle) {
730
+ sidebar.classList.add('hidden');
731
+ }
732
+ });
733
  </script>
734
  </body>
735
  </html>