Athspi commited on
Commit
d1937f0
·
verified ·
1 Parent(s): e4cd3fb

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +546 -30
templates/index.html CHANGED
@@ -1,52 +1,568 @@
1
  <!DOCTYPE html>
2
- <html>
3
  <head>
4
- <title>Upload Project - AI Code Editor</title>
 
 
 
 
 
 
 
 
 
 
5
  <style>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  body {
7
- font-family: Arial, sans-serif;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  padding: 20px;
9
- background-color: #f4f4f4;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  }
11
- h1 {
12
- color: #333;
 
13
  }
14
- form {
15
- background: #fff;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  padding: 20px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  border-radius: 8px;
18
- box-shadow: 0 0 10px #ccc;
19
- max-width: 500px;
 
 
 
 
 
 
 
 
20
  }
21
- input[type="file"], input[type="submit"] {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  margin-top: 10px;
23
- padding: 10px;
24
- font-size: 16px;
 
 
 
 
25
  }
26
- input[type="submit"] {
27
- background-color: #4CAF50;
28
- color: white;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  border: none;
 
 
30
  cursor: pointer;
 
 
 
31
  }
32
- input[type="submit"]:hover {
33
- background-color: #45a049;
34
  }
35
- </style>
36
- <script>
37
- function showLoading() {
38
- document.getElementById("loading").style.display = "block";
 
 
 
 
 
 
 
 
 
39
  }
40
- </script>
 
 
 
 
 
 
 
41
  </head>
42
  <body>
43
- <h1>AI Project Code Editor</h1>
44
- <form method="POST" enctype="multipart/form-data" onsubmit="showLoading()">
45
- <input type="file" name="files" multiple>
46
- <input type="submit" value="AI Understand Project">
47
- </form>
48
- <div id="loading" style="display:none; margin-top:20px;">
49
- <p>Loading... Please wait while AI is processing your project.</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  </body>
52
  </html>
 
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 Code Assistant</title>
7
+
8
+ <link rel="preconnect" href="https://fonts.googleapis.com">
9
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
+ <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600&display=swap" rel="stylesheet">
11
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
12
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css">
13
+ <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/diff2html/bundles/css/diff2html.min.css"/>
14
+
15
  <style>
16
+ /* --- Root Variables for Theming --- */
17
+ :root {
18
+ --font-family: 'Poppins', sans-serif;
19
+ --background-primary: #1e1e2e;
20
+ --background-secondary: #27293d;
21
+ --background-tertiary: #3b3d5b;
22
+ --text-primary: #cdd6f4;
23
+ --text-secondary: #a6adc8;
24
+ --primary-color: #89b4fa;
25
+ --secondary-color: #cba6f7;
26
+ --accent-color: #f5c2e7;
27
+ --success-color: #a6e3a1;
28
+ --warning-color: #fab387;
29
+ --error-color: #f38ba8;
30
+ --border-color: #45475a;
31
+ --shadow-color: rgba(0, 0, 0, 0.2);
32
+ }
33
+
34
+ /* --- Global Styles & Resets --- */
35
+ * {
36
+ margin: 0;
37
+ padding: 0;
38
+ box-sizing: border-box;
39
+ }
40
+
41
  body {
42
+ font-family: var(--font-family);
43
+ background-color: var(--background-primary);
44
+ color: var(--text-primary);
45
+ display: flex;
46
+ height: 100vh;
47
+ overflow: hidden;
48
+ }
49
+
50
+ /* --- Custom Scrollbar --- */
51
+ ::-webkit-scrollbar {
52
+ width: 8px;
53
+ }
54
+ ::-webkit-scrollbar-track {
55
+ background: var(--background-secondary);
56
+ }
57
+ ::-webkit-scrollbar-thumb {
58
+ background-color: var(--background-tertiary);
59
+ border-radius: 10px;
60
+ }
61
+ ::-webkit-scrollbar-thumb:hover {
62
+ background-color: var(--primary-color);
63
+ }
64
+
65
+ /* --- Main Layout --- */
66
+ .container {
67
+ display: flex;
68
+ width: 100%;
69
+ height: 100%;
70
+ }
71
+
72
+ .sidebar {
73
+ display: flex;
74
+ flex-direction: column;
75
+ background-color: var(--background-secondary);
76
+ border-right: 1px solid var(--border-color);
77
  padding: 20px;
78
+ height: 100vh;
79
+ transition: width 0.3s ease;
80
+ }
81
+ .left-sidebar { width: 20%; }
82
+ .right-sidebar { width: 25%; border-right: none; border-left: 1px solid var(--border-color); }
83
+
84
+ .sidebar-header {
85
+ display: flex;
86
+ align-items: center;
87
+ gap: 12px;
88
+ padding-bottom: 15px;
89
+ border-bottom: 1px solid var(--border-color);
90
+ margin-bottom: 15px;
91
+ }
92
+ .sidebar-header i {
93
+ color: var(--primary-color);
94
+ font-size: 1.2rem;
95
+ }
96
+ .sidebar-header h2 {
97
+ font-size: 1.1rem;
98
+ font-weight: 500;
99
+ }
100
+
101
+ .sidebar-content {
102
+ flex-grow: 1;
103
+ overflow-y: auto;
104
+ }
105
+
106
+ /* --- File Tree Styling --- */
107
+ #file-tree div {
108
+ padding: 8px 12px;
109
+ cursor: pointer;
110
+ border-radius: 6px;
111
+ margin-bottom: 4px;
112
+ transition: background-color 0.2s ease, color 0.2s ease;
113
+ font-size: 0.9rem;
114
+ color: var(--text-secondary);
115
+ white-space: nowrap;
116
+ overflow: hidden;
117
+ text-overflow: ellipsis;
118
  }
119
+ #file-tree div:hover {
120
+ background-color: var(--background-tertiary);
121
+ color: var(--text-primary);
122
  }
123
+ #file-tree div.active {
124
+ background-color: var(--primary-color);
125
+ color: var(--background-primary);
126
+ font-weight: 500;
127
+ }
128
+
129
+ /* --- File Content Viewer --- */
130
+ #file-content-display {
131
+ background-color: var(--background-tertiary);
132
+ border-radius: 8px;
133
+ height: 100%;
134
+ padding: 15px;
135
+ font-size: 0.85rem;
136
+ line-height: 1.6;
137
+ }
138
+ #file-content-display code {
139
+ font-family: 'Fira Code', 'Courier New', monospace;
140
+ }
141
+
142
+ /* --- Main Content Area --- */
143
+ .main-content {
144
+ flex-grow: 1;
145
+ display: flex;
146
+ flex-direction: column;
147
+ padding: 0;
148
+ height: 100vh;
149
+ }
150
+
151
+ /* --- Upload Section --- */
152
+ #upload-section {
153
+ display: flex;
154
+ flex-direction: column;
155
+ justify-content: center;
156
+ align-items: center;
157
+ height: 100%;
158
+ text-align: center;
159
  padding: 20px;
160
+ }
161
+ .upload-box {
162
+ background: var(--background-secondary);
163
+ padding: 40px;
164
+ border-radius: 12px;
165
+ border: 1px dashed var(--border-color);
166
+ }
167
+ .upload-icon {
168
+ font-size: 3rem;
169
+ color: var(--primary-color);
170
+ margin-bottom: 20px;
171
+ }
172
+ .upload-box h2 {
173
+ font-size: 1.8rem;
174
+ margin-bottom: 10px;
175
+ }
176
+ .upload-box p {
177
+ color: var(--text-secondary);
178
+ max-width: 400px;
179
+ margin-bottom: 30px;
180
+ }
181
+ .upload-label {
182
+ background-color: var(--primary-color);
183
+ color: var(--background-primary);
184
+ padding: 12px 20px;
185
  border-radius: 8px;
186
+ cursor: pointer;
187
+ display: inline-block;
188
+ font-weight: 600;
189
+ transition: background-color 0.2s ease;
190
+ }
191
+ .upload-label:hover {
192
+ background-color: var(--accent-color);
193
+ }
194
+ .upload-label i {
195
+ margin-right: 8px;
196
  }
197
+ .file-name {
198
+ margin-top: 15px;
199
+ color: var(--success-color);
200
+ font-weight: 500;
201
+ }
202
+ #progress-indicator p {
203
+ margin-bottom: 15px;
204
+ font-size: 1.1rem;
205
+ }
206
+ .loader {
207
+ border: 4px solid var(--background-tertiary);
208
+ border-radius: 50%;
209
+ border-top: 4px solid var(--primary-color);
210
+ width: 50px;
211
+ height: 50px;
212
+ animation: spin 1.5s linear infinite;
213
+ margin: 20px auto;
214
+ }
215
+ @keyframes spin {
216
+ 0% { transform: rotate(0deg); }
217
+ 100% { transform: rotate(360deg); }
218
+ }
219
+
220
+ /* --- Chat Section --- */
221
+ #chat-section {
222
+ display: none;
223
+ flex-direction: column;
224
+ height: 100%;
225
+ background-color: var(--background-primary);
226
+ }
227
+
228
+ #chat-window {
229
+ flex-grow: 1;
230
+ overflow-y: auto;
231
+ padding: 20px;
232
+ display: flex;
233
+ flex-direction: column;
234
+ gap: 20px;
235
+ }
236
+
237
+ .message {
238
+ padding: 12px 18px;
239
+ border-radius: 18px;
240
+ max-width: 85%;
241
+ line-height: 1.6;
242
+ }
243
+ .user-message {
244
+ background-color: var(--primary-color);
245
+ color: var(--background-primary);
246
+ border-bottom-right-radius: 4px;
247
+ align-self: flex-end;
248
+ }
249
+ .ai-message {
250
+ background-color: var(--background-secondary);
251
+ color: var(--text-primary);
252
+ border-bottom-left-radius: 4px;
253
+ align-self: flex-start;
254
+ }
255
+ .ai-message pre {
256
+ background-color: var(--background-primary);
257
+ border: 1px solid var(--border-color);
258
+ border-radius: 8px;
259
+ padding: 15px;
260
  margin-top: 10px;
261
+ font-size: 0.85rem;
262
+ white-space: pre-wrap;
263
+ word-wrap: break-word;
264
+ }
265
+ .ai-message code {
266
+ font-family: 'Fira Code', 'Courier New', monospace;
267
  }
268
+
269
+ .chat-input-area {
270
+ display: flex;
271
+ align-items: center;
272
+ padding: 15px 20px;
273
+ border-top: 1px solid var(--border-color);
274
+ }
275
+
276
+ #message-input {
277
+ flex-grow: 1;
278
+ background-color: var(--background-secondary);
279
+ color: var(--text-primary);
280
+ border: 1px solid var(--border-color);
281
+ border-radius: 10px;
282
+ padding: 12px 15px;
283
+ resize: none;
284
+ font-family: var(--font-family);
285
+ font-size: 1rem;
286
+ line-height: 1.5;
287
+ transition: border-color 0.2s ease;
288
+ }
289
+ #message-input:focus {
290
+ outline: none;
291
+ border-color: var(--primary-color);
292
+ }
293
+
294
+ #send-btn, #download-btn {
295
+ background-color: transparent;
296
  border: none;
297
+ color: var(--primary-color);
298
+ font-size: 1.5rem;
299
  cursor: pointer;
300
+ transition: color 0.2s ease;
301
+ padding: 10px;
302
+ margin-left: 10px;
303
  }
304
+ #send-btn:hover, #download-btn:hover {
305
+ color: var(--accent-color);
306
  }
307
+
308
+ #download-btn {
309
+ width: 100%;
310
+ background-color: var(--primary-color);
311
+ color: var(--background-primary);
312
+ display: flex;
313
+ align-items: center;
314
+ justify-content: center;
315
+ gap: 10px;
316
+ font-size: 1rem;
317
+ font-weight: 600;
318
+ border-radius: 8px;
319
+ padding: 12px;
320
  }
321
+ #download-btn:hover {
322
+ background-color: var(--accent-color);
323
+ color: var(--background-primary);
324
+ }
325
+ #download-btn i {
326
+ font-size: 1.1rem;
327
+ }
328
+ </style>
329
  </head>
330
  <body>
331
+ <div class="container">
332
+ <div class="sidebar left-sidebar">
333
+ <div class="sidebar-header">
334
+ <i class="fa-solid fa-code"></i>
335
+ <h2>Project Files</h2>
336
+ </div>
337
+ <div id="file-tree" class="sidebar-content"></div>
338
+ <div class="sidebar-footer">
339
+ <button id="download-btn">
340
+ <i class="fa-solid fa-download"></i>
341
+ <span>Download Project</span>
342
+ </button>
343
+ </div>
344
+ </div>
345
+
346
+ <div class="main-content">
347
+ <div id="upload-section">
348
+ <div class="upload-box">
349
+ <i class="fa-solid fa-rocket upload-icon"></i>
350
+ <h2>AI Code Assistant</h2>
351
+ <p>Upload a .zip of your project to begin analyzing and improving your code.</p>
352
+ <form id="upload-form" enctype="multipart/form-data">
353
+ <label for="project-zip" class="upload-label">
354
+ <i class="fa-solid fa-paperclip"></i>
355
+ <span>Choose a .zip file...</span>
356
+ </label>
357
+ <input type="file" id="project-zip" name="project_zip" accept=".zip" hidden>
358
+ <button type="submit" id="upload-button" style="display: none;">Upload and Analyze</button>
359
+ </form>
360
+ <p id="file-name-display" class="file-name"></p>
361
+ </div>
362
+ <div id="progress-indicator" style="display: none;">
363
+ <p>AI is understanding your project...</p>
364
+ <div class="loader"></div>
365
+ </div>
366
+ </div>
367
+
368
+ <div id="chat-section" style="display: none;">
369
+ <div id="chat-window">
370
+ </div>
371
+ <div class="chat-input-area">
372
+ <textarea id="message-input" placeholder="Ask the AI to refactor, explain, or improve your code..."></textarea>
373
+ <button id="send-btn"><i class="fa-solid fa-paper-plane"></i></button>
374
+ </div>
375
+ </div>
376
+ </div>
377
+
378
+ <div class="sidebar right-sidebar">
379
+ <div class="sidebar-header">
380
+ <i class="fa-solid fa-file-lines"></i>
381
+ <h2>File Content</h2>
382
+ </div>
383
+ <div class="sidebar-content">
384
+ <pre id="file-content-display"><code class="python">Select a file from the left to view its content.</code></pre>
385
+ </div>
386
+ </div>
387
  </div>
388
+
389
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
390
+ <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html.min.js"></script>
391
+
392
+ <script>
393
+ document.addEventListener('DOMContentLoaded', function() {
394
+ const uploadForm = document.getElementById('upload-form');
395
+ const progressIndicator = document.getElementById('progress-indicator');
396
+ const chatSection = document.getElementById('chat-section');
397
+ const uploadSection = document.getElementById('upload-section');
398
+ const chatWindow = document.getElementById('chat-window');
399
+ const messageInput = document.getElementById('message-input');
400
+ const sendBtn = document.getElementById('send-btn');
401
+ const fileTree = document.getElementById('file-tree');
402
+ const fileContentDisplay = document.querySelector('#file-content-display code');
403
+ const downloadBtn = document.getElementById('download-btn');
404
+
405
+ const projectZipInput = document.getElementById('project-zip');
406
+ const fileNameDisplay = document.getElementById('file-name-display');
407
+ const uploadButton = document.getElementById('upload-button');
408
+
409
+ // --- Event Listeners ---
410
+ projectZipInput.addEventListener('change', () => {
411
+ if (projectZipInput.files.length > 0) {
412
+ const fileName = projectZipInput.files[0].name;
413
+ fileNameDisplay.textContent = `Selected: ${fileName}`;
414
+ uploadButton.click(); // Automatically trigger the form submission
415
+ }
416
+ });
417
+
418
+ uploadForm.addEventListener('submit', async function(e) {
419
+ e.preventDefault();
420
+ const formData = new FormData(this);
421
+
422
+ document.querySelector('.upload-box').style.display = 'none';
423
+ progressIndicator.style.display = 'block';
424
+
425
+ const response = await fetch('/upload', {
426
+ method: 'POST',
427
+ body: formData
428
+ });
429
+
430
+ const data = await response.json();
431
+ progressIndicator.style.display = 'none';
432
+
433
+ if (data.error) {
434
+ alert(data.error);
435
+ document.querySelector('.upload-box').style.display = 'block';
436
+ return;
437
+ }
438
+
439
+ uploadSection.style.display = 'none';
440
+ chatSection.style.display = 'flex';
441
+ updateFileTree(data.file_tree);
442
+ chatWindow.innerHTML = '';
443
+ data.chat_history.forEach(msg => {
444
+ // Convert Gemini's 'model' role to 'assistant' for frontend consistency
445
+ const role = msg.role === 'model' ? 'assistant' : msg.role;
446
+ appendMessage(role, msg.content);
447
+ });
448
+ });
449
+
450
+ sendBtn.addEventListener('click', sendMessage);
451
+ messageInput.addEventListener('keypress', function(e) {
452
+ if (e.key === 'Enter' && !e.shiftKey) {
453
+ e.preventDefault();
454
+ sendMessage();
455
+ }
456
+ });
457
+
458
+ downloadBtn.addEventListener('click', function() {
459
+ window.location.href = '/download';
460
+ });
461
+
462
+ // --- Core Functions ---
463
+ async function sendMessage() {
464
+ const message = messageInput.value.trim();
465
+ if (!message) return;
466
+
467
+ appendMessage('user', message);
468
+ messageInput.value = '';
469
+ messageInput.style.height = 'auto'; // Reset height
470
+
471
+ // Show a typing indicator for the AI
472
+ appendMessage('assistant', '<div class="typing-indicator"><span></span><span></span><span></span></div>', true);
473
+
474
+ const response = await fetch('/chat', {
475
+ method: 'POST',
476
+ headers: { 'Content-Type': 'application/json' },
477
+ body: JSON.stringify({ message: message })
478
+ });
479
+
480
+ // Remove typing indicator
481
+ const typingIndicator = document.querySelector('.ai-message:last-child');
482
+ if (typingIndicator && typingIndicator.innerHTML.includes('typing-indicator')) {
483
+ typingIndicator.remove();
484
+ }
485
+
486
+ const data = await response.json();
487
+ if(data.error) {
488
+ appendMessage('assistant', `**Error:** ${data.error}`);
489
+ } else {
490
+ appendMessage('assistant', data.reply);
491
+ }
492
+
493
+ updateFileTree(); // Refresh file tree in case files were created/deleted
494
+ }
495
+
496
+ function appendMessage(sender, text, isRawHtml = false) {
497
+ const messageElement = document.createElement('div');
498
+ messageElement.classList.add('message', sender === 'user' ? 'user-message' : 'ai-message');
499
+
500
+ if (isRawHtml) {
501
+ messageElement.innerHTML = text;
502
+ } else {
503
+ // Basic Markdown to HTML conversion
504
+ let html = text.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>'); // Bold
505
+ html = html.replace(/\`(.*?)\`/g, '<code>$1</code>'); // Inline code
506
+
507
+ // Convert Markdown code blocks to highlighted HTML
508
+ html = html.replace(/```(\w+)?\n([\s\S]*?)```/g, (match, lang, code) => {
509
+ const highlighted = hljs.highlight(code.trim(), { language: lang || 'plaintext', ignoreIllegals: true }).value;
510
+ return `<pre><code class="hljs ${lang}">${highlighted}</code></pre>`;
511
+ });
512
+
513
+ messageElement.innerHTML = html.replace(/\n/g, '<br>');
514
+ }
515
+
516
+ chatWindow.appendChild(messageElement);
517
+ chatWindow.scrollTop = chatWindow.scrollHeight;
518
+ }
519
+
520
+ async function updateFileTree() {
521
+ const response = await fetch('/file_tree');
522
+ const data = await response.json();
523
+ if (data.file_tree) {
524
+ const currentlyActive = document.querySelector('#file-tree .active');
525
+ const activeFilePath = currentlyActive ? currentlyActive.dataset.path : null;
526
+
527
+ fileTree.innerHTML = '';
528
+ data.file_tree.forEach(path => {
529
+ const fileElement = document.createElement('div');
530
+ fileElement.textContent = path;
531
+ fileElement.dataset.path = path;
532
+ fileElement.addEventListener('click', () => {
533
+ // Remove active class from any previously active file
534
+ document.querySelectorAll('#file-tree div').forEach(el => el.classList.remove('active'));
535
+ // Add active class to the clicked one
536
+ fileElement.classList.add('active');
537
+ fetchFileContent(path);
538
+ });
539
+
540
+ if(path === activeFilePath) {
541
+ fileElement.classList.add('active');
542
+ }
543
+
544
+ fileTree.appendChild(fileElement);
545
+ });
546
+ }
547
+ }
548
+
549
+ async function fetchFileContent(path) {
550
+ const response = await fetch(`/file_content?path=${encodeURIComponent(path)}`);
551
+ const data = await response.json();
552
+ if (data.content) {
553
+ fileContentDisplay.textContent = data.content;
554
+ hljs.highlightElement(fileContentDisplay);
555
+ } else {
556
+ fileContentDisplay.textContent = 'Could not load file content.';
557
+ }
558
+ }
559
+
560
+ // Auto-resize textarea
561
+ messageInput.addEventListener('input', function () {
562
+ this.style.height = 'auto';
563
+ this.style.height = (this.scrollHeight) + 'px';
564
+ });
565
+ });
566
+ </script>
567
  </body>
568
  </html>