Mohit0199 commited on
Commit
5387391
Β·
verified Β·
1 Parent(s): 17d21e3

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.

---

### 🎨 Design Guidelines
- Use TailwindCSS styling consistent with the existing ChatRouter theme.
- Sidebar background: slightly darker tone of the current theme.
- Active chat highlighted with a lighter border or gradient.
- Smooth transitions between chats (fade in/out).
- Keep the chat header (Settings, Logout) visible at the top of the main area.

---

### βš™οΈ Integration Notes
- Keep all existing authentication, chat logic, and OpenRouter API integration untouched.
- Just add the sidebar functionality and link it to the existing chat system.
- Make sure when a user switches between chats, their messages load from `localStorage` immediately.

Files changed (1) hide show
  1. index.html +207 -18
index.html CHANGED
@@ -42,8 +42,27 @@
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>
@@ -136,9 +155,9 @@
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">
142
  <i data-feather="info" class="inline mr-2 w-4 h-4"></i>
143
  Select a free model from settings to start chatting
144
  </div>
@@ -172,11 +191,17 @@
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');
@@ -201,6 +226,9 @@ const settingsBtn = document.getElementById('settingsBtn');
201
  const themeBtns = document.querySelectorAll('.theme-btn');
202
  // User management
203
  let isRegistering = false;
 
 
 
204
 
205
  function updateAuthUI() {
206
  const loggedInUser = localStorage.getItem('chatRouterUser');
@@ -210,18 +238,135 @@ const settingsBtn = document.getElementById('settingsBtn');
210
  document.querySelector('#newChatBtn').disabled = false;
211
  document.querySelector('#settingsBtn').disabled = false;
212
  sendBtn.disabled = false;
 
 
213
  } else {
214
  authBtn.innerHTML = `<i data-feather="user" class="w-5 h-5"></i><span>Login</span>`;
215
  authBtn.dataset.state = 'login';
216
  document.querySelector('#newChatBtn').disabled = true;
217
  document.querySelector('#settingsBtn').disabled = true;
218
  sendBtn.disabled = true;
 
 
219
  showAuthModal();
220
  }
221
  feather.replace();
222
  }
223
 
224
- function showAuthModal(register = false) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
225
  isRegistering = register;
226
  authModalTitle.textContent = register ? 'Register' : 'Login';
227
  authActionBtn.textContent = register ? 'Register' : 'Login';
@@ -243,7 +388,6 @@ const settingsBtn = document.getElementById('settingsBtn');
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');
@@ -251,10 +395,10 @@ const settingsBtn = document.getElementById('settingsBtn');
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;
@@ -427,13 +571,13 @@ async function loadSettings() {
427
  chatContainer.appendChild(messageDiv);
428
  chatContainer.scrollTop = chatContainer.scrollHeight;
429
 
430
- // Save to user's chat history
 
 
431
  const username = localStorage.getItem('chatRouterUser');
432
- if (username) {
433
- saveChatHistory(username);
434
- }
435
  }
436
- // Handle API errors gracefully
437
  function handleApiError(error) {
438
  const errorMsg = error.message.toLowerCase();
439
  if (errorMsg.includes('payment') || errorMsg.includes('unauthorized')) {
@@ -491,8 +635,45 @@ async function sendMessage() {
491
  typingIndicator.classList.add('hidden');
492
  }
493
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
494
  // Event listeners
495
- authBtn.addEventListener('click', () => {
496
  if (authBtn.dataset.state === 'logout') {
497
  logoutUser();
498
  } else {
@@ -538,8 +719,7 @@ 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();
@@ -551,14 +731,23 @@ messageInput.addEventListener('keydown', (e) => {
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>
 
42
  </style>
43
  </head>
44
  <body class="bg-gradient-to-br from-indigo-900 to-purple-900 text-white">
45
+ <div class="flex h-screen">
46
+ <!-- Sidebar -->
47
+ <div id="sidebar" class="w-64 bg-gray-900 bg-opacity-80 border-r border-gray-700 flex flex-col transition-all duration-300 md:w-72">
48
+ <div class="p-4 border-b border-gray-700">
49
+ <button id="newSidebarChat" class="w-full flex items-center justify-center space-x-2 bg-indigo-600 hover:bg-indigo-500 text-white py-2 px-3 rounded-lg transition-colors">
50
+ <i data-feather="plus" class="w-4 h-4"></i>
51
+ <span>New Chat</span>
52
+ </button>
53
+ <button id="toggleSidebar" class="md:hidden mt-2 w-full flex items-center justify-center text-gray-400 hover:text-white py-1">
54
+ <i data-feather="chevron-left" class="w-4 h-4"></i>
55
+ </button>
56
+ </div>
57
+ <div id="chatList" class="flex-1 overflow-y-auto p-2 space-y-1">
58
+ <!-- Chat list items will be dynamically inserted here -->
59
+ </div>
60
+ </div>
61
+
62
+ <!-- Main Content -->
63
+ <div class="flex-1 overflow-y-auto">
64
+ <div class="container mx-auto px-4 py-8 max-w-5xl">
65
+ <header class="flex justify-between items-center mb-8">
66
  <div class="flex items-center space-x-3">
67
  <i data-feather="cpu" class="w-8 h-8 text-indigo-300"></i>
68
  <h1 class="text-2xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-indigo-300 to-purple-300">ChatRouter</h1>
 
155
  <!-- Chat Container -->
156
  <div class="bg-gray-800 bg-opacity-60 backdrop-blur-lg rounded-2xl shadow-xl overflow-hidden">
157
  <!-- Chat Messages -->
158
+ <div id="chatContainer" class="chat-height overflow-y-auto p-4 space-y-4 transition-opacity duration-300">
159
  <div class="flex justify-center">
160
+ <div class="bg-gray-700 bg-opacity-50 px-4 py-2 rounded-lg text-sm text-gray-300">
161
  <i data-feather="info" class="inline mr-2 w-4 h-4"></i>
162
  Select a free model from settings to start chatting
163
  </div>
 
191
  <p class="mt-1">Your conversations stay private and secure</p>
192
  </footer>
193
  </div>
194
+ </div>
195
+ </div>
196
 
197
  <script>
198
  feather.replace();
199
  // DOM Elements
200
+ const sidebar = document.getElementById('sidebar');
201
+ const toggleSidebar = document.getElementById('toggleSidebar');
202
+ const chatList = document.getElementById('chatList');
203
+ const newSidebarChat = document.getElementById('newSidebarChat');
204
+ const authBtn = document.getElementById('authBtn');
205
  const authModal = document.getElementById('authModal');
206
  const authModalTitle = document.getElementById('authModalTitle');
207
  const closeAuth = document.getElementById('closeAuth');
 
226
  const themeBtns = document.querySelectorAll('.theme-btn');
227
  // User management
228
  let isRegistering = false;
229
+ // Chat state management
230
+ let currentChatId = null;
231
+ let sidebarCollapsed = false;
232
 
233
  function updateAuthUI() {
234
  const loggedInUser = localStorage.getItem('chatRouterUser');
 
238
  document.querySelector('#newChatBtn').disabled = false;
239
  document.querySelector('#settingsBtn').disabled = false;
240
  sendBtn.disabled = false;
241
+ loadChatList(loggedInUser);
242
+ if (!currentChatId) createNewChat();
243
  } else {
244
  authBtn.innerHTML = `<i data-feather="user" class="w-5 h-5"></i><span>Login</span>`;
245
  authBtn.dataset.state = 'login';
246
  document.querySelector('#newChatBtn').disabled = true;
247
  document.querySelector('#settingsBtn').disabled = true;
248
  sendBtn.disabled = true;
249
+ chatList.innerHTML = '';
250
+ currentChatId = null;
251
  showAuthModal();
252
  }
253
  feather.replace();
254
  }
255
 
256
+ // Chat list management
257
+ function loadChatList(username) {
258
+ chatList.innerHTML = '';
259
+ const chatKeys = Object.keys(localStorage)
260
+ .filter(key => key.startsWith(`chatHistory_${username}_`));
261
+
262
+ if (chatKeys.length === 0) {
263
+ createNewChat();
264
+ return;
265
+ }
266
+
267
+ chatKeys.forEach(key => {
268
+ const chatId = key.split('_')[2];
269
+ const chatData = JSON.parse(localStorage.getItem(key));
270
+ const firstMessage = chatData.messages.find(m => m.role === 'user')?.content || 'New Chat';
271
+ const chatTitle = firstMessage.length > 30 ? firstMessage.substring(0, 30) + '...' : firstMessage;
272
+
273
+ const chatItem = document.createElement('div');
274
+ chatItem.className = `flex items-center justify-between p-3 rounded-lg cursor-pointer hover:bg-gray-800 transition-colors ${currentChatId === chatId ? 'bg-gray-700 border-l-4 border-indigo-500' : ''}`;
275
+ chatItem.dataset.chatId = chatId;
276
+ chatItem.innerHTML = `
277
+ <span class="truncate">${chatTitle}</span>
278
+ <div class="flex space-x-2 opacity-0 group-hover:opacity-100">
279
+ <button class="text-gray-400 hover:text-white edit-chat" data-chat-id="${chatId}">
280
+ <i data-feather="edit-2" class="w-4 h-4"></i>
281
+ </button>
282
+ <button class="text-gray-400 hover:text-red-400 delete-chat" data-chat-id="${chatId}">
283
+ <i data-feather="trash" class="w-4 h-4"></i>
284
+ </button>
285
+ </div>
286
+ `;
287
+
288
+ chatItem.addEventListener('click', () => loadChat(chatId));
289
+ chatList.appendChild(chatItem);
290
+ });
291
+ feather.replace();
292
+ }
293
+
294
+ function createNewChat() {
295
+ const username = localStorage.getItem('chatRouterUser');
296
+ if (!username) return;
297
+
298
+ currentChatId = Date.now().toString();
299
+ const newChat = {
300
+ id: currentChatId,
301
+ createdAt: new Date().toISOString(),
302
+ messages: []
303
+ };
304
+
305
+ localStorage.setItem(`chatHistory_${username}_${currentChatId}`, JSON.stringify(newChat));
306
+ loadChatList(username);
307
+ loadChat(currentChatId);
308
+ addMessage('system', 'πŸ†• New chat started. Say hello!');
309
+ }
310
+
311
+ function loadChat(chatId) {
312
+ const username = localStorage.getItem('chatRouterUser');
313
+ if (!username) return;
314
+
315
+ currentChatId = chatId;
316
+ const chatData = JSON.parse(localStorage.getItem(`chatHistory_${username}_${chatId}`));
317
+
318
+ // Fade out current chat
319
+ chatContainer.style.opacity = '0';
320
+
321
+ setTimeout(() => {
322
+ chatContainer.innerHTML = '';
323
+ if (chatData.messages.length === 0) {
324
+ addMessage('system', 'Chat loaded. Start the conversation!');
325
+ } else {
326
+ chatData.messages.forEach(msg => {
327
+ addMessage(msg.role, msg.content);
328
+ });
329
+ }
330
+ loadChatList(username);
331
+ chatContainer.style.opacity = '1';
332
+ }, 200);
333
+ }
334
+
335
+ function saveCurrentChat() {
336
+ const username = localStorage.getItem('chatRouterUser');
337
+ if (!username || !currentChatId) return;
338
+
339
+ const messages = [];
340
+ document.querySelectorAll('#chatContainer > div').forEach(msgDiv => {
341
+ const isUser = msgDiv.classList.contains('justify-end');
342
+ const content = msgDiv.querySelector('.message-bubble').textContent;
343
+ messages.push({
344
+ role: isUser ? 'user' : 'assistant',
345
+ content: content
346
+ });
347
+ });
348
+
349
+ const chatData = {
350
+ id: currentChatId,
351
+ createdAt: new Date().toISOString(),
352
+ messages: messages
353
+ };
354
+
355
+ localStorage.setItem(`chatHistory_${username}_${currentChatId}`, JSON.stringify(chatData));
356
+ }
357
+
358
+ function deleteChat(chatId) {
359
+ const username = localStorage.getItem('chatRouterUser');
360
+ if (!username || !confirm('Delete this chat permanently?')) return;
361
+
362
+ localStorage.removeItem(`chatHistory_${username}_${chatId}`);
363
+ if (currentChatId === chatId) {
364
+ createNewChat();
365
+ } else {
366
+ loadChatList(username);
367
+ }
368
+ }
369
+ function showAuthModal(register = false) {
370
  isRegistering = register;
371
  authModalTitle.textContent = register ? 'Register' : 'Login';
372
  authActionBtn.textContent = register ? 'Register' : 'Login';
 
388
  // Show welcome message
389
  addMessage('system', `Welcome back, ${username}!`);
390
  }
 
391
  function logoutUser() {
392
  const username = localStorage.getItem('chatRouterUser');
393
  localStorage.removeItem('chatRouterUser');
 
395
 
396
  // Clear current chat
397
  chatContainer.innerHTML = '';
398
+ currentChatId = null;
399
  addMessage('system', 'Please log in to start chatting.');
400
  }
401
+ function loadChatHistory(username) {
 
402
  const history = localStorage.getItem(`chatHistory_${username}`);
403
  if (history) {
404
  chatContainer.innerHTML = history;
 
571
  chatContainer.appendChild(messageDiv);
572
  chatContainer.scrollTop = chatContainer.scrollHeight;
573
 
574
+ // Save to current chat
575
+ saveCurrentChat();
576
+ // Update chat list to reflect new message
577
  const username = localStorage.getItem('chatRouterUser');
578
+ if (username) loadChatList(username);
 
 
579
  }
580
+ // Handle API errors gracefully
581
  function handleApiError(error) {
582
  const errorMsg = error.message.toLowerCase();
583
  if (errorMsg.includes('payment') || errorMsg.includes('unauthorized')) {
 
635
  typingIndicator.classList.add('hidden');
636
  }
637
  }
638
+ // Sidebar toggle
639
+ toggleSidebar.addEventListener('click', () => {
640
+ sidebarCollapsed = !sidebarCollapsed;
641
+ if (sidebarCollapsed) {
642
+ sidebar.classList.add('hidden', 'md:flex');
643
+ toggleSidebar.innerHTML = '<i data-feather="chevron-right" class="w-4 h-4"></i>';
644
+ } else {
645
+ sidebar.classList.remove('hidden');
646
+ toggleSidebar.innerHTML = '<i data-feather="chevron-left" class="w-4 h-4"></i>';
647
+ }
648
+ feather.replace();
649
+ });
650
+
651
+ // Chat list events
652
+ newSidebarChat.addEventListener('click', createNewChat);
653
+
654
+ // Delegate events for edit/delete buttons
655
+ document.addEventListener('click', (e) => {
656
+ if (e.target.classList.contains('delete-chat') || e.target.closest('.delete-chat')) {
657
+ const chatId = e.target.closest('.delete-chat').dataset.chatId;
658
+ deleteChat(chatId);
659
+ }
660
+ if (e.target.classList.contains('edit-chat') || e.target.closest('.edit-chat')) {
661
+ const chatId = e.target.closest('.edit-chat').dataset.chatId;
662
+ const chatItem = document.querySelector(`[data-chat-id="${chatId}"]`);
663
+ const currentTitle = chatItem.querySelector('span').textContent;
664
+ const newTitle = prompt('Edit chat title:', currentTitle);
665
+ if (newTitle && newTitle !== currentTitle) {
666
+ chatItem.querySelector('span').textContent = newTitle;
667
+ const username = localStorage.getItem('chatRouterUser');
668
+ const chatData = JSON.parse(localStorage.getItem(`chatHistory_${username}_${chatId}`));
669
+ chatData.title = newTitle;
670
+ localStorage.setItem(`chatHistory_${username}_${chatId}`, JSON.stringify(chatData));
671
+ }
672
+ }
673
+ });
674
+
675
  // Event listeners
676
+ authBtn.addEventListener('click', () => {
677
  if (authBtn.dataset.state === 'logout') {
678
  logoutUser();
679
  } else {
 
719
  }
720
  authModal.classList.add('hidden');
721
  });
722
+ newChatBtn.addEventListener('click', createNewChat);
 
723
  messageInput.addEventListener('keydown', (e) => {
724
  if (e.key === 'Enter' && !e.shiftKey) {
725
  e.preventDefault();
 
731
  // Initialize
732
  loadSettings();
733
  updateAuthUI();
734
+
735
  // Welcome message
736
  setTimeout(() => {
737
  if (!localStorage.getItem('chatRouterUser')) {
738
  addMessage('system', 'Welcome to ChatRouter! Please log in to start chatting.');
739
  } else if (!localStorage.getItem('chatRouterModel')) {
740
  addMessage('system', 'Please select a free model from settings to begin.');
741
+ }
742
  }, 1000);
743
+
744
+ // Auto-collapse sidebar on mobile
745
+ if (window.innerWidth < 768) {
746
+ sidebarCollapsed = true;
747
+ sidebar.classList.add('hidden', 'md:flex');
748
+ toggleSidebar.innerHTML = '<i data-feather="chevron-right" class="w-4 h-4"></i>';
749
+ feather.replace();
750
+ }
751
  </script>
752
  </body>
753
  </html>