roshnn24 commited on
Commit
1e4f197
·
verified ·
1 Parent(s): 22c23cc

Create templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +987 -0
templates/index.html ADDED
@@ -0,0 +1,987 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>AI Chat Interface</title>
7
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/4.0.2/marked.min.js"></script>
8
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.1/highlight.min.js"></script>
9
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.1/styles/default.min.css" rel="stylesheet">
10
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
11
+ <style>
12
+ .chat-area {
13
+ height: calc(100vh - 200px);
14
+ overflow-y: auto;
15
+ background-color: #1a1a1a;
16
+ color: #e6e6e6;
17
+ padding: 20px;
18
+ gap: 15px; /* Space between messages */
19
+ display: flex;
20
+ flex-direction: column;
21
+ }
22
+ .message-bubble {
23
+ max-width: 80%;
24
+ margin: 10px;
25
+ padding: 15px;
26
+ border-radius: 10px;
27
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
28
+ transition: all 0.2s ease;
29
+ }
30
+ .assistant-message {
31
+ background-color: #797D7F; /* Tailwind's gray-800 - a nice light shade for dark theme */
32
+ margin-right: auto;
33
+ max-width: 80%;
34
+ margin: 10px;
35
+ padding: 15px;
36
+ border-radius: 10px;
37
+ border: 1px solid #4a5568; /* Subtle border for definition */
38
+ color: #e5e7eb; /* Light text color for contrast */
39
+ box-shadow: 0 1px 2px rgba(0,0,0,0.1);
40
+ }
41
+
42
+ /* For user messages (to maintain contrast) */
43
+ .user-message {
44
+ background-color: #283747; /* Slightly darker than assistant messages */
45
+ margin-left: auto;
46
+ max-width: 80%;
47
+ margin: 10px;
48
+ padding: 15px;
49
+ border-radius: 10px;
50
+ border: 1px solid #4a5568;
51
+ color: #e5e7eb;
52
+ box-shadow: 0 1px 2px rgba(0,0,0,0.1);
53
+ }
54
+ .chat-list {
55
+ max-height: calc(100vh - 100px);
56
+ overflow-y: auto;
57
+ background-color: #1a1a1a;
58
+ border-right: 1px solid #4a5568;
59
+ color: #e6e6e6;
60
+ }
61
+ .chat-item {
62
+ padding: 10px 15px;
63
+ border-bottom: 1px solid #2d3748;
64
+ color: #e6e6e6;
65
+ }
66
+ .chat-item:hover {
67
+ background-color: #2d3748;
68
+ transition: background-color 0.2s ease;
69
+ }
70
+ .active-chat {
71
+ background-color: #3b82f6;
72
+ color: #ffffff;
73
+ }
74
+ .copy-button {
75
+ position: absolute;
76
+ right: 10px;
77
+ top: 5px;
78
+ padding: 4px 8px;
79
+ background-color: #3b82f6;
80
+ color: #ffffff;
81
+ border-radius: 4px;
82
+ font-size: 12px;
83
+ opacity: 0.9;
84
+ cursor: pointer;
85
+ transition: all 0.2s ease;
86
+ border: 1px solid #60a5fa;
87
+ }
88
+ .copy-button:hover {
89
+ opacity: 1;
90
+ background-color: #60a5fa;
91
+ }
92
+ .code-block-wrapper {
93
+ position: relative;
94
+ margin: 1em 0;
95
+ background-color: #1e293b;
96
+ border: 1px solid #4a5568;
97
+ border-radius: 6px;
98
+ }
99
+ .copy-tooltip {
100
+ position: absolute;
101
+ background-color: #2d3748;
102
+ color: #ffffff;
103
+ padding: 4px 8px;
104
+ border-radius: 4px;
105
+ font-size: 12px;
106
+ right: 10px;
107
+ top: -25px;
108
+ opacity: 0;
109
+ transition: opacity 0.2s;
110
+ border: 1px solid #4a5568;
111
+ }
112
+ .copy-tooltip.show {
113
+ opacity: 1;
114
+ }
115
+ .message-footer {
116
+ display: flex;
117
+ justify-content: flex-end;
118
+ margin-top: 10px;
119
+ padding-top: 8px;
120
+ border-top: 1px solid #4a5568;
121
+ }
122
+ .retry-button {
123
+ display: inline-flex;
124
+ align-items: center;
125
+ padding: 6px 12px;
126
+ background-color: #2d3748;
127
+ color: #e6e6e6;
128
+ border: 1px solid #4a5568;
129
+ border-radius: 4px;
130
+ font-size: 12px;
131
+ cursor: pointer;
132
+ transition: all 0.2s ease;
133
+ }
134
+ .retry-button:hover {
135
+ background-color: #4a5568;
136
+ color: #ffffff;
137
+ }
138
+ .retry-button svg {
139
+ width: 14px;
140
+ height: 14px;
141
+ margin-right: 4px;
142
+ }
143
+ .retry-button.loading {
144
+ opacity: 0.7;
145
+ cursor: not-allowed;
146
+ }
147
+ .code-block-wrapper {
148
+ position: relative;
149
+ margin: 1em 0;
150
+ padding-top: 40px;
151
+ background-color: #1e293b;
152
+ border: 1px solid #4a5568;
153
+ border-radius: 6px;
154
+ }
155
+ .test-button {
156
+ position: absolute;
157
+ top: 5px;
158
+ right: 120px;
159
+ background-color: #3b82f6;
160
+ color: #ffffff;
161
+ padding: 6px 12px;
162
+ border-radius: 4px;
163
+ font-size: 12px;
164
+ border: 1px solid #60a5fa;
165
+ cursor: pointer;
166
+ transition: all 0.2s ease;
167
+ }
168
+ .test-button:hover {
169
+ background-color: #60a5fa;
170
+ }
171
+ .test-results {
172
+ margin-top: 8px;
173
+ padding: 12px;
174
+ background-color: #1e293b;
175
+ border-left: 4px solid #4a5568;
176
+ font-family: monospace;
177
+ white-space: pre-wrap;
178
+ display: none;
179
+ color: #e6e6e6;
180
+ }
181
+ .test-results.show {
182
+ display: block;
183
+ }
184
+ .test-results.error {
185
+ border-left-color: #ef4444;
186
+ background-color: #2d1f1f;
187
+ }
188
+ .test-results.success {
189
+ border-left-color: #10b981;
190
+ background-color: #1a2e1f;
191
+ }
192
+ .loading-spinner {
193
+ display: inline-block;
194
+ width: 12px;
195
+ height: 12px;
196
+ border: 2px solid #ffffff;
197
+ border-radius: 50%;
198
+ border-top-color: transparent;
199
+ animation: spin 1s linear infinite;
200
+ }
201
+ @keyframes spin {
202
+ to {
203
+ transform: rotate(360deg);
204
+ }
205
+ }
206
+ .upload-button {
207
+ position: relative;
208
+ overflow: hidden;
209
+ display: inline-block;
210
+ background-color: #3b82f6;
211
+ color: #ffffff;
212
+ border: 1px solid #60a5fa;
213
+ border-radius: 4px;
214
+ transition: all 0.2s ease;
215
+ padding: 6px 12px;
216
+ }
217
+ .upload-button:hover {
218
+ background-color: #60a5fa;
219
+ }
220
+ .upload-button input[type=file] {
221
+ position: absolute;
222
+ top: 0;
223
+ right: 0;
224
+ min-width: 100%;
225
+ min-height: 100%;
226
+ opacity: 0;
227
+ cursor: pointer;
228
+ }
229
+
230
+ /* New Chat button specific styling */
231
+ .new-chat-button {
232
+ background-color: #3b82f6;
233
+ color: #ffffff;
234
+ padding: 8px 16px;
235
+ border-radius: 6px;
236
+ border: none;
237
+ cursor: pointer;
238
+ transition: all 0.2s ease;
239
+ font-weight: 500;
240
+ }
241
+ .new-chat-button:hover {
242
+ background-color: #60a5fa;
243
+ }
244
+
245
+ /* Chat title styles */
246
+ .chat-title {
247
+ color: #e6e6e6;
248
+ font-weight: 600;
249
+ margin-bottom: 4px;
250
+ }
251
+
252
+ /* Chat date styles */
253
+ .chat-date {
254
+ color: #9ca3af;
255
+ font-size: 0.875rem;
256
+ }
257
+
258
+ /* Message input area */
259
+ .message-input {
260
+ background-color: #1e293b;
261
+ border: 1px solid #4a5568;
262
+ color: #e6e6e6;
263
+ border-radius: 6px;
264
+ padding: 12px;
265
+ width: 100%;
266
+ margin-bottom: 10px;
267
+ }
268
+ .message-input:focus {
269
+ outline: none;
270
+ border-color: #60a5fa;
271
+ box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2);
272
+ }
273
+
274
+ /* Send button */
275
+ .send-button {
276
+ background-color: #3b82f6;
277
+ color: #ffffff;
278
+ padding: 8px 16px;
279
+ border-radius: 6px;
280
+ border: none;
281
+ cursor: pointer;
282
+ transition: all 0.2s ease;
283
+ font-weight: 500;
284
+ }
285
+ .send-button:hover {
286
+ background-color: #60a5fa;
287
+ }
288
+ .header-container {
289
+ text-align: center;
290
+ padding: 1rem 0;
291
+ margin-bottom: 2rem;
292
+ position: relative;
293
+ overflow: hidden;
294
+ }
295
+
296
+ .title-wrapper {
297
+ display: inline-flex;
298
+ align-items: center;
299
+ gap: 0.75rem;
300
+ padding: 0.5rem 1.5rem;
301
+ background: linear-gradient(135deg, rgba(59, 130, 246, 0.1), rgba(147, 197, 253, 0.1));
302
+ border-radius: 12px;
303
+ border: 1px solid rgba(59, 130, 246, 0.2);
304
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
305
+ }
306
+
307
+ .title {
308
+ font-size: 1.75rem;
309
+ font-weight: 700;
310
+ background: linear-gradient(135deg, #60A5FA, #3B82F6);
311
+ -webkit-background-clip: text;
312
+ -webkit-text-fill-color: transparent;
313
+ position: relative;
314
+ }
315
+
316
+ .logo-icon {
317
+ width: 32px;
318
+ height: 32px;
319
+ animation: pulse 2s infinite;
320
+ }
321
+
322
+ .status-dot {
323
+ width: 8px;
324
+ height: 8px;
325
+ background-color: #10B981;
326
+ border-radius: 50%;
327
+ position: relative;
328
+ display: inline-block;
329
+ margin-left: 0.5rem;
330
+ animation: blink 2s infinite;
331
+ }
332
+
333
+ /* Floating particles */
334
+ .particle {
335
+ position: absolute;
336
+ width: 4px;
337
+ height: 4px;
338
+ background: rgba(59, 130, 246, 0.2);
339
+ border-radius: 50%;
340
+ pointer-events: none;
341
+ }
342
+
343
+ .particle:nth-child(1) { animation: float-1 8s infinite; }
344
+ .particle:nth-child(2) { animation: float-2 10s infinite; }
345
+ .particle:nth-child(3) { animation: float-3 7s infinite; }
346
+ .particle:nth-child(4) { animation: float-4 9s infinite; }
347
+
348
+ @keyframes pulse {
349
+ 0% { transform: scale(1); }
350
+ 50% { transform: scale(1.1); }
351
+ 100% { transform: scale(1); }
352
+ }
353
+
354
+ @keyframes blink {
355
+ 0% { opacity: 1; }
356
+ 50% { opacity: 0.4; }
357
+ 100% { opacity: 1; }
358
+ }
359
+
360
+ @keyframes float-1 {
361
+ 0%, 100% { transform: translate(0, 0); }
362
+ 50% { transform: translate(20px, -20px); }
363
+ }
364
+
365
+ @keyframes float-2 {
366
+ 0%, 100% { transform: translate(0, 0); }
367
+ 50% { transform: translate(-15px, -25px); }
368
+ }
369
+
370
+ @keyframes float-3 {
371
+ 0%, 100% { transform: translate(0, 0); }
372
+ 50% { transform: translate(25px, -15px); }
373
+ }
374
+
375
+ @keyframes float-4 {
376
+ 0%, 100% { transform: translate(0, 0); }
377
+ 50% { transform: translate(-20px, -10px); }
378
+ }
379
+ .model-label {
380
+ position: fixed;
381
+ top: 1rem;
382
+ right: 1rem;
383
+ background-color: #f3f4f6;
384
+ color: #4b5563;
385
+ padding: 0.25rem 0.75rem;
386
+ border-radius: 9999px;
387
+ font-size: 0.875rem;
388
+ font-weight: 500;
389
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
390
+ z-index: 50;
391
+ }
392
+ .loading-indicator {
393
+ position: sticky;
394
+ bottom: 0;
395
+ left: 0;
396
+ right: 0;
397
+ background: rgba(17, 24, 39, 0.95);
398
+ padding: 1rem;
399
+ border-top: 1px solid #4b5563;
400
+ z-index: 50;
401
+ display: flex;
402
+ flex-direction: column;
403
+ align-items: center;
404
+ gap: 0.5rem;
405
+ }
406
+
407
+ .loading-indicator.hidden {
408
+ display: none;
409
+ }
410
+
411
+ .loading-bar {
412
+ width: 200px;
413
+ height: 4px;
414
+ background: #374151;
415
+ border-radius: 2px;
416
+ overflow: hidden;
417
+ }
418
+
419
+ .loading-progress {
420
+ width: 40%;
421
+ height: 100%;
422
+ background: #3b82f6;
423
+ border-radius: 2px;
424
+ animation: moveProgress 1.5s infinite ease-in-out;
425
+ }
426
+
427
+ .loading-text {
428
+ color: #9ca3af;
429
+ font-size: 0.875rem;
430
+ font-weight: 500;
431
+ }
432
+
433
+ @keyframes moveProgress {
434
+ 0% {
435
+ transform: translateX(-100%);
436
+ }
437
+ 50% {
438
+ transform: translateX(100%);
439
+ }
440
+ 100% {
441
+ transform: translateX(-100%);
442
+ }
443
+ }
444
+ </style>
445
+ </head>
446
+ <body class="bg-gray-900">
447
+ <div class="model-label">Mistral:7b</div>
448
+ <div class="container mx-auto px-4 py-8">
449
+ <h1 class="text-2xl font-bold text-blue-500 text-center mb-6">Figr AI Code Assistant</h1>
450
+ <div class="flex gap-4">
451
+ <!-- Sidebar with chat list -->
452
+ <div class="w-1/4 bg-gray-800 rounded-lg shadow-lg p-4">
453
+ <div class="flex justify-between items-center mb-4">
454
+ <h2 class="text-xl font-bold text-gray-100">Chats</h2>
455
+ <button onclick="createNewChat()" class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600">
456
+ New Chat
457
+ </button>
458
+ </div>
459
+ <div id="chatList" class="chat-list">
460
+ <!-- Chat items will be dynamically added here -->
461
+ </div>
462
+ </div>
463
+
464
+ <!-- Main chat area -->
465
+ <div class="w-3/4 bg-gray-800 rounded-lg shadow-lg p-4">
466
+ <div id="chatArea" class="chat-area mb-4">
467
+ <!-- Messages will be dynamically added here -->
468
+ </div>
469
+
470
+ <!-- Input area -->
471
+ <div class="flex gap-2">
472
+ <textarea
473
+ id="userInput"
474
+ class="w-full p-2 border rounded-lg resize-none bg-gray-700 text-gray-100"
475
+ rows="3"
476
+ placeholder="Type your message here..."
477
+ ></textarea>
478
+ <div class="flex flex-col gap-2">
479
+ <label class="relative cursor-pointer bg-gray-700 hover:bg-gray-600 p-2 rounded-lg flex items-center justify-center">
480
+ <input type="file"
481
+ id="fileInput"
482
+ accept=".py"
483
+ class="hidden"
484
+ onchange="handleFileUpload(event)"/>
485
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-blue-400">
486
+ <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
487
+ <polyline points="17 8 12 3 7 8"></polyline>
488
+ <line x1="12" y1="3" x2="12" y2="15"></line>
489
+ </svg>
490
+ </label>
491
+ <button
492
+ onclick="sendMessage()"
493
+ class="bg-blue-500 text-white px-6 py-2 rounded-lg hover:bg-blue-600"
494
+ >
495
+ Send
496
+ </button>
497
+ </div>
498
+ </div>
499
+ <div id="loadingIndicator" class="loading-indicator hidden">
500
+ <div class="loading-bar">
501
+ <div class="loading-progress"></div>
502
+ </div>
503
+ <div class="loading-text">Generating...</div>
504
+ </div>
505
+
506
+ <!-- Important Information -->
507
+ <div class="mt-4">
508
+ <h3 class="text-lg font-bold mb-2 text-gray-100">Important Information</h3>
509
+ <div id="importantInfo" class="bg-gray-800 p-4 rounded-lg text-gray-100 border border-gray-700">
510
+ <!-- Important info will be added here -->
511
+ </div>
512
+ </div>
513
+ </div>
514
+ </div>
515
+ </div>
516
+
517
+ <script>
518
+ let currentSessionId = null;
519
+
520
+ // Initialize marked with syntax highlighting
521
+ marked.setOptions({
522
+ highlight: function(code, lang) {
523
+ return hljs.highlight(code, {language: lang || 'plaintext'}).value;
524
+ }
525
+ });
526
+ const renderer = new marked.Renderer();
527
+ renderer.code = function(code, language) {
528
+ const highlightedCode = language ? hljs.highlight(code, {language}).value : hljs.highlightAuto(code).value;
529
+ return `
530
+ <div class="code-block-wrapper">
531
+ <button class="test-button">Test Code</button>
532
+ <button class="copy-button">Copy Code</button>
533
+ <pre><code class="hljs ${language || ''}">${highlightedCode}</code></pre>
534
+ <div class="test-results"></div>
535
+ </div>
536
+ `;
537
+ };
538
+ marked.setOptions({ renderer });
539
+ // Create new chat
540
+ async function createNewChat() {
541
+ try {
542
+ const response = await fetch('/api/new-chat', {
543
+ method: 'POST',
544
+ headers: {
545
+ 'Content-Type': 'application/json'
546
+ }
547
+ });
548
+ const data = await response.json();
549
+ if (data.success) {
550
+ currentSessionId = data.chat.id;
551
+ await loadChatList();
552
+ clearChatArea();
553
+ }
554
+ } catch (error) {
555
+ console.error('Error creating new chat:', error);
556
+ }
557
+ }
558
+
559
+ // Load chat list
560
+ async function loadChatList() {
561
+ try {
562
+ const response = await fetch('/api/chat-list');
563
+ const data = await response.json();
564
+ const chatListElement = document.getElementById('chatList');
565
+ chatListElement.innerHTML = '';
566
+
567
+ data.chats.forEach(chat => {
568
+ const chatElement = document.createElement('div');
569
+ chatElement.className = `chat-item p-3 cursor-pointer rounded ${
570
+ chat.id === currentSessionId ? 'active-chat' : ''
571
+ }`;
572
+ chatElement.onclick = () => loadChat(chat.id);
573
+ chatElement.innerHTML = `
574
+ <div class="font-medium">${chat.title || 'New Chat'}</div>
575
+ <div class="text-sm text-gray-500">${new Date(chat.date).toLocaleDateString()}</div>
576
+ `;
577
+ chatListElement.appendChild(chatElement);
578
+ });
579
+ } catch (error) {
580
+ console.error('Error loading chat list:', error);
581
+ }
582
+ }
583
+
584
+ // Load specific chat
585
+ async function loadChat(sessionId) {
586
+ currentSessionId = sessionId;
587
+ try {
588
+ const response = await fetch(`/api/chat-history?sessionId=${sessionId}`);
589
+ const data = await response.json();
590
+ displayChatHistory(data.history);
591
+ displayImportantInfo(data.important_info);
592
+ await loadChatList(); // Refresh chat list to update active state
593
+ } catch (error) {
594
+ console.error('Error loading chat:', error);
595
+ }
596
+ }
597
+
598
+ // Display chat history
599
+ function displayChatHistory(history) {
600
+ const chatArea = document.getElementById('chatArea');
601
+ chatArea.innerHTML = '';
602
+ history.forEach(message => {
603
+ displayMessage(message.role, message.content);
604
+ });
605
+ scrollToBottom();
606
+ }
607
+
608
+ // Display important information
609
+ function displayImportantInfo(info) {
610
+ const infoElement = document.getElementById('importantInfo');
611
+ infoElement.innerHTML = info.length > 0
612
+ ? info.map(item => `<p>• ${item}</p>`).join('')
613
+ : '<p>No important information yet</p>';
614
+ }
615
+
616
+ // Send message
617
+ async function sendMessage() {
618
+ const userInput = document.getElementById('userInput');
619
+ const message = userInput.value.trim();
620
+ const loadingIndicator = document.getElementById('loadingIndicator');
621
+
622
+ if (!message) return;
623
+ if (!currentSessionId) {
624
+ await createNewChat();
625
+ }
626
+
627
+ displayMessage('user', message);
628
+ userInput.value = '';
629
+ scrollToBottom();
630
+
631
+ // Show loading indicator
632
+ loadingIndicator.classList.remove('hidden');
633
+
634
+ try {
635
+ const response = await fetch('/api/chat', {
636
+ method: 'POST',
637
+ headers: {
638
+ 'Content-Type': 'application/json'
639
+ },
640
+ body: JSON.stringify({
641
+ message: message,
642
+ sessionId: currentSessionId
643
+ })
644
+ });
645
+
646
+ const data = await response.json();
647
+
648
+ // Hide loading indicator
649
+ loadingIndicator.classList.add('hidden');
650
+
651
+ if (data.success) {
652
+ displayMessage('assistant', data.response);
653
+ displayImportantInfo(data.important_info);
654
+ await loadChatList();
655
+ } else {
656
+ displayMessage('assistant', 'Error: ' + data.response);
657
+ }
658
+ } catch (error) {
659
+ // Hide loading indicator on error too
660
+ loadingIndicator.classList.add('hidden');
661
+ console.error('Error sending message:', error);
662
+ displayMessage('assistant', 'Error sending message');
663
+ }
664
+ scrollToBottom();
665
+ }
666
+ async function handleFileUpload(event) {
667
+ const file = event.target.files[0];
668
+ if (!file) return;
669
+
670
+ if (!file.name.endsWith('.py')) {
671
+ alert('Please upload only Python (.py) files');
672
+ return;
673
+ }
674
+
675
+ const formData = new FormData();
676
+ formData.append('file', file);
677
+
678
+ try {
679
+ const response = await fetch('/api/upload', {
680
+ method: 'POST',
681
+ body: formData
682
+ });
683
+
684
+ const data = await response.json();
685
+
686
+ if (data.success) {
687
+ // Add the file content and analysis to the chat
688
+ const message = `I've uploaded a Python file named "${data.filename}". Here's the code:\n\n\`\`\`python\n${data.content}\n\`\`\``;
689
+ document.getElementById('userInput').value = message;
690
+ await sendMessage();
691
+
692
+ // Send the analysis as an assistant message
693
+ const analysisMessage = data.analysis;
694
+ displayMessage('assistant', analysisMessage);
695
+
696
+ // Clear the file input
697
+ event.target.value = '';
698
+ } else {
699
+ alert(data.error || 'Error uploading file');
700
+ }
701
+ } catch (error) {
702
+ console.error('Error:', error);
703
+ alert('Error uploading file');
704
+ }
705
+ }
706
+
707
+
708
+ // Display a single message
709
+ function displayMessage(role, content) {
710
+ const chatArea = document.getElementById('chatArea');
711
+ const messageDiv = document.createElement('div');
712
+ messageDiv.className = `message-bubble ${role}-message`;
713
+
714
+ // Create a div for message content
715
+ const contentDiv = document.createElement('div');
716
+ contentDiv.className = 'message-content';
717
+
718
+ // Custom renderer for marked to wrap code blocks (same as before)
719
+ const renderer = new marked.Renderer();
720
+ renderer.code = function(code, language) {
721
+ const highlightedCode = language ? hljs.highlight(code, {language}).value : hljs.highlightAuto(code).value;
722
+
723
+ // Simplified HTML structure with both buttons
724
+ const buttonsHtml = `
725
+ <button class="test-button">Test Code</button>
726
+ <button class="copy-button">Copy Code</button>
727
+ `;
728
+
729
+ return `
730
+ <div class="code-block-wrapper">
731
+ ${buttonsHtml}
732
+ <pre><code class="hljs ${language || ''}">${highlightedCode}</code></pre>
733
+ <div class="test-results"></div>
734
+ </div>
735
+ `;
736
+ };
737
+
738
+ marked.setOptions({ renderer });
739
+
740
+ // Parse markdown and render HTML
741
+ contentDiv.innerHTML = marked.parse(content);
742
+ messageDiv.appendChild(contentDiv);
743
+
744
+ // Immediately initialize code blocks after adding content
745
+ messageDiv.querySelectorAll('.code-block-wrapper').forEach(wrapper => {
746
+ // Ensure buttons exist
747
+ if (!wrapper.querySelector('.test-button') || !wrapper.querySelector('.copy-button')) {
748
+ const buttonsDiv = document.createElement('div');
749
+ buttonsDiv.innerHTML = `
750
+ <button class="test-button">Test Code</button>
751
+ <button class="copy-button">Copy Code</button>
752
+ `;
753
+ wrapper.insertBefore(buttonsDiv, wrapper.firstChild);
754
+ }
755
+
756
+ const copyButton = wrapper.querySelector('.copy-button');
757
+ const testButton = wrapper.querySelector('.test-button');
758
+ const codeElement = wrapper.querySelector('code');
759
+ const resultsElement = wrapper.querySelector('.test-results') || (() => {
760
+ const div = document.createElement('div');
761
+ div.className = 'test-results';
762
+ wrapper.appendChild(div);
763
+ return div;
764
+ })();
765
+
766
+ // Copy button handler
767
+ copyButton.addEventListener('click', async () => {
768
+ try {
769
+ await navigator.clipboard.writeText(codeElement.textContent);
770
+ copyButton.textContent = 'Copied!';
771
+ setTimeout(() => {
772
+ copyButton.textContent = 'Copy Code';
773
+ }, 2000);
774
+ } catch (err) {
775
+ console.error('Failed to copy:', err);
776
+ }
777
+ });
778
+
779
+ // Test button handler
780
+ testButton.addEventListener('click', async () => {
781
+ testButton.textContent = 'Testing...';
782
+ testButton.disabled = true;
783
+
784
+ try {
785
+ const response = await fetch('/api/test-code', {
786
+ method: 'POST',
787
+ headers: {
788
+ 'Content-Type': 'application/json'
789
+ },
790
+ body: JSON.stringify({
791
+ code: codeElement.textContent
792
+ })
793
+ });
794
+
795
+ const data = await response.json();
796
+
797
+ resultsElement.textContent = data.output || 'Code executed successfully with no output';
798
+ resultsElement.className = `test-results show ${data.success ? 'success' : 'error'}`;
799
+
800
+ } catch (error) {
801
+ resultsElement.textContent = `Error: ${error.message}`;
802
+ resultsElement.className = 'test-results show error';
803
+ } finally {
804
+ testButton.textContent = 'Test Code';
805
+ testButton.disabled = false;
806
+ }
807
+ });
808
+ });
809
+
810
+
811
+ // Add retry button for assistant messages
812
+ if (role === 'assistant') {
813
+ const footer = document.createElement('div');
814
+ footer.className = 'message-footer';
815
+ footer.innerHTML = `
816
+ <button class="retry-button">
817
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor">
818
+ <path d="M2.5 2v6h6M21.5 22v-6h-6"/>
819
+ <path d="M22 11.5A10 10 0 003.2 7.2M2 12.5a10 10 0 0018.8 4.2"/>
820
+ </svg>
821
+ Retry
822
+ </button>
823
+ `;
824
+ messageDiv.appendChild(footer);
825
+
826
+ // Add click handler for retry button
827
+ const retryButton = footer.querySelector('.retry-button');
828
+ const lastUserMessage = findLastUserMessage();
829
+ if (lastUserMessage) {
830
+ retryButton.addEventListener('click', () => retryMessage(lastUserMessage, messageDiv));
831
+ }
832
+ }
833
+
834
+ // Add copy button functionality
835
+ addCopyButtons(contentDiv);
836
+
837
+ chatArea.appendChild(messageDiv);
838
+ }
839
+
840
+ // Helper function to find the last user message before an assistant message
841
+ function findLastUserMessage() {
842
+ const chatArea = document.getElementById('chatArea');
843
+ const messages = chatArea.querySelectorAll('.message-bubble');
844
+ let lastUserMessage = null;
845
+
846
+ for (let i = messages.length - 1; i >= 0; i--) {
847
+ if (messages[i].classList.contains('user-message')) {
848
+ const contentDiv = messages[i].querySelector('.message-content');
849
+ if (contentDiv) {
850
+ lastUserMessage = contentDiv.textContent;
851
+ break;
852
+ }
853
+ }
854
+ }
855
+
856
+ return lastUserMessage;
857
+ }
858
+
859
+ // Clear chat area
860
+ function clearChatArea() {
861
+ document.getElementById('chatArea').innerHTML = '';
862
+ document.getElementById('importantInfo').innerHTML = '<p>No important information yet</p>';
863
+ document.getElementById('userInput').value = '';
864
+ }
865
+
866
+ // Scroll chat area to bottom
867
+ function scrollToBottom() {
868
+ const chatArea = document.getElementById('chatArea');
869
+ chatArea.scrollTop = chatArea.scrollHeight;
870
+ }
871
+
872
+ // Handle Enter key in textarea
873
+ document.getElementById('userInput').addEventListener('keydown', function(e) {
874
+ if (e.key === 'Enter' && !e.shiftKey) {
875
+ e.preventDefault();
876
+ sendMessage();
877
+ }
878
+ });
879
+
880
+ // Initialize
881
+ window.onload = async function() {
882
+ await loadChatList();
883
+ };
884
+ async function copyCode(codeElement, tooltipElement) {
885
+ try {
886
+ await navigator.clipboard.writeText(codeElement.textContent);
887
+ tooltipElement.textContent = 'Copied!';
888
+ tooltipElement.classList.add('show');
889
+ setTimeout(() => {
890
+ tooltipElement.classList.remove('show');
891
+ }, 1500);
892
+ } catch (err) {
893
+ console.error('Failed to copy code:', err);
894
+ tooltipElement.textContent = 'Failed to copy';
895
+ tooltipElement.classList.add('show');
896
+ setTimeout(() => {
897
+ tooltipElement.classList.remove('show');
898
+ }, 1500);
899
+ }
900
+ }
901
+ async function retryMessage(originalMessage, messageElement) {
902
+ if (!currentSessionId) return;
903
+
904
+ const retryButton = messageElement.querySelector('.retry-button');
905
+
906
+
907
+ // Show loading state
908
+ retryButton.classList.add('loading');
909
+
910
+ retryButton.innerHTML = `
911
+ <svg class="animate-spin" viewBox="0 0 24 24">
912
+ <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
913
+ <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path>
914
+ </svg>
915
+ Regenerating...
916
+ `;
917
+
918
+ try {
919
+ const response = await fetch('/api/chat', {
920
+ method: 'POST',
921
+ headers: {
922
+ 'Content-Type': 'application/json'
923
+ },
924
+ body: JSON.stringify({
925
+ message: originalMessage,
926
+ sessionId: currentSessionId
927
+ })
928
+ });
929
+
930
+ const data = await response.json();
931
+ if (data.success) {
932
+ // Replace the old message content with new content
933
+ const contentDiv = messageElement.querySelector('.message-content');
934
+ contentDiv.innerHTML = marked.parse(data.response);
935
+
936
+ // Reapply syntax highlighting
937
+ contentDiv.querySelectorAll('pre code').forEach((block) => {
938
+ hljs.highlightBlock(block);
939
+ });
940
+
941
+ // Add copy buttons to new code blocks
942
+ addCopyButtons(contentDiv);
943
+
944
+ // Update important info
945
+ displayImportantInfo(data.important_info);
946
+ } else {
947
+ alert('Error regenerating response: ' + data.response);
948
+ }
949
+ } catch (error) {
950
+ console.error('Error regenerating message:', error);
951
+ alert('Error regenerating response');
952
+ } finally {
953
+ // Restore retry button
954
+ retryButton.classList.remove('loading');
955
+ retryButton.innerHTML = `
956
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor">
957
+ <path d="M2.5 2v6h6M21.5 22v-6h-6"/>
958
+ <path d="M22 11.5A10 10 0 003.2 7.2M2 12.5a10 10 0 0018.8 4.2"/>
959
+ </svg>
960
+ Retry
961
+ `;
962
+ }
963
+ }
964
+
965
+ // Helper function to add copy buttons to code blocks
966
+ function addCopyButtons(element) {
967
+ element.querySelectorAll('.code-block-wrapper').forEach(wrapper => {
968
+ const copyButton = wrapper.querySelector('.copy-button');
969
+ const codeElement = wrapper.querySelector('code');
970
+ const tooltipElement = wrapper.querySelector('.copy-tooltip');
971
+
972
+ copyButton.addEventListener('click', () => copyCode(codeElement, tooltipElement));
973
+
974
+ copyButton.addEventListener('mouseenter', () => {
975
+ tooltipElement.textContent = 'Copy';
976
+ tooltipElement.classList.add('show');
977
+ });
978
+ copyButton.addEventListener('mouseleave', () => {
979
+ if (tooltipElement.textContent === 'Copy') {
980
+ tooltipElement.classList.remove('show');
981
+ }
982
+ });
983
+ });
984
+ }
985
+ </script>
986
+ </body>
987
+ </html>