Mohit0199 commited on
Commit
0c9c0ee
Β·
verified Β·
1 Parent(s): ef28587

Promote version 509727d to main

Browse files

Promoted commit 509727d283975ae21c3bb89e1590b61abb075025 to main branch

Files changed (1) hide show
  1. index.html +307 -22
index.html CHANGED
@@ -69,11 +69,48 @@
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>
72
- <button id="settingsBtn" class="flex items-center space-x-2 bg-indigo-800 hover:bg-indigo-700 px-4 py-2 rounded-lg transition-all">
73
- <i data-feather="settings" class="w-5 h-5"></i>
74
- <span>Settings</span>
75
- </button>
 
 
 
 
 
 
 
 
 
 
76
  </header>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  <!-- Settings Modal -->
78
  <div id="settingsModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
79
  <div class="bg-gray-800 rounded-xl p-6 w-full max-w-md">
@@ -135,7 +172,7 @@
135
  <div class="flex justify-center">
136
  <div class="bg-gray-700 bg-opacity-50 px-4 py-2 rounded-lg text-sm text-gray-300">
137
  <i data-feather="info" class="inline mr-2 w-4 h-4"></i>
138
- Select a free model and start chatting
139
  </div>
140
  </div>
141
  </div>
@@ -201,6 +238,37 @@ const settingsBtn = document.getElementById('settingsBtn');
201
  const typingIndicator = document.getElementById('typingIndicator');
202
  const modelIndicator = document.getElementById('modelIndicator');
203
  const themeBtns = document.querySelectorAll('.theme-btn');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
204
  // Chat management
205
  let currentChatId = null;
206
 
@@ -208,24 +276,172 @@ const settingsBtn = document.getElementById('settingsBtn');
208
  return 'chat_' + Date.now().toString(36) + Math.random().toString(36).substr(2, 5);
209
  }
210
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211
  function createNewChat() {
 
 
 
212
  currentChatId = generateChatId();
213
  chatContainer.innerHTML = '';
214
  addMessage('system', 'πŸ†• New chat started.');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
  }
216
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
217
  // Load saved settings
218
  async function loadSettings() {
 
 
219
  const savedTheme = localStorage.getItem('chatRouterTheme') || 'indigo';
 
 
 
 
 
 
 
 
 
 
 
220
  // Apply theme
221
  document.body.className = `bg-gradient-to-br from-${savedTheme}-900 to-purple-900 text-white`;
222
-
223
- // Clear any saved API key on reload
224
- localStorage.removeItem('chatRouterApiKey');
225
- localStorage.removeItem('chatRouterModel');
226
- sendBtn.disabled = true;
227
  }
228
-
229
  // Free models from OpenRouter
230
  const freeModels = [
231
  'alibaba/tongyi-deepresearch-30b-a3b:free',
@@ -264,7 +480,7 @@ async function loadSettings() {
264
  'mistralai/mistral-nemo:free',
265
  'google/gemma-2-9b-it:free'
266
  ];
267
- // Fetch available free models
268
  async function fetchModels(apiKey) {
269
  try {
270
  // Clear existing options
@@ -285,7 +501,11 @@ async function loadSettings() {
285
  option.textContent = modelId;
286
  modelSelect.appendChild(option);
287
  });
288
- } catch (error) {
 
 
 
 
289
  addMessage('system', `Error loading models: ${error.message}`);
290
  }
291
  }
@@ -308,19 +528,27 @@ async function loadSettings() {
308
  // Verify API key by fetching models
309
  await fetchModels(apiKey);
310
 
311
- // If successful, enable chat
 
 
312
  sendBtn.disabled = false;
313
  modelIndicator.textContent = `Using: ${model.split('/').pop()}`;
314
  settingsModal.classList.add('hidden');
315
 
316
  // Add confirmation message to chat
317
- addMessage('system', 'API key verified. You can now start chatting!');
318
- createNewChat();
319
  } catch (error) {
320
  addMessage('system', `Error verifying API key: ${error.message}`);
321
  }
322
  });
323
 
 
 
 
 
 
 
 
324
  // Theme selection
325
  themeBtns.forEach(btn => {
326
  btn.addEventListener('click', () => {
@@ -427,6 +655,54 @@ async function sendMessage() {
427
  typingIndicator.classList.add('hidden');
428
  }
429
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
430
  newSidebarChat.addEventListener('click', createNewChat);
431
 
432
  // Sidebar toggle for mobile
@@ -439,13 +715,26 @@ messageInput.addEventListener('keydown', (e) => {
439
  sendMessage();
440
  }
441
  });
442
- sendBtn.addEventListener('click', sendMessage);
 
 
 
 
 
 
443
  // Initialize
444
  loadSettings();
 
445
 
446
  // Welcome message
447
  setTimeout(() => {
448
- addMessage('system', 'Welcome to ChatRouter! Please enter your OpenRouter API key & select a model in settings to start chatting.');
 
 
 
 
 
 
449
  }, 1000);
450
 
451
  // Close sidebar when clicking outside on mobile
@@ -454,10 +743,6 @@ messageInput.addEventListener('keydown', (e) => {
454
  sidebar.classList.add('hidden');
455
  }
456
  });
457
-
458
- // Clear chat history on page load
459
- localStorage.removeItem('chatRouterApiKey');
460
- localStorage.removeItem('chatRouterModel');
461
  </script>
462
  </body>
463
  </html>
 
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>
72
+ <div class="flex items-center space-x-2">
73
+ <button id="newChatBtn" class="flex items-center space-x-2 bg-indigo-800 hover:bg-indigo-700 px-4 py-2 rounded-lg transition-all">
74
+ <i data-feather="plus" class="w-5 h-5"></i>
75
+ <span>New Chat</span>
76
+ </button>
77
+ <button id="settingsBtn" class="flex items-center space-x-2 bg-indigo-800 hover:bg-indigo-700 px-4 py-2 rounded-lg transition-all">
78
+ <i data-feather="settings" class="w-5 h-5"></i>
79
+ <span>Settings</span>
80
+ </button>
81
+ <button id="authBtn" class="flex items-center space-x-2 bg-indigo-800 hover:bg-indigo-700 px-4 py-2 rounded-lg transition-all">
82
+ <i data-feather="user" class="w-5 h-5"></i>
83
+ <span>Login</span>
84
+ </button>
85
+ </div>
86
  </header>
87
+ <!-- Auth Modal -->
88
+ <div id="authModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
89
+ <div class="bg-gray-800 rounded-xl p-6 w-full max-w-md">
90
+ <div class="flex justify-between items-center mb-4">
91
+ <h2 class="text-xl font-bold" id="authModalTitle">Login</h2>
92
+ <button id="closeAuth" class="text-gray-400 hover:text-white">
93
+ <i data-feather="x"></i>
94
+ </button>
95
+ </div>
96
+ <div id="authForm" class="space-y-4">
97
+ <div id="registerFields" class="hidden">
98
+ <label class="block text-sm font-medium mb-1">Username</label>
99
+ <input type="text" id="regUsername" placeholder="Choose a username" class="w-full bg-gray-700 border border-gray-600 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500">
100
+ </div>
101
+ <div>
102
+ <label class="block text-sm font-medium mb-1">Password</label>
103
+ <input type="password" id="authPassword" placeholder="Enter your password" class="w-full bg-gray-700 border border-gray-600 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500">
104
+ </div>
105
+ <button id="authActionBtn" class="w-full bg-indigo-600 hover:bg-indigo-500 text-white py-2 rounded-lg transition-colors">Login</button>
106
+ <div class="text-center text-sm">
107
+ <span id="authToggleText">Don't have an account? </span>
108
+ <button id="authToggleBtn" class="text-indigo-400 hover:text-indigo-300">Register</button>
109
+ </div>
110
+ </div>
111
+ </div>
112
+ </div>
113
+
114
  <!-- Settings Modal -->
115
  <div id="settingsModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
116
  <div class="bg-gray-800 rounded-xl p-6 w-full max-w-md">
 
172
  <div class="flex justify-center">
173
  <div class="bg-gray-700 bg-opacity-50 px-4 py-2 rounded-lg text-sm text-gray-300">
174
  <i data-feather="info" class="inline mr-2 w-4 h-4"></i>
175
+ Select a free model from settings to start chatting
176
  </div>
177
  </div>
178
  </div>
 
238
  const typingIndicator = document.getElementById('typingIndicator');
239
  const modelIndicator = document.getElementById('modelIndicator');
240
  const themeBtns = document.querySelectorAll('.theme-btn');
241
+ // User management
242
+ let isRegistering = false;
243
+
244
+ function updateAuthUI() {
245
+ const loggedInUser = localStorage.getItem('chatRouterUser');
246
+ if (loggedInUser) {
247
+ authBtn.innerHTML = `<i data-feather="log-out" class="w-5 h-5"></i><span>Logout</span>`;
248
+ authBtn.dataset.state = 'logout';
249
+ document.querySelector('#newChatBtn').disabled = false;
250
+ document.querySelector('#settingsBtn').disabled = false;
251
+ sendBtn.disabled = false;
252
+ } else {
253
+ authBtn.innerHTML = `<i data-feather="user" class="w-5 h-5"></i><span>Login</span>`;
254
+ authBtn.dataset.state = 'login';
255
+ document.querySelector('#newChatBtn').disabled = true;
256
+ document.querySelector('#settingsBtn').disabled = true;
257
+ sendBtn.disabled = true;
258
+ showAuthModal();
259
+ }
260
+ feather.replace();
261
+ }
262
+
263
+ function showAuthModal(register = false) {
264
+ isRegistering = register;
265
+ authModalTitle.textContent = register ? 'Register' : 'Login';
266
+ authActionBtn.textContent = register ? 'Register' : 'Login';
267
+ authToggleText.textContent = register ? 'Already have an account? ' : 'Don\'t have an account? ';
268
+ authToggleBtn.textContent = register ? 'Login' : 'Register';
269
+ registerFields.classList.toggle('hidden', !register);
270
+ authModal.classList.remove('hidden');
271
+ }
272
  // Chat management
273
  let currentChatId = null;
274
 
 
276
  return 'chat_' + Date.now().toString(36) + Math.random().toString(36).substr(2, 5);
277
  }
278
 
279
+ function loginUser(username, password) {
280
+ // Simple auth - store user in localStorage
281
+ localStorage.setItem('chatRouterUser', username);
282
+ localStorage.setItem(`chatRouterPass_${username}`, password); // Not secure for production!
283
+ updateAuthUI();
284
+
285
+ // Create a new chat session
286
+ createNewChat();
287
+
288
+ // Load user's chat list
289
+ loadChatList(username);
290
+ }
291
+ function logoutUser() {
292
+ const username = localStorage.getItem('chatRouterUser');
293
+ localStorage.removeItem('chatRouterUser');
294
+ updateAuthUI();
295
+
296
+ // Clear current chat and sidebar
297
+ chatContainer.innerHTML = '';
298
+ chatList.innerHTML = '';
299
+ currentChatId = null;
300
+ addMessage('system', 'Please log in to start chatting.');
301
+ }
302
  function createNewChat() {
303
+ const username = localStorage.getItem('chatRouterUser');
304
+ if (!username) return;
305
+
306
  currentChatId = generateChatId();
307
  chatContainer.innerHTML = '';
308
  addMessage('system', 'πŸ†• New chat started.');
309
+ saveChatHistory(username, currentChatId);
310
+ addChatToList(username, currentChatId, 'New Chat');
311
+ }
312
+
313
+ function loadChatHistory(username, chatId) {
314
+ const history = localStorage.getItem(`chatHistory_${username}_${chatId}`);
315
+ if (history) {
316
+ chatContainer.innerHTML = history;
317
+ chatContainer.scrollTop = chatContainer.scrollHeight;
318
+ currentChatId = chatId;
319
+ }
320
+ }
321
+
322
+ function saveChatHistory(username, chatId) {
323
+ if (!chatId) chatId = currentChatId;
324
+ if (!username || !chatId) return;
325
+
326
+ localStorage.setItem(`chatHistory_${username}_${chatId}`, chatContainer.innerHTML);
327
+
328
+ // Update the chat title if it's the first message
329
+ const messages = chatContainer.querySelectorAll('.message-bubble');
330
+ if (messages.length === 1) {
331
+ updateChatTitle(username, chatId, messages[0].textContent.substring(0, 30));
332
+ }
333
  }
334
 
335
+ function loadChatList(username) {
336
+ chatList.innerHTML = '';
337
+ const chats = [];
338
+
339
+ // Find all chats for this user
340
+ for (let i = 0; i < localStorage.length; i++) {
341
+ const key = localStorage.key(i);
342
+ if (key.startsWith(`chatHistory_${username}_`)) {
343
+ const chatId = key.split('_')[2];
344
+ const title = localStorage.getItem(`chatTitle_${username}_${chatId}`) || 'New Chat';
345
+ chats.push({ id: chatId, title });
346
+ }
347
+ }
348
+
349
+ // Sort by most recent first
350
+ chats.sort((a, b) => b.id.localeCompare(a.id));
351
+
352
+ // Add to sidebar
353
+ chats.forEach(chat => {
354
+ addChatToList(username, chat.id, chat.title);
355
+ });
356
+ }
357
+
358
+ function addChatToList(username, chatId, title) {
359
+ const chatItem = document.createElement('div');
360
+ chatItem.className = 'flex justify-between items-center p-2 hover:bg-gray-700 rounded-lg cursor-pointer';
361
+ chatItem.dataset.chatId = chatId;
362
+
363
+ const titleSpan = document.createElement('span');
364
+ titleSpan.className = 'truncate flex-1';
365
+ titleSpan.textContent = title;
366
+
367
+ const actionsDiv = document.createElement('div');
368
+ actionsDiv.className = 'flex space-x-2';
369
+
370
+ const renameBtn = document.createElement('button');
371
+ renameBtn.className = 'text-gray-400 hover:text-white';
372
+ renameBtn.innerHTML = '<i data-feather="edit-2" class="w-4 h-4"></i>';
373
+ renameBtn.onclick = (e) => {
374
+ e.stopPropagation();
375
+ renameChat(username, chatId, titleSpan);
376
+ };
377
+
378
+ const deleteBtn = document.createElement('button');
379
+ deleteBtn.className = 'text-gray-400 hover:text-white';
380
+ deleteBtn.innerHTML = '<i data-feather="trash-2" class="w-4 h-4"></i>';
381
+ deleteBtn.onclick = (e) => {
382
+ e.stopPropagation();
383
+ deleteChat(username, chatId, chatItem);
384
+ };
385
+
386
+ actionsDiv.appendChild(renameBtn);
387
+ actionsDiv.appendChild(deleteBtn);
388
+ chatItem.appendChild(titleSpan);
389
+ chatItem.appendChild(actionsDiv);
390
+
391
+ chatItem.addEventListener('click', () => {
392
+ loadChatHistory(username, chatId);
393
+ });
394
+
395
+ chatList.prepend(chatItem);
396
+ feather.replace();
397
+ }
398
+
399
+ function updateChatTitle(username, chatId, title) {
400
+ localStorage.setItem(`chatTitle_${username}_${chatId}`, title);
401
+ const chatItem = chatList.querySelector(`[data-chat-id="${chatId}"]`);
402
+ if (chatItem) {
403
+ chatItem.querySelector('span').textContent = title;
404
+ }
405
+ }
406
+
407
+ function renameChat(username, chatId, titleElement) {
408
+ const newTitle = prompt('Enter new chat title:', titleElement.textContent);
409
+ if (newTitle && newTitle.trim()) {
410
+ localStorage.setItem(`chatTitle_${username}_${chatId}`, newTitle.trim());
411
+ titleElement.textContent = newTitle.trim();
412
+ }
413
+ }
414
+
415
+ function deleteChat(username, chatId, element) {
416
+ if (confirm('Are you sure you want to delete this chat?')) {
417
+ localStorage.removeItem(`chatHistory_${username}_${chatId}`);
418
+ localStorage.removeItem(`chatTitle_${username}_${chatId}`);
419
+ element.remove();
420
+
421
+ if (currentChatId === chatId) {
422
+ createNewChat();
423
+ }
424
+ }
425
+ }
426
  // Load saved settings
427
  async function loadSettings() {
428
+ const savedApiKey = localStorage.getItem('chatRouterApiKey');
429
+ const savedModel = localStorage.getItem('chatRouterModel');
430
  const savedTheme = localStorage.getItem('chatRouterTheme') || 'indigo';
431
+
432
+ if (savedApiKey) {
433
+ apiKeyInput.value = savedApiKey;
434
+ sendBtn.disabled = false;
435
+ await fetchModels(savedApiKey); // Load models when API key exists
436
+ }
437
+ if (savedModel) {
438
+ modelSelect.value = savedModel;
439
+ modelIndicator.textContent = `Using: ${savedModel.split('/').pop()}`;
440
+ }
441
+
442
  // Apply theme
443
  document.body.className = `bg-gradient-to-br from-${savedTheme}-900 to-purple-900 text-white`;
 
 
 
 
 
444
  }
 
445
  // Free models from OpenRouter
446
  const freeModels = [
447
  'alibaba/tongyi-deepresearch-30b-a3b:free',
 
480
  'mistralai/mistral-nemo:free',
481
  'google/gemma-2-9b-it:free'
482
  ];
483
+ // Fetch available free models
484
  async function fetchModels(apiKey) {
485
  try {
486
  // Clear existing options
 
501
  option.textContent = modelId;
502
  modelSelect.appendChild(option);
503
  });
504
+ // Verify API key is still needed for these free models
505
+ if (!apiKey) {
506
+ addMessage('system', 'Free models selected. Some features may require an API key.');
507
+ }
508
+ } catch (error) {
509
  addMessage('system', `Error loading models: ${error.message}`);
510
  }
511
  }
 
528
  // Verify API key by fetching models
529
  await fetchModels(apiKey);
530
 
531
+ // If successful, save settings
532
+ localStorage.setItem('chatRouterApiKey', apiKey);
533
+ localStorage.setItem('chatRouterModel', model);
534
  sendBtn.disabled = false;
535
  modelIndicator.textContent = `Using: ${model.split('/').pop()}`;
536
  settingsModal.classList.add('hidden');
537
 
538
  // Add confirmation message to chat
539
+ addMessage('system', 'Settings saved successfully. You can now start chatting!');
 
540
  } catch (error) {
541
  addMessage('system', `Error verifying API key: ${error.message}`);
542
  }
543
  });
544
 
545
+ // Load models when API key changes
546
+ apiKeyInput.addEventListener('blur', async () => {
547
+ const apiKey = apiKeyInput.value.trim();
548
+ if (apiKey) {
549
+ await fetchModels(apiKey);
550
+ }
551
+ });
552
  // Theme selection
553
  themeBtns.forEach(btn => {
554
  btn.addEventListener('click', () => {
 
655
  typingIndicator.classList.add('hidden');
656
  }
657
  }
658
+ // Event listeners
659
+ authBtn.addEventListener('click', () => {
660
+ if (authBtn.dataset.state === 'logout') {
661
+ logoutUser();
662
+ } else {
663
+ showAuthModal();
664
+ }
665
+ });
666
+
667
+ closeAuth.addEventListener('click', () => {
668
+ authModal.classList.add('hidden');
669
+ });
670
+
671
+ authToggleBtn.addEventListener('click', () => {
672
+ showAuthModal(!isRegistering);
673
+ });
674
+
675
+ authActionBtn.addEventListener('click', () => {
676
+ if (isRegistering) {
677
+ const username = regUsername.value.trim();
678
+ const password = authPassword.value.trim();
679
+
680
+ if (!username || !password) {
681
+ alert('Please enter both username and password');
682
+ return;
683
+ }
684
+
685
+ if (localStorage.getItem(`chatRouterPass_${username}`)) {
686
+ alert('Username already exists');
687
+ return;
688
+ }
689
+
690
+ loginUser(username, password);
691
+ } else {
692
+ const username = regUsername.value.trim();
693
+ const password = authPassword.value.trim();
694
+ const storedPass = localStorage.getItem(`chatRouterPass_${username}`);
695
+
696
+ if (!storedPass || storedPass !== password) {
697
+ alert('Invalid username or password');
698
+ return;
699
+ }
700
+
701
+ loginUser(username, password);
702
+ }
703
+ authModal.classList.add('hidden');
704
+ });
705
+ newChatBtn.addEventListener('click', createNewChat);
706
  newSidebarChat.addEventListener('click', createNewChat);
707
 
708
  // Sidebar toggle for mobile
 
715
  sendMessage();
716
  }
717
  });
718
+ sendBtn.addEventListener('click', () => {
719
+ const username = localStorage.getItem('chatRouterUser');
720
+ if (username) {
721
+ sendMessage();
722
+ saveChatHistory(username, currentChatId);
723
+ }
724
+ });
725
  // Initialize
726
  loadSettings();
727
+ updateAuthUI();
728
 
729
  // Welcome message
730
  setTimeout(() => {
731
+ if (!localStorage.getItem('chatRouterUser')) {
732
+ addMessage('system', 'Welcome to ChatRouter! Please log in to start chatting.');
733
+ } else if (!localStorage.getItem('chatRouterModel')) {
734
+ addMessage('system', 'Please select a free model from settings to begin.');
735
+ } else if (!currentChatId) {
736
+ createNewChat();
737
+ }
738
  }, 1000);
739
 
740
  // Close sidebar when clicking outside on mobile
 
743
  sidebar.classList.add('hidden');
744
  }
745
  });
 
 
 
 
746
  </script>
747
  </body>
748
  </html>