Docfile commited on
Commit
d8d4f9e
·
verified ·
1 Parent(s): b4c693d

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +156 -51
templates/index.html CHANGED
@@ -13,12 +13,10 @@
13
  }
14
 
15
  html {
16
- /* Permet un défilement plus doux */
17
  scroll-behavior: smooth;
18
  }
19
 
20
  body {
21
- /* Police système : performante et native */
22
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
23
  background-color: #121212; /* Fond sombre principal */
24
  color: #f1f1f1; /* Couleur de texte par défaut */
@@ -158,6 +156,8 @@
158
  align-self: flex-start;
159
  border-bottom-left-radius: 4px;
160
  }
 
 
161
 
162
  .thoughts {
163
  background: #252525;
@@ -314,7 +314,6 @@
314
  border: 1px solid #333;
315
  }
316
  }
317
-
318
  </style>
319
  </head>
320
  <body>
@@ -337,9 +336,16 @@
337
  </div>
338
 
339
  <div class="messages" id="messages">
340
- <div class="message assistant">
341
- <div>👋 Bonjour ! Ceci est une interface de chat au design plat et minimaliste.</div>
342
- <div style="margin-top: 10px;">Posez-moi une question pour commencer.</div>
 
 
 
 
 
 
 
343
  </div>
344
  </div>
345
 
@@ -376,14 +382,14 @@
376
  </div>
377
  </div>
378
 
379
- <!-- Le JavaScript est identique à votre version originale -->
380
  <script>
381
  let currentFile = null;
382
  let conversationId = 'session_' + Date.now();
383
 
384
  function autoResize(textarea) {
385
  textarea.style.height = 'auto';
386
- textarea.style.height = Math.min(textarea.scrollHeight, 150) + 'px'; // Augmentation de la hauteur max
387
  }
388
 
389
  function handleKeyDown(event) {
@@ -401,20 +407,28 @@
401
  const formData = new FormData();
402
  formData.append('file', file);
403
 
404
- // Simule l'upload pour l'exemple
405
- // Remplacez cette partie par votre logique d'upload réelle si nécessaire
406
- try {
407
- currentFile = { success: true, filename: file.name, path: 'simulated/path/' + file.name };
408
- filePreview.innerHTML = `
409
- <div style="display: flex; justify-content: space-between; align-items: center;">
410
- <span>📎 ${currentFile.filename}</span>
411
- <button onclick="removeFile()" style="background: #555; color: white; border: none; padding: 5px 10px; border-radius: 5px; cursor: pointer;">✕</button>
412
- </div>
413
- `;
414
- filePreview.style.display = 'block';
415
- } catch (error) {
416
- showError('Erreur lors de la sélection du fichier: ' + error.message);
417
- }
 
 
 
 
 
 
 
 
418
  }
419
 
420
  function removeFile() {
@@ -445,28 +459,91 @@
445
  autoResize(messageInput);
446
 
447
  const thinkingEnabled = document.getElementById('thinkingToggle').checked;
448
-
449
- // --- SIMULATION DE RÉPONSE DE L'IA ---
450
- // Remplacez cette simulation par votre appel fetch réel
451
- setTimeout(() => {
452
- typingIndicator.style.display = 'none';
453
-
454
- if (thinkingEnabled) {
455
- const thoughtsElement = addThoughts('');
456
- thoughtsElement.innerHTML = `
457
- <div class="thoughts-header">🧠 Réflexion de l'assistant:</div>
458
- <div>L'utilisateur a demandé une réponse. Je vais formater ma réponse en utilisant du markdown simple. Le fichier ${currentFile ? `"${currentFile.filename}"` : ''} a été mentionné. Je vais construire une réponse cohérente.</div>
459
- `;
 
 
 
 
 
 
 
460
  }
461
 
462
- addMessage('assistant', `Ceci est une réponse simulée à votre message : "${message}". ${currentFile ? `Vous avez également joint le fichier : **${currentFile.filename}**.` : ''}`);
463
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
464
  messageInput.disabled = false;
465
  sendButton.disabled = false;
 
466
  messageInput.focus();
467
- removeFile();
468
- }, 2000);
469
- // --- FIN DE LA SIMULATION ---
470
  }
471
 
472
  function addMessage(role, content) {
@@ -483,23 +560,37 @@
483
  const messagesContainer = document.getElementById('messages');
484
  const thoughtsDiv = document.createElement('div');
485
  thoughtsDiv.className = 'thoughts';
486
- thoughtsDiv.innerHTML = content; // Le contenu est déjà du HTML
487
  messagesContainer.appendChild(thoughtsDiv);
488
  messagesContainer.scrollTop = messagesContainer.scrollHeight;
489
  return thoughtsDiv;
490
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
491
 
492
  function formatMessage(content) {
493
- // Sécurise le contenu pour éviter l'injection de HTML
494
- const escapedContent = content.replace(/</g, "&lt;").replace(/>/g, "&gt;");
495
-
496
- return escapedContent
497
  .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
498
  .replace(/\*(.*?)\*/g, '<em>$1</em>')
499
  .replace(/`(.*?)`/g, '<code style="background: #444; padding: 2px 5px; border-radius: 4px; font-family: monospace;">$1</code>')
500
  .replace(/\n/g, '<br>');
501
  }
502
 
 
503
  function showError(message) {
504
  const messagesContainer = document.getElementById('messages');
505
  const errorDiv = document.createElement('div');
@@ -511,13 +602,27 @@
511
 
512
  function resetConversation() {
513
  if (confirm('Êtes-vous sûr de vouloir réinitialiser la conversation ?')) {
514
- document.getElementById('messages').innerHTML = `
515
- <div class="message assistant">
516
- <div>👋 Conversation réinitialisée ! Comment puis-je vous aider ?</div>
517
- </div>
518
- `;
519
- conversationId = 'session_' + Date.now();
520
- removeFile();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
521
  }
522
  }
523
 
 
13
  }
14
 
15
  html {
 
16
  scroll-behavior: smooth;
17
  }
18
 
19
  body {
 
20
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
21
  background-color: #121212; /* Fond sombre principal */
22
  color: #f1f1f1; /* Couleur de texte par défaut */
 
156
  align-self: flex-start;
157
  border-bottom-left-radius: 4px;
158
  }
159
+
160
+ .message ul { margin-top: 10px; padding-left: 20px; }
161
 
162
  .thoughts {
163
  background: #252525;
 
314
  border: 1px solid #333;
315
  }
316
  }
 
317
  </style>
318
  </head>
319
  <body>
 
336
  </div>
337
 
338
  <div class="messages" id="messages">
339
+ <div class="message assistant">
340
+ <div>👋 Bonjour ! Je suis votre assistant IA Gemini. Je peux :</div>
341
+ <ul>
342
+ <li>💻 Exécuter du code Python</li>
343
+ <li>🔍 Rechercher des informations sur Google</li>
344
+ <li>📄 Analyser vos fichiers (images, PDF, vidéos, texte)</li>
345
+ <li>🧠 Utiliser le mode "thinking" pour des raisonnements complexes</li>
346
+ <li>🌐 Accéder au contenu d'URLs</li>
347
+ </ul>
348
+ <div style="margin-top: 10px;">Comment puis-je vous aider aujourd'hui ?</div>
349
  </div>
350
  </div>
351
 
 
382
  </div>
383
  </div>
384
 
385
+ <!-- SCRIPT ORIGINAL ET FONCTIONNEL -->
386
  <script>
387
  let currentFile = null;
388
  let conversationId = 'session_' + Date.now();
389
 
390
  function autoResize(textarea) {
391
  textarea.style.height = 'auto';
392
+ textarea.style.height = Math.min(textarea.scrollHeight, 150) + 'px';
393
  }
394
 
395
  function handleKeyDown(event) {
 
407
  const formData = new FormData();
408
  formData.append('file', file);
409
 
410
+ fetch('/upload', {
411
+ method: 'POST',
412
+ body: formData
413
+ })
414
+ .then(response => response.json())
415
+ .then(data => {
416
+ if (data.success) {
417
+ currentFile = data;
418
+ filePreview.innerHTML = `
419
+ <div style="display: flex; justify-content: space-between; align-items: center;">
420
+ <span>📎 ${data.filename}</span>
421
+ <button onclick="removeFile()" style="background: #555; color: white; border: none; padding: 5px 10px; border-radius: 5px; cursor: pointer;">✕</button>
422
+ </div>
423
+ `;
424
+ filePreview.style.display = 'block';
425
+ } else {
426
+ showError('Erreur lors du téléchargement: ' + data.error);
427
+ }
428
+ })
429
+ .catch(error => {
430
+ showError('Erreur lors du téléchargement: ' + error.message);
431
+ });
432
  }
433
 
434
  function removeFile() {
 
459
  autoResize(messageInput);
460
 
461
  const thinkingEnabled = document.getElementById('thinkingToggle').checked;
462
+ const endpoint = currentFile ? '/chat_with_file' : '/chat';
463
+ const payload = {
464
+ message: message || 'Analyse ce fichier',
465
+ thinking_enabled: thinkingEnabled,
466
+ conversation_id: conversationId
467
+ };
468
+
469
+ if (currentFile) {
470
+ payload.file_data = currentFile;
471
+ }
472
+
473
+ fetch(endpoint, {
474
+ method: 'POST',
475
+ headers: { 'Content-Type': 'application/json' },
476
+ body: JSON.stringify(payload)
477
+ })
478
+ .then(response => {
479
+ if (!response.ok) {
480
+ throw new Error(`Erreur réseau: ${response.status} ${response.statusText}`);
481
  }
482
 
483
+ const reader = response.body.getReader();
484
+ const decoder = new TextDecoder();
485
+ let buffer = '';
486
+ let messageElement = null;
487
+ let thoughtsElement = null;
488
+
489
+ function processStream() {
490
+ return reader.read().then(({ done, value }) => {
491
+ if (done) {
492
+ // La fin du stream est gérée par le message 'end'
493
+ return;
494
+ }
495
+
496
+ buffer += decoder.decode(value, { stream: true });
497
+ const lines = buffer.split('\n');
498
+ buffer = lines.pop() || '';
499
+
500
+ for (const line of lines) {
501
+ if (line.startsWith('data: ')) {
502
+ try {
503
+ const data = JSON.parse(line.substring(6));
504
+
505
+ if (data.type === 'text') {
506
+ if (!messageElement) {
507
+ messageElement = addMessage('assistant', '');
508
+ }
509
+ messageElement.innerHTML += formatChunk(data.content);
510
+ } else if (data.type === 'thought' && thinkingEnabled) {
511
+ if (!thoughtsElement) {
512
+ thoughtsElement = addThoughts('');
513
+ thoughtsElement.innerHTML = `<div class="thoughts-header">🧠 Réflexion de l'assistant:</div><div class="thought-content"></div>`;
514
+ }
515
+ thoughtsElement.querySelector('.thought-content').innerHTML += formatChunk(data.content);
516
+ } else if (data.type === 'error') {
517
+ showError(data.content);
518
+ } else if (data.type === 'end') {
519
+ typingIndicator.style.display = 'none';
520
+ messageInput.disabled = false;
521
+ sendButton.disabled = false;
522
+ messageInput.focus();
523
+ removeFile();
524
+ return; // Fin du traitement du stream
525
+ }
526
+ // Scroll vers le bas à chaque nouveau chunk
527
+ const messagesContainer = document.getElementById('messages');
528
+ messagesContainer.scrollTop = messagesContainer.scrollHeight;
529
+
530
+ } catch (e) {
531
+ console.error('Erreur parsing JSON:', e, "Ligne reçue:", line);
532
+ }
533
+ }
534
+ }
535
+ return processStream();
536
+ });
537
+ }
538
+ return processStream();
539
+ })
540
+ .catch(error => {
541
+ typingIndicator.style.display = 'none';
542
  messageInput.disabled = false;
543
  sendButton.disabled = false;
544
+ showError('Erreur: ' + error.message);
545
  messageInput.focus();
546
+ });
 
 
547
  }
548
 
549
  function addMessage(role, content) {
 
560
  const messagesContainer = document.getElementById('messages');
561
  const thoughtsDiv = document.createElement('div');
562
  thoughtsDiv.className = 'thoughts';
563
+ thoughtsDiv.innerHTML = content;
564
  messagesContainer.appendChild(thoughtsDiv);
565
  messagesContainer.scrollTop = messagesContainer.scrollHeight;
566
  return thoughtsDiv;
567
  }
568
+
569
+ function escapeHtml(unsafe) {
570
+ return unsafe
571
+ .replace(/&/g, "&amp;")
572
+ .replace(/</g, "&lt;")
573
+ .replace(/>/g, "&gt;")
574
+ .replace(/"/g, "&quot;")
575
+ .replace(/'/g, "&#039;");
576
+ }
577
+
578
+ function formatChunk(chunk) {
579
+ // Pour le streaming, on échappe le HTML et on remplace juste les sauts de ligne
580
+ return escapeHtml(chunk).replace(/\n/g, '<br>');
581
+ }
582
 
583
  function formatMessage(content) {
584
+ // Cette fonction est utilisée pour le message initial de l'utilisateur
585
+ // Elle peut gérer plus de markdown car elle traite le message en une seule fois
586
+ return escapeHtml(content)
 
587
  .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
588
  .replace(/\*(.*?)\*/g, '<em>$1</em>')
589
  .replace(/`(.*?)`/g, '<code style="background: #444; padding: 2px 5px; border-radius: 4px; font-family: monospace;">$1</code>')
590
  .replace(/\n/g, '<br>');
591
  }
592
 
593
+
594
  function showError(message) {
595
  const messagesContainer = document.getElementById('messages');
596
  const errorDiv = document.createElement('div');
 
602
 
603
  function resetConversation() {
604
  if (confirm('Êtes-vous sûr de vouloir réinitialiser la conversation ?')) {
605
+ fetch('/reset_conversation', {
606
+ method: 'POST',
607
+ headers: { 'Content-Type': 'application/json' },
608
+ body: JSON.stringify({ conversation_id: conversationId })
609
+ })
610
+ .then(response => {
611
+ if (response.ok) {
612
+ document.getElementById('messages').innerHTML = `
613
+ <div class="message assistant">
614
+ <div>👋 Conversation réinitialisée ! Comment puis-je vous aider ?</div>
615
+ </div>
616
+ `;
617
+ conversationId = 'session_' + Date.now();
618
+ removeFile();
619
+ } else {
620
+ showError('La réinitialisation a échoué.');
621
+ }
622
+ })
623
+ .catch(error => {
624
+ showError('Erreur lors de la réinitialisation: ' + error.message);
625
+ });
626
  }
627
  }
628