Athspi commited on
Commit
ab375e9
·
verified ·
1 Parent(s): d9e48e0

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +119 -97
templates/index.html CHANGED
@@ -78,8 +78,8 @@
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;
@@ -88,6 +88,7 @@
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);
@@ -103,6 +104,12 @@
103
  overflow-y: auto;
104
  }
105
 
 
 
 
 
 
 
106
  /* --- File Tree Styling --- */
107
  #file-tree div {
108
  padding: 8px 12px;
@@ -128,7 +135,7 @@
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;
@@ -163,6 +170,8 @@
163
  padding: 40px;
164
  border-radius: 12px;
165
  border: 1px dashed var(--border-color);
 
 
166
  }
167
  .upload-icon {
168
  font-size: 3rem;
@@ -175,7 +184,6 @@
175
  }
176
  .upload-box p {
177
  color: var(--text-secondary);
178
- max-width: 400px;
179
  margin-bottom: 30px;
180
  }
181
  .upload-label {
@@ -245,12 +253,14 @@
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);
@@ -259,8 +269,8 @@
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;
@@ -271,6 +281,7 @@
271
  align-items: center;
272
  padding: 15px 20px;
273
  border-top: 1px solid var(--border-color);
 
274
  }
275
 
276
  #message-input {
@@ -285,6 +296,8 @@
285
  font-size: 1rem;
286
  line-height: 1.5;
287
  transition: border-color 0.2s ease;
 
 
288
  }
289
  #message-input:focus {
290
  outline: none;
@@ -331,7 +344,7 @@
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>
@@ -369,7 +382,7 @@
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>
@@ -381,7 +394,7 @@
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>
@@ -422,29 +435,39 @@
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);
@@ -468,92 +491,91 @@
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
 
 
78
  height: 100vh;
79
  transition: width 0.3s ease;
80
  }
81
+ .left-sidebar { width: 20%; min-width: 250px; }
82
+ .right-sidebar { width: 25%; min-width: 300px; border-right: none; border-left: 1px solid var(--border-color); }
83
 
84
  .sidebar-header {
85
  display: flex;
 
88
  padding-bottom: 15px;
89
  border-bottom: 1px solid var(--border-color);
90
  margin-bottom: 15px;
91
+ flex-shrink: 0;
92
  }
93
  .sidebar-header i {
94
  color: var(--primary-color);
 
104
  overflow-y: auto;
105
  }
106
 
107
+ .sidebar-footer {
108
+ flex-shrink: 0;
109
+ padding-top: 15px;
110
+ border-top: 1px solid var(--border-color);
111
+ }
112
+
113
  /* --- File Tree Styling --- */
114
  #file-tree div {
115
  padding: 8px 12px;
 
135
 
136
  /* --- File Content Viewer --- */
137
  #file-content-display {
138
+ background-color: #161625;
139
  border-radius: 8px;
140
  height: 100%;
141
  padding: 15px;
 
170
  padding: 40px;
171
  border-radius: 12px;
172
  border: 1px dashed var(--border-color);
173
+ width: 90%;
174
+ max-width: 500px;
175
  }
176
  .upload-icon {
177
  font-size: 3rem;
 
184
  }
185
  .upload-box p {
186
  color: var(--text-secondary);
 
187
  margin-bottom: 30px;
188
  }
189
  .upload-label {
 
253
  color: var(--background-primary);
254
  border-bottom-right-radius: 4px;
255
  align-self: flex-end;
256
+ word-wrap: break-word;
257
  }
258
  .ai-message {
259
  background-color: var(--background-secondary);
260
  color: var(--text-primary);
261
  border-bottom-left-radius: 4px;
262
  align-self: flex-start;
263
+ word-wrap: break-word;
264
  }
265
  .ai-message pre {
266
  background-color: var(--background-primary);
 
269
  padding: 15px;
270
  margin-top: 10px;
271
  font-size: 0.85rem;
272
+ white-space: pre-wrap; /* Since CSS 2.1 */
273
+ word-wrap: break-word; /* Internet Explorer 5.5+ */
274
  }
275
  .ai-message code {
276
  font-family: 'Fira Code', 'Courier New', monospace;
 
281
  align-items: center;
282
  padding: 15px 20px;
283
  border-top: 1px solid var(--border-color);
284
+ flex-shrink: 0;
285
  }
286
 
287
  #message-input {
 
296
  font-size: 1rem;
297
  line-height: 1.5;
298
  transition: border-color 0.2s ease;
299
+ max-height: 200px;
300
+ overflow-y: auto;
301
  }
302
  #message-input:focus {
303
  outline: none;
 
344
  <div class="container">
345
  <div class="sidebar left-sidebar">
346
  <div class="sidebar-header">
347
+ <i class="fa-solid fa-folder-tree"></i>
348
  <h2>Project Files</h2>
349
  </div>
350
  <div id="file-tree" class="sidebar-content"></div>
 
382
  <div id="chat-window">
383
  </div>
384
  <div class="chat-input-area">
385
+ <textarea id="message-input" placeholder="Ask the AI to refactor, explain, or improve your code..." rows="1"></textarea>
386
  <button id="send-btn"><i class="fa-solid fa-paper-plane"></i></button>
387
  </div>
388
  </div>
 
394
  <h2>File Content</h2>
395
  </div>
396
  <div class="sidebar-content">
397
+ <pre id="file-content-display"><code class="plaintext">Select a file from the left to view its content.</code></pre>
398
  </div>
399
  </div>
400
  </div>
 
435
  document.querySelector('.upload-box').style.display = 'none';
436
  progressIndicator.style.display = 'block';
437
 
438
+ try {
439
+ const response = await fetch('/upload', {
440
+ method: 'POST',
441
+ body: formData
442
+ });
 
 
443
 
444
+ const data = await response.json();
445
+ progressIndicator.style.display = 'none';
446
+
447
+ if (data.error) {
448
+ alert(data.error);
449
+ document.querySelector('.upload-box').style.display = 'block';
450
+ return;
451
+ }
452
+
453
+ uploadSection.style.display = 'none';
454
+ chatSection.style.display = 'flex';
455
+ updateFileTree(data.file_tree);
456
+ chatWindow.innerHTML = '';
457
+ data.chat_history.forEach(msg => {
458
+ const role = msg.role === 'model' ? 'assistant' : msg.role;
459
+ let content = msg.content;
460
+ // For the very first message, don't show the full file context
461
+ if (role === 'user' && content.startsWith('The user has uploaded a new project')) {
462
+ content = 'Project context sent to AI.'
463
+ }
464
+ appendMessage(role, content);
465
+ });
466
+ } catch (error) {
467
+ progressIndicator.style.display = 'none';
468
+ alert('An error occurred during upload. Please check the console and ensure the backend is running.');
469
  document.querySelector('.upload-box').style.display = 'block';
 
470
  }
 
 
 
 
 
 
 
 
 
 
471
  });
472
 
473
  sendBtn.addEventListener('click', sendMessage);
 
491
  messageInput.value = '';
492
  messageInput.style.height = 'auto'; // Reset height
493
 
494
+ try {
495
+ const response = await fetch('/chat', {
496
+ method: 'POST',
497
+ headers: { 'Content-Type': 'application/json' },
498
+ body: JSON.stringify({ message: message })
499
+ });
500
+
501
+ const data = await response.json();
 
 
 
 
 
 
502
 
503
+ if(data.error) {
504
+ appendMessage('assistant', `**Error:** ${data.error}`);
505
+ } else {
506
+ appendMessage('assistant', data.reply);
507
+ }
 
508
 
509
+ updateFileTree(); // Refresh file tree in case files were created/deleted
510
+ } catch (error) {
511
+ appendMessage('assistant', `**Error:** Could not connect to the backend. Please ensure it is running correctly.`);
512
+ }
513
  }
514
 
515
+ function appendMessage(sender, text) {
516
  const messageElement = document.createElement('div');
517
  messageElement.classList.add('message', sender === 'user' ? 'user-message' : 'ai-message');
518
 
519
+ // Basic Markdown to HTML conversion
520
+ let html = text.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>'); // Bold
521
+ html = html.replace(/\`(.*?)\`/g, '<code>$1</code>'); // Inline code
522
+
523
+ // Convert Markdown code blocks to highlighted HTML
524
+ html = html.replace(/```(\w+)?\n([\s\S]*?)```/g, (match, lang, code) => {
525
+ const trimmedCode = code.trim();
526
+ const highlighted = hljs.highlight(trimmedCode, { language: lang || 'plaintext', ignoreIllegals: true }).value;
527
+ return `<pre><code class="hljs <span class="math-inline">\{lang\}"\></span>{highlighted}</code></pre>`;
528
+ });
529
+
530
+ messageElement.innerHTML = html.replace(/\n/g, '<br>');
 
 
 
531
 
532
  chatWindow.appendChild(messageElement);
533
  chatWindow.scrollTop = chatWindow.scrollHeight;
534
  }
535
 
536
  async function updateFileTree() {
537
+ try {
538
+ const response = await fetch('/file_tree');
539
+ const data = await response.json();
540
+ if (data.file_tree) {
541
+ const currentlyActive = document.querySelector('#file-tree .active');
542
+ const activeFilePath = currentlyActive ? currentlyActive.dataset.path : null;
543
+
544
+ fileTree.innerHTML = '';
545
+ data.file_tree.forEach(path => {
546
+ const fileElement = document.createElement('div');
547
+ fileElement.textContent = path;
548
+ fileElement.dataset.path = path;
549
+ fileElement.addEventListener('click', () => {
550
+ document.querySelectorAll('#file-tree div').forEach(el => el.classList.remove('active'));
551
+ fileElement.classList.add('active');
552
+ fetchFileContent(path);
553
+ });
554
+
555
+ if(path === activeFilePath) {
556
+ fileElement.classList.add('active');
557
+ }
558
+
559
+ fileTree.appendChild(fileElement);
560
  });
561
+ }
562
+ } catch(error) {
563
+ console.error("Could not update file tree:", error);
 
 
 
 
564
  }
565
  }
566
 
567
  async function fetchFileContent(path) {
568
+ try {
569
+ const response = await fetch(`/file_content?path=${encodeURIComponent(path)}`);
570
+ const data = await response.json();
571
+ if (data.content) {
572
+ fileContentDisplay.textContent = data.content;
573
+ hljs.highlightElement(fileContentDisplay);
574
+ } else {
575
+ fileContentDisplay.textContent = 'Could not load file content.';
576
+ }
577
+ } catch (error) {
578
+ fileContentDisplay.textContent = 'Error loading file content.';
579
  }
580
  }
581