kokofixcomputers commited on
Commit
e6a7e66
·
verified ·
1 Parent(s): 70b5c44

can you add a sidebar for more features such as cube root, greatest common factor and a lot more scientific features? and make it so that even pressing the sceietific feature like squre root not just directly becore like equal cause it will just perform the action right away. add peranthese too - Follow Up Deployment

Browse files
Files changed (1) hide show
  1. index.html +563 -1028
index.html CHANGED
@@ -3,1133 +3,668 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>NexusChat - Advanced AI Client</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
8
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
- <script>
10
- tailwind.config = {
11
- theme: {
12
- extend: {
13
- colors: {
14
- primary: {
15
- 50: '#f0f9ff',
16
- 100: '#e0f2fe',
17
- 200: '#bae6fd',
18
- 300: '#7dd3fc',
19
- 400: '#38bdf8',
20
- 500: '#0ea5e9',
21
- 600: '#0284c7',
22
- 700: '#0369a1',
23
- 800: '#075985',
24
- 900: '#0c4a6e',
25
- },
26
- dark: {
27
- 800: '#1e293b',
28
- 900: '#0f172a',
29
- }
30
- }
31
- }
32
- }
33
- }
34
- </script>
35
  <style>
36
- /* Custom scrollbar */
37
- ::-webkit-scrollbar {
38
- width: 6px;
39
  }
40
- ::-webkit-scrollbar-track {
41
- background: #f1f1f1;
42
  }
43
- ::-webkit-scrollbar-thumb {
44
- background: #888;
45
- border-radius: 3px;
46
  }
47
- ::-webkit-scrollbar-thumb:hover {
48
- background: #555;
49
  }
50
-
51
- /* Dark mode scrollbar */
52
- .dark ::-webkit-scrollbar-track {
53
- background: #1e293b;
54
- }
55
- .dark ::-webkit-scrollbar-thumb {
56
- background: #64748b;
57
- }
58
-
59
- /* Chat message typing animation */
60
- @keyframes typing {
61
- from { width: 0 }
62
- to { width: 100% }
63
  }
64
-
65
- .typing-animation {
66
- overflow: hidden;
67
- white-space: nowrap;
68
- animation: typing 2s steps(40, end);
69
  }
70
-
71
- /* Smooth transitions */
72
- .transition-all {
73
- transition: all 0.3s ease;
74
  }
75
  </style>
76
  </head>
77
- <body class="bg-gray-50 dark:bg-dark-900 text-gray-800 dark:text-gray-200 min-h-screen">
78
- <div class="flex h-screen overflow-hidden">
79
- <!-- Sidebar -->
80
- <div class="w-64 bg-white dark:bg-dark-800 border-r border-gray-200 dark:border-gray-700 flex flex-col">
81
- <div class="p-4 border-b border-gray-200 dark:border-gray-700">
82
- <button id="newChatBtn" class="w-full flex items-center justify-center gap-2 bg-primary-500 hover:bg-primary-600 text-white py-2 px-4 rounded-lg transition-all">
83
- <i class="fas fa-plus"></i>
84
- <span>New Chat</span>
85
- </button>
86
- </div>
87
-
88
- <div class="flex-1 overflow-y-auto p-2" id="chatList">
89
- <!-- Chat items will be added here dynamically -->
90
- </div>
91
-
92
- <div class="p-4 border-t border-gray-200 dark:border-gray-700">
93
- <div class="flex items-center gap-3 mb-4 cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-700 p-2 rounded-lg transition-all">
94
- <div class="w-8 h-8 rounded-full bg-primary-500 flex items-center justify-center text-white">
95
- <i class="fas fa-user"></i>
96
  </div>
97
- <span class="font-medium">User Account</span>
98
  </div>
99
 
100
- <div id="settingsBtn" class="flex items-center gap-3 cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-700 p-2 rounded-lg transition-all">
101
- <div class="w-8 h-8 rounded-full bg-gray-200 dark:bg-gray-600 flex items-center justify-center">
102
- <i class="fas fa-cog"></i>
103
- </div>
104
- <span>Settings</span>
105
- </div>
106
- </div>
107
- </div>
108
-
109
- <!-- Main Content -->
110
- <div class="flex-1 flex flex-col overflow-hidden">
111
- <!-- Header -->
112
- <header class="bg-white dark:bg-dark-800 border-b border-gray-200 dark:border-gray-700 p-4 flex items-center justify-between">
113
- <div class="flex items-center gap-2">
114
- <button id="sidebarToggle" class="md:hidden text-gray-500 dark:text-gray-400">
115
- <i class="fas fa-bars text-xl"></i>
116
- </button>
117
- <h1 class="text-xl font-bold">NexusChat</h1>
118
  </div>
119
 
120
- <div class="flex items-center gap-4">
121
- <div class="relative">
122
- <button id="modelDropdownBtn" class="flex items-center gap-2 bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 py-2 px-3 rounded-lg transition-all">
123
- <span id="currentModel">GPT-4</span>
124
- <i class="fas fa-chevron-down text-xs"></i>
125
- </button>
126
-
127
- <div id="modelDropdown" class="hidden absolute right-0 mt-2 w-48 bg-white dark:bg-dark-800 rounded-lg shadow-lg border border-gray-200 dark:border-gray-700 z-10">
128
- <div class="p-2">
129
- <div class="text-xs uppercase text-gray-500 dark:text-gray-400 px-2 py-1">Default Models</div>
130
- <div class="model-option cursor-pointer px-3 py-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded" data-model="gpt-3.5">GPT-3.5</div>
131
- <div class="model-option cursor-pointer px-3 py-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded" data-model="gpt-4">GPT-4</div>
132
-
133
- <div class="text-xs uppercase text-gray-500 dark:text-gray-400 px-2 py-1 mt-2">Custom Models</div>
134
- <div id="customModelsList">
135
- <!-- Custom models will be added here -->
136
- </div>
137
-
138
- <div class="border-t border-gray-200 dark:border-gray-700 mt-2 pt-2">
139
- <div id="addModelBtn" class="cursor-pointer px-3 py-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded flex items-center gap-2 text-primary-500">
140
- <i class="fas fa-plus"></i>
141
- <span>Add Custom Model</span>
142
- </div>
143
- </div>
144
- </div>
145
- </div>
146
- </div>
147
-
148
- <button id="darkModeToggle" class="text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200">
149
- <i class="fas fa-moon dark:hidden"></i>
150
- <i class="fas fa-sun hidden dark:block"></i>
151
  </button>
152
- </div>
153
- </header>
154
-
155
- <!-- Chat Area -->
156
- <div class="flex-1 overflow-y-auto p-4 bg-gray-50 dark:bg-dark-900" id="chatArea">
157
- <div class="max-w-3xl mx-auto">
158
- <!-- Welcome message -->
159
- <div id="welcomeMessage" class="text-center py-10">
160
- <div class="w-16 h-16 mx-auto mb-4 bg-primary-500 rounded-full flex items-center justify-center text-white">
161
- <i class="fas fa-robot text-2xl"></i>
162
- </div>
163
- <h2 class="text-2xl font-bold mb-2">Welcome to NexusChat</h2>
164
- <p class="text-gray-600 dark:text-gray-400 mb-6">Start a new conversation or select one from your history</p>
165
- <button id="quickStartBtn" class="bg-primary-500 hover:bg-primary-600 text-white py-2 px-6 rounded-lg transition-all">
166
- Quick Start
167
- </button>
168
- </div>
169
-
170
- <!-- Chat messages will be added here dynamically -->
171
  </div>
172
  </div>
173
 
174
- <!-- Input Area -->
175
- <div class="p-4 border-t border-gray-200 dark:border-gray-700 bg-white dark:bg-dark-800">
176
- <div class="max-w-3xl mx-auto">
177
- <form id="messageForm" class="relative">
178
- <textarea id="messageInput" rows="1" class="w-full p-4 pr-16 bg-gray-100 dark:bg-gray-700 rounded-lg resize-none focus:outline-none focus:ring-2 focus:ring-primary-500" placeholder="Type your message here..."></textarea>
179
- <button type="submit" class="absolute right-4 bottom-4 w-8 h-8 bg-primary-500 hover:bg-primary-600 text-white rounded-full flex items-center justify-center transition-all">
180
- <i class="fas fa-paper-plane"></i>
181
- </button>
182
- </form>
183
-
184
- <div class="text-xs text-gray-500 dark:text-gray-400 mt-2 text-center">
185
- NexusChat may produce inaccurate information about people, places, or facts.
186
- </div>
187
- </div>
188
- </div>
189
- </div>
190
- </div>
191
-
192
- <!-- Add Model Modal -->
193
- <div id="addModelModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
194
- <div class="bg-white dark:bg-dark-800 rounded-lg shadow-xl w-full max-w-md p-6">
195
- <div class="flex justify-between items-center mb-4">
196
- <h3 class="text-lg font-bold">Add Custom Model</h3>
197
- <button id="closeModelModal" class="text-gray-500 hover:text-gray-700 dark:hover:text-gray-300">
198
- <i class="fas fa-times"></i>
199
- </button>
 
 
 
 
 
 
 
 
 
 
 
200
  </div>
201
 
202
- <form id="addModelForm">
203
- <div class="mb-4">
204
- <label for="modelName" class="block text-sm font-medium mb-1">Model Name</label>
205
- <input type="text" id="modelName" class="w-full p-2 border border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-700 focus:ring-primary-500 focus:border-primary-500" placeholder="e.g. My Custom Model" required>
 
 
 
206
  </div>
207
 
208
- <div class="mb-4">
209
- <label for="modelEndpoint" class="block text-sm font-medium mb-1">API Endpoint</label>
210
- <input type="text" id="modelEndpoint" class="w-full p-2 border border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-700 focus:ring-primary-500 focus:border-primary-500" placeholder="https://api.example.com/v1/chat" required>
 
 
 
 
 
 
211
  </div>
212
 
213
  <div class="mb-4">
214
- <label for="modelKey" class="block text-sm font-medium mb-1">API Key (optional)</label>
215
- <input type="password" id="modelKey" class="w-full p-2 border border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-700 focus:ring-primary-500 focus:border-primary-500" placeholder="sk-...">
216
- </div>
217
-
218
- <div class="mb-4 flex items-center">
219
- <input type="checkbox" id="modelStreaming" class="w-4 h-4 text-primary-600 rounded border-gray-300 focus:ring-primary-500 dark:border-gray-600 dark:bg-gray-700">
220
- <label for="modelStreaming" class="ml-2 text-sm font-medium text-gray-700 dark:text-gray-300">Supports Streaming</label>
221
  </div>
222
- <input type="hidden" id="editModelId" value="">
223
 
224
- <div class="flex justify-end gap-2">
225
- <button type="button" id="cancelAddModel" class="px-4 py-2 border border-gray-300 dark:border-gray-600 rounded hover:bg-gray-100 dark:hover:bg-gray-700 transition-all">
226
- Cancel
227
- </button>
228
- <button type="submit" class="px-4 py-2 bg-primary-500 hover:bg-primary-600 text-white rounded transition-all">
229
- Add Model
230
- </button>
231
- </div>
232
- </form>
233
- </div>
234
- </div>
235
-
236
- <!-- API Key Modal -->
237
- <div id="apiKeyModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
238
- <div class="bg-white dark:bg-dark-800 rounded-lg shadow-xl w-full max-w-md p-6">
239
- <div class="flex justify-between items-center mb-4">
240
- <h3 class="text-lg font-bold">OpenAI API Key Required</h3>
241
- <button id="closeApiKeyModal" class="text-gray-500 hover:text-gray-700 dark:hover:text-gray-300">
242
- <i class="fas fa-times"></i>
243
- </button>
244
- </div>
245
-
246
- <p class="mb-4">To use OpenAI models, please enter your API key:</p>
247
-
248
- <form id="apiKeyForm">
249
  <div class="mb-4">
250
- <input type="password" id="apiKeyInput" class="w-full p-2 border border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-700 focus:ring-primary-500 focus:border-primary-500" placeholder="sk-..." required>
251
- </div>
252
-
253
- <div class="flex justify-end gap-2">
254
- <button type="button" id="cancelApiKey" class="px-4 py-2 border border-gray-300 dark:border-gray-600 rounded hover:bg-gray-100 dark:hover:bg-gray-700 transition-all">
255
- Cancel
256
- </button>
257
- <button type="submit" class="px-4 py-2 bg-primary-500 hover:bg-primary-600 text-white rounded transition-all">
258
- Save Key
259
- </button>
260
  </div>
261
- </form>
262
- </div>
263
- </div>
264
-
265
- <!-- Delete Chat Confirmation Modal -->
266
- <div id="deleteChatModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
267
- <div class="bg-white dark:bg-dark-800 rounded-lg shadow-xl w-full max-w-md p-6">
268
- <div class="flex justify-between items-center mb-4">
269
- <h3 class="text-lg font-bold">Delete Chat</h3>
270
- <button id="closeDeleteModal" class="text-gray-500 hover:text-gray-700 dark:hover:text-gray-300">
271
- <i class="fas fa-times"></i>
272
- </button>
273
  </div>
274
-
275
- <p class="mb-6">Are you sure you want to delete this chat? This action cannot be undone.</p>
276
-
277
- <div class="flex justify-end gap-2">
278
- <button type="button" id="cancelDelete" class="px-4 py-2 border border-gray-300 dark:border-gray-600 rounded hover:bg-gray-100 dark:hover:bg-gray-700 transition-all">
279
- Cancel
280
- </button>
281
- <button type="button" id="confirmDelete" class="px-4 py-2 bg-red-500 hover:bg-red-600 text-white rounded transition-all">
282
- Delete
283
- </button>
284
  </div>
285
  </div>
286
  </div>
287
 
288
  <script>
289
- // DOM Elements
290
- const apiKeyModal = document.getElementById('apiKeyModal');
291
- const closeApiKeyModal = document.getElementById('closeApiKeyModal');
292
- const cancelApiKey = document.getElementById('cancelApiKey');
293
- const apiKeyForm = document.getElementById('apiKeyForm');
294
- const apiKeyInput = document.getElementById('apiKeyInput');
295
- const sidebarToggle = document.getElementById('sidebarToggle');
296
- const darkModeToggle = document.getElementById('darkModeToggle');
297
- const newChatBtn = document.getElementById('newChatBtn');
298
- const chatList = document.getElementById('chatList');
299
- const chatArea = document.getElementById('chatArea');
300
- const welcomeMessage = document.getElementById('welcomeMessage');
301
- const messageForm = document.getElementById('messageForm');
302
- const messageInput = document.getElementById('messageInput');
303
- const quickStartBtn = document.getElementById('quickStartBtn');
304
- const modelDropdownBtn = document.getElementById('modelDropdownBtn');
305
- const modelDropdown = document.getElementById('modelDropdown');
306
- const currentModel = document.getElementById('currentModel');
307
- const customModelsList = document.getElementById('customModelsList');
308
- const addModelBtn = document.getElementById('addModelBtn');
309
- const addModelModal = document.getElementById('addModelModal');
310
- const closeModelModal = document.getElementById('closeModelModal');
311
- const cancelAddModel = document.getElementById('cancelAddModel');
312
- const addModelForm = document.getElementById('addModelForm');
313
- const deleteChatModal = document.getElementById('deleteChatModal');
314
- const closeDeleteModal = document.getElementById('closeDeleteModal');
315
- const cancelDelete = document.getElementById('cancelDelete');
316
- const confirmDelete = document.getElementById('confirmDelete');
317
-
318
- // State
319
- let chats = JSON.parse(localStorage.getItem('chats')) || [];
320
- let currentChatId = null;
321
- let models = [
322
- { name: 'GPT-3.5', id: 'gpt-3.5', type: 'default' },
323
- { name: 'GPT-4', id: 'gpt-4', type: 'default' }
324
- ];
325
- let customModels = JSON.parse(localStorage.getItem('customModels')) || [];
326
- let selectedModel = 'gpt-4';
327
- let chatToDelete = null;
328
-
329
- // Initialize
330
- function init() {
331
- // Load custom models
332
- customModels.forEach(model => {
333
- models.push({ ...model, type: 'custom' });
 
 
 
 
 
 
 
 
 
334
  });
335
 
336
- // Render chat list
337
- renderChatList();
338
-
339
- // Set dark mode if preferred
340
- if (localStorage.getItem('darkMode') === 'true' ||
341
- (!localStorage.getItem('darkMode') && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
342
- document.documentElement.classList.add('dark');
343
- }
344
-
345
- // Set current model
346
- const savedModel = localStorage.getItem('selectedModel');
347
- if (savedModel) {
348
- const model = models.find(m => m.id === savedModel);
349
- if (model) {
350
- selectedModel = savedModel;
351
- currentModel.textContent = model.name;
352
- }
353
- }
354
-
355
- // Render custom models in dropdown
356
- renderCustomModels();
357
- }
358
-
359
- // Render chat list
360
- function renderChatList() {
361
- chatList.innerHTML = '';
362
-
363
- if (chats.length === 0) {
364
- chatList.innerHTML = `
365
- <div class="p-4 text-center text-gray-500 dark:text-gray-400">
366
- No chats yet
367
- </div>
368
- `;
369
- return;
370
- }
371
 
372
- chats.forEach(chat => {
373
- const chatElement = document.createElement('div');
374
- chatElement.className = `group relative flex items-center gap-3 p-2 rounded-lg cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-700 transition-all ${currentChatId === chat.id ? 'bg-gray-100 dark:bg-gray-700' : ''}`;
375
- chatElement.dataset.id = chat.id;
376
-
377
- chatElement.innerHTML = `
378
- <div class="w-8 h-8 rounded-full bg-primary-100 dark:bg-primary-900 text-primary-500 dark:text-primary-300 flex items-center justify-center">
379
- <i class="fas fa-comment"></i>
380
- </div>
381
- <div class="flex-1 min-w-0">
382
- <p class="truncate font-medium">${chat.title || 'New Chat'}</p>
383
- <p class="text-xs text-gray-500 dark:text-gray-400 truncate">${chat.lastMessage || ''}</p>
384
- </div>
385
- <button class="delete-chat opacity-0 group-hover:opacity-100 text-gray-400 hover:text-red-500 transition-all p-1">
386
- <i class="fas fa-trash text-sm"></i>
387
- </button>
388
- `;
389
-
390
- chatElement.addEventListener('click', () => loadChat(chat.id));
391
-
392
- const deleteBtn = chatElement.querySelector('.delete-chat');
393
- deleteBtn.addEventListener('click', (e) => {
394
- e.stopPropagation();
395
- showDeleteModal(chat.id);
396
- });
397
-
398
- chatList.appendChild(chatElement);
399
  });
400
- }
401
 
402
- // Render custom models in dropdown
403
- function renderCustomModels() {
404
- customModelsList.innerHTML = '';
405
-
406
- if (customModels.length === 0) {
407
- customModelsList.innerHTML = `
408
- <div class="text-center text-gray-500 dark:text-gray-400 py-2 text-sm">
409
- No custom models added
410
- </div>
411
- `;
412
- return;
413
- }
414
-
415
- customModels.forEach(model => {
416
- const modelElement = document.createElement('div');
417
- modelElement.className = 'model-option cursor-pointer px-3 py-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded flex justify-between items-center';
418
- modelElement.dataset.model = model.id;
 
 
 
419
 
420
- modelElement.innerHTML = `
421
- <span>${model.name}</span>
422
- <div class="flex gap-1">
423
- <button class="edit-model text-gray-400 hover:text-blue-500 transition-all p-1">
424
- <i class="fas fa-edit text-xs"></i>
425
- </button>
426
- <button class="delete-model text-gray-400 hover:text-red-500 transition-all p-1">
427
- <i class="fas fa-trash text-xs"></i>
428
- </button>
429
- </div>
430
- `;
431
 
432
- modelElement.addEventListener('click', (e) => {
433
- if (!e.target.classList.contains('delete-model')) {
434
- selectModel(model.id);
435
- }
436
- });
437
 
438
- const deleteBtn = modelElement.querySelector('.delete-model');
439
- deleteBtn.addEventListener('click', (e) => {
440
- e.stopPropagation();
441
- deleteCustomModel(model.id);
442
- });
443
-
444
- const editBtn = modelElement.querySelector('.edit-model');
445
- editBtn.addEventListener('click', (e) => {
446
- e.stopPropagation();
447
- showEditModelModal(model);
448
  });
449
 
450
- customModelsList.appendChild(modelElement);
451
- });
452
- }
453
-
454
- // Create new chat
455
- function createNewChat() {
456
- const newChat = {
457
- id: Date.now().toString(),
458
- title: 'New Chat',
459
- model: selectedModel,
460
- messages: [],
461
- lastMessage: '',
462
- createdAt: new Date().toISOString()
463
- };
464
-
465
- chats.unshift(newChat);
466
- saveChats();
467
- loadChat(newChat.id);
468
- renderChatList();
469
- }
470
-
471
- // Load chat
472
- function loadChat(chatId) {
473
- currentChatId = chatId;
474
- const chat = chats.find(c => c.id === chatId);
475
-
476
- if (!chat) {
477
- createNewChat();
478
- return;
479
  }
480
 
481
- // Update UI
482
- document.querySelectorAll('[data-id]').forEach(el => {
483
- el.classList.toggle('bg-gray-100', el.dataset.id === chatId);
484
- el.classList.toggle('dark:bg-gray-700', el.dataset.id === chatId);
485
- });
486
-
487
- // Render messages
488
- renderMessages(chat.messages);
489
-
490
- // Hide welcome message
491
- welcomeMessage.classList.add('hidden');
492
- }
493
-
494
- // Render messages
495
- function renderMessages(messages) {
496
- chatArea.innerHTML = '';
497
-
498
- if (messages.length === 0) {
499
- welcomeMessage.classList.remove('hidden');
500
- return;
501
  }
502
 
503
- const messagesContainer = document.createElement('div');
504
- messagesContainer.className = 'space-y-6';
505
-
506
- messages.forEach((msg, index) => {
507
- const messageElement = document.createElement('div');
508
- messageElement.className = `flex gap-4 ${msg.role === 'user' ? 'justify-end' : 'justify-start'}`;
509
-
510
- if (msg.role === 'assistant') {
511
- messageElement.innerHTML = `
512
- <div class="flex-shrink-0 w-8 h-8 rounded-full bg-primary-500 text-white flex items-center justify-center">
513
- <i class="fas fa-robot"></i>
514
- </div>
515
- <div class="max-w-[80%] bg-white dark:bg-gray-800 rounded-lg p-4 shadow-sm relative group">
516
- <div class="prose dark:prose-invert">${msg.content}</div>
517
- <div class="absolute right-2 top-2 opacity-0 group-hover:opacity-100 transition-opacity flex gap-1">
518
- <button class="message-action bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 p-1 rounded text-gray-700 dark:text-gray-300" data-action="regenerate" title="Regenerate">
519
- <i class="fas fa-sync-alt text-xs"></i>
520
- </button>
521
- <button class="message-action bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 p-1 rounded text-gray-700 dark:text-gray-300" data-action="edit" title="Edit">
522
- <i class="fas fa-edit text-xs"></i>
523
- </button>
524
- </div>
525
- </div>
526
- `;
527
  } else {
528
- messageElement.innerHTML = `
529
- <div class="max-w-[80%] bg-primary-500 text-white rounded-lg p-4 shadow-sm relative group">
530
- <div class="prose prose-white">${msg.content}</div>
531
- <div class="absolute right-2 top-2 opacity-0 group-hover:opacity-100 transition-opacity">
532
- <button class="message-action bg-primary-600 hover:bg-primary-700 p-1 rounded text-white" data-action="edit" title="Edit">
533
- <i class="fas fa-edit text-xs"></i>
534
- </button>
535
- </div>
536
- </div>
537
- `;
538
  }
539
-
540
- messagesContainer.appendChild(messageElement);
541
- });
542
-
543
- chatArea.appendChild(messagesContainer);
544
- chatArea.scrollTop = chatArea.scrollHeight;
545
- }
546
-
547
- // Send message
548
- async function sendMessage() {
549
- const message = messageInput.value.trim();
550
- if (!message || !currentChatId) return;
551
-
552
- // Get current chat
553
- const chatIndex = chats.findIndex(c => c.id === currentChatId);
554
- if (chatIndex === -1) return;
555
-
556
- const chat = chats[chatIndex];
557
- const model = models.find(m => m.id === chat.model);
558
- if (!model) return;
559
-
560
- if (model.type === 'default' && !localStorage.getItem('openaiApiKey')) {
561
- showApiKeyModal();
562
- return;
563
  }
564
 
565
- // Add user message to chat
566
- const userMessage = {
567
- role: 'user',
568
- content: message,
569
- timestamp: new Date().toISOString()
570
- };
571
-
572
- chat.messages.push(userMessage);
573
- chat.lastMessage = message.length > 30 ? message.substring(0, 30) + '...' : message;
574
-
575
- // Update UI
576
- renderMessages(chat.messages);
577
- messageInput.value = '';
578
-
579
- // Add loading indicator for assistant response
580
- const loadingElement = document.createElement('div');
581
- loadingElement.className = 'flex gap-4 justify-start';
582
- loadingElement.innerHTML = `
583
- <div class="flex-shrink-0 w-8 h-8 rounded-full bg-primary-500 text-white flex items-center justify-center">
584
- <i class="fas fa-robot"></i>
585
- </div>
586
- <div class="max-w-[80%] bg-white dark:bg-gray-800 rounded-lg p-4 shadow-sm w-full">
587
- <div class="flex gap-2">
588
- <div class="w-2 h-2 rounded-full bg-gray-300 animate-pulse"></div>
589
- <div class="w-2 h-2 rounded-full bg-gray-300 animate-pulse delay-75"></div>
590
- <div class="w-2 h-2 rounded-full bg-gray-300 animate-pulse delay-150"></div>
591
- </div>
592
- </div>
593
- `;
594
 
595
- chatArea.appendChild(loadingElement);
596
- chatArea.scrollTop = chatArea.scrollHeight;
 
 
 
 
 
 
 
597
 
598
- try {
599
- // Ensure we have the latest chat reference first
600
- const chatIndex = chats.findIndex(c => c.id === currentChatId);
601
- if (chatIndex === -1) throw new Error('Chat not found');
602
- const currentChat = chats[chatIndex];
 
603
 
604
- // Get the current model
605
- const model = models.find(m => m.id === currentChat.model);
606
- if (!model) throw new Error('Model not found');
607
 
608
- // Prepare messages for API
609
- const messages = currentChat.messages.map(msg => ({
610
- role: msg.role,
611
- content: msg.content
612
- }));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
613
 
614
- let response;
615
- if (model.type === 'default') {
616
- // Call OpenAI API
617
- response = await callOpenAIAPI(messages, model.id);
618
- } else {
619
- // Call custom model API
620
- response = await callCustomModelAPI(messages, model);
 
 
 
 
 
 
 
 
 
 
 
621
  }
622
 
623
- // Remove loading indicator
624
- loadingElement.remove();
625
-
626
- const assistantMessage = {
627
- role: 'assistant',
628
- content: response,
629
- timestamp: new Date().toISOString()
630
  };
 
 
 
 
631
 
632
- currentChat.messages.push(assistantMessage);
633
- saveChats();
634
- renderMessages(chat.messages);
635
-
636
- // Update chat list
637
- renderChatList();
638
- } catch (error) {
639
- console.error('API Error:', error);
640
- loadingElement.remove();
641
 
642
- // Show error message
643
- const errorElement = document.createElement('div');
644
- errorElement.className = 'flex gap-4 justify-start';
645
- errorElement.innerHTML = `
646
- <div class="flex-shrink-0 w-8 h-8 rounded-full bg-red-500 text-white flex items-center justify-center">
647
- <i class="fas fa-exclamation-triangle"></i>
648
- </div>
649
- <div class="max-w-[80%] bg-white dark:bg-gray-800 rounded-lg p-4 shadow-sm">
650
- <div class="text-red-500">Error: ${error.message}</div>
651
- </div>
652
- `;
653
-
654
- chatArea.appendChild(errorElement);
655
- chatArea.scrollTop = chatArea.scrollHeight;
656
  }
657
- }
658
-
659
- // Call OpenAI API
660
- async function callOpenAIAPI(messages, model, apiKey) {
661
- const response = await fetch('https://api.openai.com/v1/chat/completions', {
662
- method: 'POST',
663
- headers: {
664
- 'Content-Type': 'application/json',
665
- 'Authorization': `Bearer ${apiKey}`
666
- },
667
- body: JSON.stringify({
668
- model: model === 'gpt-3.5' ? 'gpt-3.5-turbo' : 'gpt-4',
669
- messages,
670
- temperature: 0.7
671
- })
672
- });
673
 
674
- if (!response.ok) {
675
- const errorData = await response.json();
676
- throw new Error(errorData.error?.message || 'Failed to call OpenAI API');
 
 
677
  }
678
 
679
- const data = await response.json();
680
- return data.choices[0]?.message?.content || 'No response from model';
681
- }
682
-
683
- // Call custom model API
684
- async function callCustomModelAPI(messages, model) {
685
- if (model.streaming) {
686
- // Handle streaming response
687
- const response = await fetch(model.endpoint, {
688
- method: 'POST',
689
- headers: {
690
- 'Content-Type': 'application/json',
691
- ...(model.key ? { 'Authorization': `Bearer ${model.key}` } : {})
692
- },
693
- body: JSON.stringify({
694
- model: model.name,
695
- messages,
696
- temperature: 0.7,
697
- stream: true
698
- })
699
- });
700
-
701
- if (!response.ok) {
702
- const errorText = await response.text();
703
- throw new Error(errorText || 'Failed to call custom model API');
704
  }
705
-
706
- const reader = response.body.getReader();
707
- const decoder = new TextDecoder();
708
- let result = '';
709
-
710
- // Create message element for streaming
711
- const messageElement = document.createElement('div');
712
- messageElement.className = 'flex gap-4 justify-start';
713
- messageElement.innerHTML = `
714
- <div class="flex-shrink-0 w-8 h-8 rounded-full bg-primary-500 text-white flex items-center justify-center">
715
- <i class="fas fa-robot"></i>
716
- </div>
717
- <div class="max-w-[80%] bg-white dark:bg-gray-800 rounded-lg p-4 shadow-sm relative group">
718
- <div class="prose dark:prose-invert" id="streaming-content"></div>
719
- </div>
720
- `;
721
- chatArea.appendChild(messageElement);
722
- const contentElement = messageElement.querySelector('#streaming-content');
723
-
724
- try {
725
- while (true) {
726
- const { done, value } = await reader.read();
727
- if (done) break;
728
-
729
- const chunk = decoder.decode(value);
730
- const lines = chunk.split('\n').filter(line => line.trim() !== '');
731
-
732
- for (const line of lines) {
733
- if (line.startsWith('data: ')) {
734
- const data = line.substring(6);
735
- if (data === '[DONE]') continue;
736
-
737
- try {
738
- const parsed = JSON.parse(data);
739
- const content = parsed.choices?.[0]?.delta?.content || '';
740
- result += content;
741
- contentElement.innerHTML = result;
742
- chatArea.scrollTop = chatArea.scrollHeight;
743
- } catch (e) {
744
- console.error('Error parsing stream data:', e);
745
- }
746
- }
747
- }
748
- }
749
- } catch (e) {
750
- console.error('Stream error:', e);
751
- throw e;
752
  }
753
-
754
- return result;
755
- } else {
756
- // Handle regular response
757
- const response = await fetch(model.endpoint, {
758
- method: 'POST',
759
- headers: {
760
- 'Content-Type': 'application/json',
761
- ...(model.key ? { 'Authorization': `Bearer ${model.key}` } : {})
762
- },
763
- body: JSON.stringify({
764
- model: model.id,
765
- messages,
766
- temperature: 0.7,
767
- stream: false
768
- })
769
- });
770
 
771
- if (!response.ok) {
772
- try {
773
- const errorData = await response.json();
774
- throw new Error(errorData.error?.message || 'Failed to call custom model API');
775
- } catch (e) {
776
- const errorText = await response.text();
777
- throw new Error(errorText || 'Failed to call custom model API');
778
- }
 
 
 
 
 
 
 
 
779
  }
780
 
781
- try {
782
- const data = await response.json();
783
- return data.choices[0]?.message?.content || data.response || 'No response from model';
784
- } catch (e) {
785
- const text = await response.text();
786
- return text || 'No response from model';
 
 
 
 
787
  }
 
 
 
788
  }
789
- }
790
-
791
- // Select model
792
- function selectModel(modelId) {
793
- const model = models.find(m => m.id === modelId);
794
- if (!model) return;
795
-
796
- selectedModel = modelId;
797
- currentModel.textContent = model.name;
798
- modelDropdown.classList.add('hidden');
799
 
800
- // Save to localStorage
801
- localStorage.setItem('selectedModel', modelId);
802
-
803
- // Update current chat's model if one is active
804
- if (currentChatId) {
805
- const chat = chats.find(c => c.id === currentChatId);
806
- if (chat) {
807
- chat.model = modelId;
808
- saveChats();
 
 
 
 
 
 
 
 
 
 
 
 
809
  }
 
810
  }
811
- }
812
 
813
- // Add custom model
814
- function addCustomModel(name, endpoint, key, streaming) {
815
- const newModel = {
816
- id: `custom-${Date.now()}`,
817
- name,
818
- endpoint,
819
- key,
820
- streaming: streaming || false,
821
- type: 'custom'
822
- };
823
-
824
- customModels.push(newModel);
825
- models.push(newModel);
826
-
827
- // Save to localStorage
828
- localStorage.setItem('customModels', JSON.stringify(customModels));
829
-
830
- // Update UI
831
- renderCustomModels();
832
- hideAddModelModal();
833
- }
834
-
835
- // Edit custom model
836
- function editCustomModel(modelId, name, endpoint, key, streaming) {
837
- const modelIndex = customModels.findIndex(m => m.id === modelId);
838
- if (modelIndex === -1) return;
839
-
840
- customModels[modelIndex] = {
841
- ...customModels[modelIndex],
842
- name,
843
- endpoint,
844
- key,
845
- streaming: streaming || false
846
- };
847
-
848
- // Update in models array
849
- const globalModelIndex = models.findIndex(m => m.id === modelId);
850
- if (globalModelIndex !== -1) {
851
- models[globalModelIndex] = {
852
- ...models[globalModelIndex],
853
- name,
854
- endpoint,
855
- key
856
- };
857
  }
858
-
859
- // Save to localStorage
860
- localStorage.setItem('customModels', JSON.stringify(customModels));
861
-
862
- // Update UI
863
- renderCustomModels();
864
- hideAddModelModal();
865
- }
866
 
867
- // Delete custom model
868
- function deleteCustomModel(modelId) {
869
- customModels = customModels.filter(m => m.id !== modelId);
870
- models = models.filter(m => m.id !== modelId);
871
-
872
- // Save to localStorage
873
- localStorage.setItem('customModels', JSON.stringify(customModels));
874
-
875
- // Update UI
876
- renderCustomModels();
877
-
878
- // If the deleted model was selected, switch to default
879
- if (selectedModel === modelId) {
880
- selectModel('gpt-4');
881
  }
882
- }
883
 
884
- // Delete chat
885
- function deleteChat(chatId) {
886
- chats = chats.filter(c => c.id !== chatId);
887
- saveChats();
888
-
889
- if (currentChatId === chatId) {
890
- currentChatId = null;
891
- welcomeMessage.classList.remove('hidden');
892
- chatArea.innerHTML = '';
893
  }
894
-
895
- renderChatList();
896
- hideDeleteModal();
897
- }
898
-
899
- // Show delete modal
900
- function showDeleteModal(chatId) {
901
- chatToDelete = chatId;
902
- deleteChatModal.classList.remove('hidden');
903
- }
904
-
905
- // Hide delete modal
906
- function hideDeleteModal() {
907
- deleteChatModal.classList.add('hidden');
908
- chatToDelete = null;
909
- }
910
-
911
- // Show add model modal
912
- function showAddModelModal() {
913
- addModelModal.classList.remove('hidden');
914
- }
915
-
916
- // Show edit model modal
917
- function showEditModelModal(model) {
918
- document.getElementById('modelName').value = model.name;
919
- document.getElementById('modelEndpoint').value = model.endpoint;
920
- document.getElementById('modelKey').value = model.key || '';
921
- document.getElementById('modelStreaming').checked = model.streaming || false;
922
- document.getElementById('editModelId').value = model.id;
923
-
924
- // Change modal title and submit button text
925
- document.querySelector('#addModelModal h3').textContent = 'Edit Custom Model';
926
- document.querySelector('#addModelForm button[type="submit"]').textContent = 'Save Changes';
927
-
928
- showAddModelModal();
929
- }
930
-
931
- // Hide add model modal
932
- function hideAddModelModal() {
933
- addModelModal.classList.add('hidden');
934
- addModelForm.reset();
935
-
936
- // Reset modal title and submit button text
937
- document.querySelector('#addModelModal h3').textContent = 'Add Custom Model';
938
- document.querySelector('#addModelForm button[type="submit"]').textContent = 'Add Model';
939
- }
940
-
941
- // Save chats to localStorage
942
- function saveChats() {
943
- localStorage.setItem('chats', JSON.stringify(chats));
944
- }
945
-
946
- // Toggle dark mode
947
- function toggleDarkMode() {
948
- const isDark = document.documentElement.classList.toggle('dark');
949
- localStorage.setItem('darkMode', isDark);
950
- }
951
 
952
- // Toggle sidebar on mobile
953
- function toggleSidebar() {
954
- document.querySelector('.w-64').classList.toggle('hidden');
955
- document.querySelector('.w-64').classList.toggle('block');
956
- }
957
-
958
- // Show API key modal
959
- function showApiKeyModal() {
960
- apiKeyModal.classList.remove('hidden');
961
- }
962
-
963
- // Hide API key modal
964
- function hideApiKeyModal() {
965
- apiKeyModal.classList.add('hidden');
966
- apiKeyInput.value = '';
967
- }
968
-
969
- // Save API key
970
- function saveApiKey(key) {
971
- localStorage.setItem('openaiApiKey', key);
972
- hideApiKeyModal();
973
- }
974
 
975
- // Event Listeners
976
- closeApiKeyModal.addEventListener('click', hideApiKeyModal);
977
- cancelApiKey.addEventListener('click', hideApiKeyModal);
978
-
979
- apiKeyForm.addEventListener('submit', (e) => {
980
- e.preventDefault();
981
- const key = apiKeyInput.value.trim();
982
- if (key) {
983
- saveApiKey(key);
984
- // Retry sending the message
985
- setTimeout(() => {
986
- sendMessage();
987
- }, 100);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
988
  }
989
- });
990
- sidebarToggle.addEventListener('click', toggleSidebar);
991
- darkModeToggle.addEventListener('click', (e) => {
992
- e.stopPropagation();
993
- toggleDarkMode();
994
- });
995
- newChatBtn.addEventListener('click', (e) => {
996
- e.preventDefault();
997
- e.stopPropagation();
998
- createNewChat();
999
-
1000
- // Hide welcome message when new chat is created
1001
- welcomeMessage.classList.add('hidden');
1002
 
1003
- // Close sidebar on mobile after creating new chat
1004
- if (window.innerWidth < 768) {
1005
- document.querySelector('.w-64').classList.add('hidden');
 
 
 
 
 
1006
  }
1007
- });
1008
-
1009
- quickStartBtn.addEventListener('click', (e) => {
1010
- e.preventDefault();
1011
- e.stopPropagation();
1012
- createNewChat();
1013
 
1014
- // Hide welcome message when quick start is clicked
1015
- welcomeMessage.classList.add('hidden');
1016
-
1017
- // Close sidebar on mobile after quick start
1018
- if (window.innerWidth < 768) {
1019
- document.querySelector('.w-64').classList.add('hidden');
1020
  }
1021
- });
1022
-
1023
- messageForm.addEventListener('submit', (e) => {
1024
- e.preventDefault();
1025
- sendMessage();
1026
- });
1027
-
1028
- // Auto-resize textarea
1029
- messageInput.addEventListener('input', () => {
1030
- messageInput.style.height = 'auto';
1031
- messageInput.style.height = `${messageInput.scrollHeight}px`;
1032
- });
1033
-
1034
- // Model dropdown
1035
- modelDropdownBtn.addEventListener('click', (e) => {
1036
- e.stopPropagation();
1037
- modelDropdown.classList.toggle('hidden');
1038
- });
1039
 
1040
- // Close dropdown when clicking outside
1041
- document.addEventListener('click', (e) => {
1042
- if (!modelDropdown.contains(e.target) && e.target !== modelDropdownBtn && !e.target.closest('.model-option')) {
1043
- modelDropdown.classList.add('hidden');
1044
  }
1045
- });
1046
 
1047
- // Handle model selection - delegated event listener
1048
- document.addEventListener('click', (e) => {
1049
- const modelOption = e.target.closest('.model-option');
1050
- if (modelOption && !e.target.classList.contains('delete-model')) {
1051
- const modelId = modelOption.dataset.model;
1052
- selectModel(modelId);
1053
- }
1054
- });
1055
-
1056
- // Add model
1057
- addModelBtn.addEventListener('click', showAddModelModal);
1058
- closeModelModal.addEventListener('click', hideAddModelModal);
1059
- cancelAddModel.addEventListener('click', hideAddModelModal);
1060
-
1061
- addModelForm.addEventListener('submit', (e) => {
1062
- e.preventDefault();
1063
- const name = document.getElementById('modelName').value;
1064
- const endpoint = document.getElementById('modelEndpoint').value;
1065
- const key = document.getElementById('modelKey').value;
1066
- const streaming = document.getElementById('modelStreaming').checked;
1067
- const editModelId = document.getElementById('editModelId').value;
1068
-
1069
- if (editModelId) {
1070
- editCustomModel(editModelId, name, endpoint, key, streaming);
1071
- } else {
1072
- addCustomModel(name, endpoint, key);
1073
- }
1074
- });
1075
-
1076
- // Delete chat
1077
- closeDeleteModal.addEventListener('click', hideDeleteModal);
1078
- cancelDelete.addEventListener('click', hideDeleteModal);
1079
- confirmDelete.addEventListener('click', () => {
1080
- if (chatToDelete) {
1081
- deleteChat(chatToDelete);
1082
  }
1083
- });
1084
-
1085
- // Settings button
1086
- document.getElementById('settingsBtn').addEventListener('click', (e) => {
1087
- e.stopPropagation();
1088
- alert('Settings panel will be implemented here');
1089
- // You can replace this with actual settings modal later
1090
- });
1091
-
1092
- // Handle message actions
1093
- function handleMessageAction(action, messageIndex) {
1094
- if (!currentChatId) return;
1095
 
1096
- const chat = chats.find(c => c.id === currentChatId);
1097
- if (!chat || !chat.messages[messageIndex]) return;
1098
-
1099
- if (action === 'regenerate') {
1100
- // Only regenerate if this is the last assistant message
1101
- if (messageIndex === chat.messages.length - 1 &&
1102
- chat.messages[messageIndex].role === 'assistant') {
1103
- chat.messages.splice(messageIndex, 1);
1104
- renderMessages(chat.messages);
1105
-
1106
- // Resend last user message
1107
- const lastUserMessage = chat.messages[chat.messages.length - 1];
1108
- if (lastUserMessage && lastUserMessage.role === 'user') {
1109
- sendMessage(lastUserMessage.content);
1110
- }
 
 
 
 
 
 
 
 
 
 
 
 
1111
  }
1112
- } else if (action === 'edit') {
1113
- // TODO: Implement edit functionality
1114
- alert('Edit functionality will be implemented here');
1115
- }
1116
- }
1117
-
1118
- // Initialize the app
1119
- init();
1120
-
1121
- // Add delegated event listener for message actions
1122
- document.addEventListener('click', (e) => {
1123
- const actionBtn = e.target.closest('.message-action');
1124
- if (actionBtn) {
1125
- e.preventDefault();
1126
- e.stopPropagation();
1127
-
1128
- const messageElement = actionBtn.closest('.flex.gap-4');
1129
- const messageIndex = Array.from(messageElement.parentNode.children).indexOf(messageElement);
1130
- const action = actionBtn.dataset.action;
1131
-
1132
- handleMessageAction(action, messageIndex);
1133
  }
1134
  });
1135
  </script>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Modern Scientific Calculator</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
8
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  <style>
10
+ .sidebar {
11
+ transform: translateX(-100%);
12
+ transition: transform 0.3s ease;
13
  }
14
+ .sidebar.open {
15
+ transform: translateX(0);
16
  }
17
+ .calculator-btn {
18
+ transition: all 0.2s ease;
19
+ transform: scale(1);
20
  }
21
+ .calculator-btn:active {
22
+ transform: scale(0.95);
23
  }
24
+ .history-item:hover {
25
+ background-color: rgba(255, 255, 255, 0.1);
26
+ cursor: pointer;
 
 
 
 
 
 
 
 
 
 
27
  }
28
+ .scrollbar-hide::-webkit-scrollbar {
29
+ display: none;
 
 
 
30
  }
31
+ .scrollbar-hide {
32
+ -ms-overflow-style: none;
33
+ scrollbar-width: none;
 
34
  }
35
  </style>
36
  </head>
37
+ <body class="bg-gray-900 text-white min-h-screen flex items-center justify-center p-4">
38
+ <div class="w-full max-w-md">
39
+ <div class="bg-gray-800 rounded-2xl shadow-xl overflow-hidden">
40
+ <!-- Display Area -->
41
+ <div class="p-4">
42
+ <div class="flex justify-between items-center mb-2">
43
+ <h1 class="text-xl font-bold text-blue-400">Scientific Calculator</h1>
44
+ <div class="flex space-x-2">
45
+ <button id="sidebar-toggle" class="text-gray-300 hover:text-white">
46
+ <i class="fas fa-sliders-h"></i>
47
+ </button>
48
+ <button id="theme-toggle" class="text-gray-300 hover:text-white">
49
+ <i class="fas fa-moon"></i>
50
+ </button>
 
 
 
 
 
51
  </div>
 
52
  </div>
53
 
54
+ <div class="bg-gray-700 rounded-lg p-3 mb-2">
55
+ <div id="history-display" class="text-gray-400 text-sm h-6 overflow-x-auto whitespace-nowrap scrollbar-hide"></div>
56
+ <div id="display" class="text-right text-3xl font-semibold break-all">0</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  </div>
58
 
59
+ <div class="flex space-x-2 mb-2">
60
+ <button id="clear" class="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-lg flex-1 calculator-btn">AC</button>
61
+ <button id="backspace" class="bg-gray-600 hover:bg-gray-500 text-white px-4 py-2 rounded-lg calculator-btn">
62
+ <i class="fas fa-backspace"></i>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  </button>
64
+ <button id="percentage" class="bg-gray-600 hover:bg-gray-500 text-white px-4 py-2 rounded-lg calculator-btn">%</button>
65
+ <button id="divide" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-lg calculator-btn">÷</button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  </div>
67
  </div>
68
 
69
+ <!-- Main Calculator -->
70
+ <div class="grid grid-cols-4 gap-2 p-4">
71
+ <!-- Row 1 -->
72
+ <button data-value="7" class="bg-gray-700 hover:bg-gray-600 text-white text-xl p-3 rounded-lg calculator-btn">7</button>
73
+ <button data-value="8" class="bg-gray-700 hover:bg-gray-600 text-white text-xl p-3 rounded-lg calculator-btn">8</button>
74
+ <button data-value="9" class="bg-gray-700 hover:bg-gray-600 text-white text-xl p-3 rounded-lg calculator-btn">9</button>
75
+ <button id="multiply" class="bg-blue-500 hover:bg-blue-600 text-white text-xl p-3 rounded-lg calculator-btn">×</button>
76
+
77
+ <!-- Row 2 -->
78
+ <button data-value="4" class="bg-gray-700 hover:bg-gray-600 text-white text-xl p-3 rounded-lg calculator-btn">4</button>
79
+ <button data-value="5" class="bg-gray-700 hover:bg-gray-600 text-white text-xl p-3 rounded-lg calculator-btn">5</button>
80
+ <button data-value="6" class="bg-gray-700 hover:bg-gray-600 text-white text-xl p-3 rounded-lg calculator-btn">6</button>
81
+ <button id="subtract" class="bg-blue-500 hover:bg-blue-600 text-white text-xl p-3 rounded-lg calculator-btn">-</button>
82
+
83
+ <!-- Row 3 -->
84
+ <button data-value="1" class="bg-gray-700 hover:bg-gray-600 text-white text-xl p-3 rounded-lg calculator-btn">1</button>
85
+ <button data-value="2" class="bg-gray-700 hover:bg-gray-600 text-white text-xl p-3 rounded-lg calculator-btn">2</button>
86
+ <button data-value="3" class="bg-gray-700 hover:bg-gray-600 text-white text-xl p-3 rounded-lg calculator-btn">3</button>
87
+ <button id="add" class="bg-blue-500 hover:bg-blue-600 text-white text-xl p-3 rounded-lg calculator-btn">+</button>
88
+
89
+ <!-- Row 4 -->
90
+ <button id="decimal" class="bg-gray-700 hover:bg-gray-600 text-white text-xl p-3 rounded-lg calculator-btn">.</button>
91
+ <button data-value="0" class="bg-gray-700 hover:bg-gray-600 text-white text-xl p-3 rounded-lg calculator-btn">0</button>
92
+ <button id="equals" class="bg-green-500 hover:bg-green-600 text-white text-xl p-3 rounded-lg calculator-btn">=</button>
93
+ <button id="sqrt" class="bg-purple-500 hover:bg-purple-600 text-white text-xl p-3 rounded-lg calculator-btn">√</button>
94
+
95
+ <!-- Scientific Functions Row -->
96
+ <button id="power" class="bg-purple-500 hover:bg-purple-600 text-white text-xl p-3 rounded-lg calculator-btn">x^y</button>
97
+ <button id="sin" class="bg-purple-500 hover:bg-purple-600 text-white text-xl p-3 rounded-lg calculator-btn">sin</button>
98
+ <button id="cos" class="bg-purple-500 hover:bg-purple-600 text-white text-xl p-3 rounded-lg calculator-btn">cos</button>
99
+ <button id="tan" class="bg-purple-500 hover:bg-purple-600 text-white text-xl p-3 rounded-lg calculator-btn">tan</button>
100
+
101
+ <!-- More Functions Row -->
102
+ <button id="log" class="bg-purple-500 hover:bg-purple-600 text-white text-xl p-3 rounded-lg calculator-btn">log</button>
103
+ <button id="ln" class="bg-purple-500 hover:bg-purple-600 text-white text-xl p-3 rounded-lg calculator-btn">ln</button>
104
+ <button id="pi" class="bg-purple-500 hover:bg-purple-600 text-white text-xl p-3 rounded-lg calculator-btn">π</button>
105
+ <button id="factorial" class="bg-purple-500 hover:bg-purple-600 text-white text-xl p-3 rounded-lg calculator-btn">n!</button>
106
  </div>
107
 
108
+ <!-- Scientific Sidebar -->
109
+ <div id="sidebar" class="sidebar absolute top-0 left-0 w-64 h-full bg-gray-800 z-10 p-4 overflow-y-auto">
110
+ <div class="flex justify-between items-center mb-4">
111
+ <h2 class="text-lg font-semibold text-blue-400">Scientific Functions</h2>
112
+ <button id="close-sidebar" class="text-gray-300 hover:text-white">
113
+ <i class="fas fa-times"></i>
114
+ </button>
115
  </div>
116
 
117
+ <div class="grid grid-cols-2 gap-2 mb-4">
118
+ <button id="cube-root" class="bg-purple-500 hover:bg-purple-600 text-white p-2 rounded calculator-btn">∛</button>
119
+ <button id="nth-root" class="bg-purple-500 hover:bg-purple-600 text-white p-2 rounded calculator-btn">ⁿ√</button>
120
+ <button id="gcd" class="bg-purple-500 hover:bg-purple-600 text-white p-2 rounded calculator-btn">GCD</button>
121
+ <button id="lcm" class="bg-purple-500 hover:bg-purple-600 text-white p-2 rounded calculator-btn">LCM</button>
122
+ <button id="abs" class="bg-purple-500 hover:bg-purple-600 text-white p-2 rounded calculator-btn">|x|</button>
123
+ <button id="exp" class="bg-purple-500 hover:bg-purple-600 text-white p-2 rounded calculator-btn">e^x</button>
124
+ <button id="mod" class="bg-purple-500 hover:bg-purple-600 text-white p-2 rounded calculator-btn">mod</button>
125
+ <button id="rand" class="bg-purple-500 hover:bg-purple-600 text-white p-2 rounded calculator-btn">Rand</button>
126
  </div>
127
 
128
  <div class="mb-4">
129
+ <h3 class="text-md font-semibold text-blue-400 mb-2">Parentheses</h3>
130
+ <div class="grid grid-cols-2 gap-2">
131
+ <button id="open-paren" class="bg-blue-500 hover:bg-blue-600 text-white p-2 rounded calculator-btn">(</button>
132
+ <button id="close-paren" class="bg-blue-500 hover:bg-blue-600 text-white p-2 rounded calculator-btn">)</button>
133
+ </div>
 
 
134
  </div>
 
135
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
  <div class="mb-4">
137
+ <h3 class="text-md font-semibold text-blue-400 mb-2">Constants</h3>
138
+ <div class="grid grid-cols-2 gap-2">
139
+ <button id="e" class="bg-purple-500 hover:bg-purple-600 text-white p-2 rounded calculator-btn">e</button>
140
+ <button id="phi" class="bg-purple-500 hover:bg-purple-600 text-white p-2 rounded calculator-btn">φ</button>
141
+ </div>
 
 
 
 
 
142
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
143
  </div>
144
+
145
+ <!-- History Panel -->
146
+ <div class="bg-gray-700 p-4 border-t border-gray-600">
147
+ <div class="flex justify-between items-center mb-2">
148
+ <h2 class="text-lg font-semibold text-blue-400">History</h2>
149
+ <button id="clear-history" class="text-gray-300 hover:text-white text-sm">Clear</button>
150
+ </div>
151
+ <div id="history-list" class="max-h-40 overflow-y-auto scrollbar-hide space-y-1"></div>
 
 
152
  </div>
153
  </div>
154
  </div>
155
 
156
  <script>
157
+ document.addEventListener('DOMContentLoaded', function() {
158
+ // Calculator state
159
+ let currentInput = '0';
160
+ let previousInput = '';
161
+ let operation = null;
162
+ let resetInput = false;
163
+ let calculationHistory = [];
164
+
165
+ // DOM elements
166
+ const display = document.getElementById('display');
167
+ const historyDisplay = document.getElementById('history-display');
168
+ const historyList = document.getElementById('history-list');
169
+ const themeToggle = document.getElementById('theme-toggle');
170
+ const clearHistoryBtn = document.getElementById('clear-history');
171
+
172
+ // Number buttons
173
+ const numberButtons = document.querySelectorAll('[data-value]');
174
+
175
+ // Operation buttons
176
+ const addButton = document.getElementById('add');
177
+ const subtractButton = document.getElementById('subtract');
178
+ const multiplyButton = document.getElementById('multiply');
179
+ const divideButton = document.getElementById('divide');
180
+ const equalsButton = document.getElementById('equals');
181
+ const clearButton = document.getElementById('clear');
182
+ const backspaceButton = document.getElementById('backspace');
183
+ const decimalButton = document.getElementById('decimal');
184
+ const percentageButton = document.getElementById('percentage');
185
+
186
+ // Scientific functions
187
+ const sqrtButton = document.getElementById('sqrt');
188
+ const powerButton = document.getElementById('power');
189
+ const sinButton = document.getElementById('sin');
190
+ const cosButton = document.getElementById('cos');
191
+ const tanButton = document.getElementById('tan');
192
+ const logButton = document.getElementById('log');
193
+ const lnButton = document.getElementById('ln');
194
+ const piButton = document.getElementById('pi');
195
+ const factorialButton = document.getElementById('factorial');
196
+
197
+ // Initialize
198
+ updateDisplay();
199
+
200
+ // Theme toggle
201
+ themeToggle.addEventListener('click', toggleTheme);
202
+
203
+ // Clear history
204
+ clearHistoryBtn.addEventListener('click', clearHistory);
205
+
206
+ // Number buttons
207
+ numberButtons.forEach(button => {
208
+ button.addEventListener('click', () => {
209
+ appendNumber(button.getAttribute('data-value'));
210
+ });
211
  });
212
 
213
+ // Operation buttons
214
+ addButton.addEventListener('click', () => setOperation('+'));
215
+ subtractButton.addEventListener('click', () => setOperation('-'));
216
+ multiplyButton.addEventListener('click', () => setOperation('×'));
217
+ divideButton.addEventListener('click', () => setOperation('÷'));
218
+ equalsButton.addEventListener('click', compute);
219
+ clearButton.addEventListener('click', clear);
220
+ backspaceButton.addEventListener('click', backspace);
221
+ decimalButton.addEventListener('click', appendDecimal);
222
+ percentageButton.addEventListener('click', percentage);
223
+
224
+ // Sidebar toggle
225
+ const sidebarToggle = document.getElementById('sidebar-toggle');
226
+ const closeSidebar = document.getElementById('close-sidebar');
227
+ const sidebar = document.getElementById('sidebar');
228
+
229
+ sidebarToggle.addEventListener('click', () => {
230
+ sidebar.classList.add('open');
231
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
232
 
233
+ closeSidebar.addEventListener('click', () => {
234
+ sidebar.classList.remove('open');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
235
  });
 
236
 
237
+ // Scientific functions
238
+ sqrtButton.addEventListener('click', () => appendFunction('√('));
239
+ powerButton.addEventListener('click', () => setOperation('^'));
240
+ sinButton.addEventListener('click', () => applyFunction('sin'));
241
+ cosButton.addEventListener('click', () => applyFunction('cos'));
242
+ tanButton.addEventListener('click', () => applyFunction('tan'));
243
+ logButton.addEventListener('click', () => applyFunction('log'));
244
+ lnButton.addEventListener('click', () => applyFunction('ln'));
245
+ piButton.addEventListener('click', appendPi);
246
+ factorialButton.addEventListener('click', factorial);
247
+
248
+ // Keyboard support
249
+ document.addEventListener('keydown', handleKeyboardInput);
250
+
251
+ // Functions
252
+ function toggleTheme() {
253
+ document.body.classList.toggle('bg-gray-100');
254
+ document.body.classList.toggle('text-gray-900');
255
+ document.body.classList.toggle('bg-gray-900');
256
+ document.body.classList.toggle('text-white');
257
 
258
+ const calculator = document.querySelector('.bg-gray-800');
259
+ calculator.classList.toggle('bg-gray-800');
260
+ calculator.classList.toggle('bg-white');
 
 
 
 
 
 
 
 
261
 
262
+ const displayBg = document.querySelector('.bg-gray-700');
263
+ displayBg.classList.toggle('bg-gray-700');
264
+ displayBg.classList.toggle('bg-gray-100');
 
 
265
 
266
+ const buttons = document.querySelectorAll('.bg-gray-600, .bg-gray-700');
267
+ buttons.forEach(btn => {
268
+ btn.classList.toggle('bg-gray-600');
269
+ btn.classList.toggle('bg-gray-700');
270
+ btn.classList.toggle('bg-gray-200');
271
+ btn.classList.toggle('bg-gray-300');
272
+ btn.classList.toggle('text-white');
273
+ btn.classList.toggle('text-gray-900');
 
 
274
  });
275
 
276
+ const icon = themeToggle.querySelector('i');
277
+ if (icon.classList.contains('fa-moon')) {
278
+ icon.classList.remove('fa-moon');
279
+ icon.classList.add('fa-sun');
280
+ } else {
281
+ icon.classList.remove('fa-sun');
282
+ icon.classList.add('fa-moon');
283
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
284
  }
285
 
286
+ function clearHistory() {
287
+ calculationHistory = [];
288
+ updateHistoryList();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
289
  }
290
 
291
+ function appendNumber(number) {
292
+ if (currentInput === '0' || resetInput) {
293
+ currentInput = number;
294
+ resetInput = false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
295
  } else {
296
+ currentInput += number;
 
 
 
 
 
 
 
 
 
297
  }
298
+ updateDisplay();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
299
  }
300
 
301
+ function appendDecimal() {
302
+ if (resetInput) {
303
+ currentInput = '0.';
304
+ resetInput = false;
305
+ return;
306
+ }
307
+
308
+ if (currentInput.indexOf('.') === -1) {
309
+ currentInput += '.';
310
+ }
311
+ updateDisplay();
312
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
313
 
314
+ function appendPi() {
315
+ if (currentInput !== '0' && !resetInput) {
316
+ currentInput += Math.PI.toString();
317
+ } else {
318
+ currentInput = Math.PI.toString();
319
+ resetInput = false;
320
+ }
321
+ updateDisplay();
322
+ }
323
 
324
+ function setOperation(op) {
325
+ if (currentInput === '0' && previousInput && !resetInput) {
326
+ operation = op;
327
+ updateDisplay();
328
+ return;
329
+ }
330
 
331
+ if (operation && !resetInput) {
332
+ compute();
333
+ }
334
 
335
+ previousInput = currentInput;
336
+ operation = op;
337
+ resetInput = true;
338
+ updateDisplay();
339
+ }
340
+
341
+ function compute() {
342
+ try {
343
+ // Replace display symbols with JS-compatible ones
344
+ let expression = currentInput
345
+ .replace(/×/g, '*')
346
+ .replace(/÷/g, '/')
347
+ .replace(/√\(/g, 'Math.sqrt(')
348
+ .replace(/∛\(/g, 'Math.cbrt(')
349
+ .replace(/\^/g, '**')
350
+ .replace(/mod/g, '%')
351
+ .replace(/abs\(/g, 'Math.abs(')
352
+ .replace(/exp\(/g, 'Math.exp(')
353
+ .replace(/log\(/g, 'Math.log10(')
354
+ .replace(/ln\(/g, 'Math.log(')
355
+ .replace(/sin\(/g, 'Math.sin(')
356
+ .replace(/cos\(/g, 'Math.cos(')
357
+ .replace(/tan\(/g, 'Math.tan(')
358
+ .replace(/π/g, 'Math.PI')
359
+ .replace(/φ/g, '((1 + Math.sqrt(5)) / 2)')
360
+ .replace(/e/g, 'Math.E');
361
+
362
+ // Handle factorial (must be done after other replacements)
363
+ expression = expression.replace(/(\d+)!/g, function(match, num) {
364
+ let n = parseInt(num);
365
+ if (n < 0) return 'NaN';
366
+ let result = 1;
367
+ for (let i = 2; i <= n; i++) result *= i;
368
+ return result;
369
+ });
370
+
371
+ // Handle gcd and lcm (simple implementations)
372
+ expression = expression.replace(/gcd\(([^,]+),([^)]+)\)/g, function(match, a, b) {
373
+ a = parseFloat(a);
374
+ b = parseFloat(b);
375
+ while (b) [a, b] = [b, a % b];
376
+ return a;
377
+ });
378
+
379
+ expression = expression.replace(/lcm\(([^,]+),([^)]+)\)/g, function(match, a, b) {
380
+ a = parseFloat(a);
381
+ b = parseFloat(b);
382
+ return (a * b) / gcd(a, b);
383
+ });
384
+
385
+ // Evaluate the expression
386
+ const computation = eval(expression);
387
 
388
+ switch (operation) {
389
+ case '+':
390
+ computation = prev + current;
391
+ break;
392
+ case '-':
393
+ computation = prev - current;
394
+ break;
395
+ case '×':
396
+ computation = prev * current;
397
+ break;
398
+ case '÷':
399
+ computation = prev / current;
400
+ break;
401
+ case '^':
402
+ computation = Math.pow(prev, current);
403
+ break;
404
+ default:
405
+ return;
406
  }
407
 
408
+ // Add to history
409
+ const historyEntry = {
410
+ expression: `${previousInput} ${operation} ${currentInput}`,
411
+ result: computation.toString()
 
 
 
412
  };
413
+ calculationHistory.unshift(historyEntry);
414
+ if (calculationHistory.length > 10) {
415
+ calculationHistory.pop();
416
+ }
417
 
418
+ currentInput = computation.toString();
419
+ operation = null;
420
+ resetInput = true;
 
 
 
 
 
 
421
 
422
+ updateDisplay();
423
+ updateHistoryList();
 
 
 
 
 
 
 
 
 
 
 
 
424
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
425
 
426
+ function clear() {
427
+ currentInput = '0';
428
+ previousInput = '';
429
+ operation = null;
430
+ updateDisplay();
431
  }
432
 
433
+ function backspace() {
434
+ if (currentInput.length === 1) {
435
+ currentInput = '0';
436
+ } else {
437
+ currentInput = currentInput.slice(0, -1);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
438
  }
439
+ updateDisplay();
440
+ }
441
+
442
+ function percentage() {
443
+ currentInput = (parseFloat(currentInput) / 100).toString();
444
+ updateDisplay();
445
+ }
446
+
447
+ function squareRoot() {
448
+ currentInput = Math.sqrt(parseFloat(currentInput)).toString();
449
+ updateDisplay();
450
+
451
+ // Add to history
452
+ const historyEntry = {
453
+ expression: `√(${currentInput})`,
454
+ result: currentInput
455
+ };
456
+ calculationHistory.unshift(historyEntry);
457
+ if (calculationHistory.length > 10) {
458
+ calculationHistory.pop();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
459
  }
460
+ updateHistoryList();
461
+ }
462
+
463
+ function applyFunction(func) {
464
+ const value = parseFloat(currentInput);
465
+ let result;
 
 
 
 
 
 
 
 
 
 
 
466
 
467
+ switch (func) {
468
+ case 'sin':
469
+ result = Math.sin(value);
470
+ break;
471
+ case 'cos':
472
+ result = Math.cos(value);
473
+ break;
474
+ case 'tan':
475
+ result = Math.tan(value);
476
+ break;
477
+ case 'log':
478
+ result = Math.log10(value);
479
+ break;
480
+ case 'ln':
481
+ result = Math.log(value);
482
+ break;
483
  }
484
 
485
+ currentInput = result.toString();
486
+
487
+ // Add to history
488
+ const historyEntry = {
489
+ expression: `${func}(${value})`,
490
+ result: currentInput
491
+ };
492
+ calculationHistory.unshift(historyEntry);
493
+ if (calculationHistory.length > 10) {
494
+ calculationHistory.pop();
495
  }
496
+
497
+ updateDisplay();
498
+ updateHistoryList();
499
  }
 
 
 
 
 
 
 
 
 
 
500
 
501
+ // Sidebar buttons
502
+ document.getElementById('cube-root').addEventListener('click', () => appendFunction('∛('));
503
+ document.getElementById('nth-root').addEventListener('click', () => appendFunction('^(1/'));
504
+ document.getElementById('gcd').addEventListener('click', () => appendFunction('gcd('));
505
+ document.getElementById('lcm').addEventListener('click', () => appendFunction('lcm('));
506
+ document.getElementById('abs').addEventListener('click', () => appendFunction('abs('));
507
+ document.getElementById('exp').addEventListener('click', () => appendFunction('exp('));
508
+ document.getElementById('mod').addEventListener('click', () => appendFunction(' mod '));
509
+ document.getElementById('rand').addEventListener('click', randomNumber);
510
+ document.getElementById('open-paren').addEventListener('click', () => appendValue('('));
511
+ document.getElementById('close-paren').addEventListener('click', () => appendValue(')'));
512
+ document.getElementById('e').addEventListener('click', appendE);
513
+ document.getElementById('phi').addEventListener('click', appendPhi);
514
+
515
+ // New functions
516
+ function appendFunction(func) {
517
+ if (resetInput) {
518
+ currentInput = func;
519
+ resetInput = false;
520
+ } else {
521
+ currentInput += func;
522
  }
523
+ updateDisplay();
524
  }
 
525
 
526
+ function appendValue(value) {
527
+ if (currentInput === '0' || resetInput) {
528
+ currentInput = value;
529
+ resetInput = false;
530
+ } else {
531
+ currentInput += value;
532
+ }
533
+ updateDisplay();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
534
  }
 
 
 
 
 
 
 
 
535
 
536
+ function appendE() {
537
+ if (currentInput !== '0' && !resetInput) {
538
+ currentInput += Math.E.toString();
539
+ } else {
540
+ currentInput = Math.E.toString();
541
+ resetInput = false;
542
+ }
543
+ updateDisplay();
 
 
 
 
 
 
544
  }
 
545
 
546
+ function appendPhi() {
547
+ const phi = (1 + Math.sqrt(5)) / 2;
548
+ if (currentInput !== '0' && !resetInput) {
549
+ currentInput += phi.toString();
550
+ } else {
551
+ currentInput = phi.toString();
552
+ resetInput = false;
553
+ }
554
+ updateDisplay();
555
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
556
 
557
+ function randomNumber() {
558
+ const random = Math.random();
559
+ if (currentInput !== '0' && !resetInput) {
560
+ currentInput += random.toString();
561
+ } else {
562
+ currentInput = random.toString();
563
+ resetInput = false;
564
+ }
565
+ updateDisplay();
566
+ }
 
 
 
 
 
 
 
 
 
 
 
 
567
 
568
+ function factorial() {
569
+ let num = parseInt(currentInput);
570
+ if (num < 0) {
571
+ currentInput = 'Error';
572
+ updateDisplay();
573
+ return;
574
+ }
575
+
576
+ let result = 1;
577
+ for (let i = 2; i <= num; i++) {
578
+ result *= i;
579
+ }
580
+
581
+ currentInput = result.toString();
582
+
583
+ // Add to history
584
+ const historyEntry = {
585
+ expression: `${num}!`,
586
+ result: currentInput
587
+ };
588
+ calculationHistory.unshift(historyEntry);
589
+ if (calculationHistory.length > 10) {
590
+ calculationHistory.pop();
591
+ }
592
+
593
+ updateDisplay();
594
+ updateHistoryList();
595
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
596
 
597
+ function updateDisplay() {
598
+ display.textContent = currentInput;
599
+
600
+ if (operation !== null) {
601
+ historyDisplay.textContent = `${previousInput} ${operation}`;
602
+ } else {
603
+ historyDisplay.textContent = '';
604
+ }
605
  }
 
 
 
 
 
 
606
 
607
+ // Helper functions
608
+ function gcd(a, b) {
609
+ a = Math.abs(a);
610
+ b = Math.abs(b);
611
+ while (b) [a, b] = [b, a % b];
612
+ return a;
613
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
614
 
615
+ function lcm(a, b) {
616
+ return (a * b) / gcd(a, b);
 
 
617
  }
 
618
 
619
+ function updateHistoryList() {
620
+ historyList.innerHTML = '';
621
+
622
+ calculationHistory.forEach((entry, index) => {
623
+ const historyItem = document.createElement('div');
624
+ historyItem.className = 'history-item bg-gray-600 rounded p-2 text-sm';
625
+ historyItem.innerHTML = `
626
+ <div class="text-gray-300">${entry.expression} =</div>
627
+ <div class="text-right font-semibold">${entry.result}</div>
628
+ `;
629
+
630
+ historyItem.addEventListener('click', () => {
631
+ currentInput = entry.result;
632
+ resetInput = true;
633
+ updateDisplay();
634
+ });
635
+
636
+ historyList.appendChild(historyItem);
637
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
638
  }
 
 
 
 
 
 
 
 
 
 
 
 
639
 
640
+ function handleKeyboardInput(e) {
641
+ if (e.key >= '0' && e.key <= '9') {
642
+ appendNumber(e.key);
643
+ } else if (e.key === '.') {
644
+ appendDecimal();
645
+ } else if (e.key === '+') {
646
+ setOperation('+');
647
+ } else if (e.key === '-') {
648
+ setOperation('-');
649
+ } else if (e.key === '*') {
650
+ setOperation('×');
651
+ } else if (e.key === '/') {
652
+ setOperation('÷');
653
+ } else if (e.key === '^') {
654
+ setOperation('^');
655
+ } else if (e.key === '(') {
656
+ appendValue('(');
657
+ } else if (e.key === ')') {
658
+ appendValue(')');
659
+ } else if (e.key === 'Enter' || e.key === '=') {
660
+ compute();
661
+ } else if (e.key === 'Escape') {
662
+ clear();
663
+ } else if (e.key === 'Backspace') {
664
+ backspace();
665
+ } else if (e.key === '%') {
666
+ percentage();
667
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
668
  }
669
  });
670
  </script>