File size: 8,517 Bytes
040db21 37619a6 040db21 23f07ff b728b86 d510348 fe49da4 d510348 edad585 d510348 edad585 d510348 a5874c3 d510348 b728b86 edad585 d510348 b728b86 d510348 b728b86 d510348 b728b86 fe49da4 d510348 b728b86 d510348 b728b86 23f07ff d510348 b728b86 fe49da4 d510348 fe49da4 d510348 23f07ff d510348 b728b86 d510348 68ce09b d510348 b728b86 a5874c3 040db21 0d2d16d |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mariam AI!</title>
<!-- Tailwind CSS via CDN -->
<script src="https://cdn.tailwindcss.com"></script>
<style>
/* Style personnalisé pour la barre de défilement (optionnel) */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 10px;
}
::-webkit-scrollbar-thumb {
background: #888;
border-radius: 10px;
}
::-webkit-scrollbar-thumb:hover {
background: #555;
}
/* Empêche le FOUC (Flash of Unstyled Content) */
[x-cloak] { display: none !important; }
</style>
<!-- Alpine.js pour une interactivité simple (optionnel mais utile) -->
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js"></script>
</head>
<body class="bg-gray-100 flex items-center justify-center min-h-screen" x-data="{ processing: {{ 'true' if processing_message else 'false' }} }">
<div class="chat-container bg-white rounded-lg shadow-xl w-full max-w-3xl h-[90vh] flex flex-col" x-cloak>
<!-- En-tête -->
<div class="chat-header bg-gradient-to-r from-emerald-500 to-teal-600 text-white p-4 rounded-t-lg flex justify-between items-center">
<h1 class="text-xl font-semibold">Mariam AI!</h1>
<!-- Bouton pour effacer (optionnel mais utile) -->
<form action="{{ url_for('clear_chat') }}" method="post" onsubmit="return confirm('Voulez-vous vraiment effacer la conversation ?');">
<button type="submit" title="Effacer la conversation" class="text-white hover:text-red-300 transition duration-150">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
<path stroke-linecap="round" stroke-linejoin="round" d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12.576 0a48.108 48.108 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" />
</svg>
</button>
</form>
</div>
<!-- Zone des messages -->
<div class="chat-messages flex-grow overflow-y-auto p-4 md:p-6 space-y-4" id="chat-messages">
{% for message in chat_history %}
<div class="message flex {% if message.role == 'user' %}justify-end{% else %}justify-start{% endif %}">
<div class="max-w-xs md:max-w-md lg:max-w-lg px-4 py-2 rounded-lg shadow {% if message.role == 'user' %}bg-emerald-500 text-white rounded-br-none{% else %}bg-gray-200 text-gray-800 rounded-bl-none{% endif %}">
<p class="text-sm break-words whitespace-pre-wrap">{{ message.text }}</p>
</div>
</div>
{% endfor %}
<!-- Indicateur de chargement -->
<div x-show="processing" class="flex justify-center items-center py-4">
<div class="animate-spin rounded-full h-6 w-6 border-b-2 border-emerald-500"></div>
<span class="ml-2 text-gray-500 italic">
{% if session.get('processing_web_search') %}
Recherche web et génération en cours...
{% else %}
Génération de la réponse...
{% endif %}
</span>
</div>
</div>
<!-- Affichage des erreurs -->
{% if error %}
<div class="error-message bg-red-100 border border-red-400 text-red-700 px-4 py-2 rounded-md mx-4 mb-2 text-sm">
<strong>Erreur :</strong> {{ error }}
</div>
{% endif %}
<!-- Zone de saisie et options -->
<div class="chat-input-area border-t border-gray-200 p-4 bg-gray-50 rounded-b-lg">
<form method="POST" action="{{ url_for('chat') }}" enctype="multipart/form-data" id="chat-form" @submit="processing = true">
<!-- Options : Recherche Web & Upload -->
<div class="options flex flex-col sm:flex-row justify-between items-center mb-3 gap-3 text-sm text-gray-600">
<label for="web_search_toggle" class="flex items-center cursor-pointer">
<input type="checkbox" id="web_search_toggle" name="web_search" value="true" {% if web_search_active %}checked{% endif %} class="mr-2 h-4 w-4 rounded border-gray-300 text-emerald-600 focus:ring-emerald-500">
Activer la recherche web
</label>
<label for="file_upload" class="flex items-center cursor-pointer bg-white border border-gray-300 rounded-md px-3 py-1 hover:bg-gray-50 transition duration-150">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4 mr-2 text-gray-500">
<path stroke-linecap="round" stroke-linejoin="round" d="m18.375 12.739-7.693 7.693a4.5 4.5 0 0 1-6.364-6.364l10.94-10.94A3 3 0 1 1 19.5 7.372L8.552 18.32m.009-.01-.01.01m5.699-9.941-7.81 7.81a1.5 1.5 0 0 0 2.122 2.122l7.81-7.81" />
</svg>
<span id="file-label-text">Joindre un fichier</span> (.jpg, .png, .pdf, .txt)
<input type="file" id="file_upload" name="file" class="hidden" accept=".jpg,.jpeg,.png,.pdf,.txt" onchange="updateFileLabel(this)">
</label>
</div>
<!-- Champ de saisie et bouton envoyer -->
<div class="flex items-center gap-2">
<input type="text" name="prompt" id="prompt-input" placeholder="Posez votre question à Mariam..." required autofocus
class="flex-grow p-3 border border-gray-300 rounded-full focus:outline-none focus:ring-2 focus:ring-emerald-500 focus:border-transparent transition duration-150"
:disabled="processing"> <!-- Désactivé pendant le traitement -->
<button type="submit" id="send-button"
class="bg-emerald-500 hover:bg-emerald-600 text-white rounded-full p-3 focus:outline-none focus:ring-2 focus:ring-emerald-500 focus:ring-offset-1 transition duration-150 disabled:opacity-50 disabled:cursor-not-allowed"
:disabled="processing"> <!-- Désactivé pendant le traitement -->
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 12 3.269 3.125A59.769 59.769 0 0 1 21.485 12 59.768 59.768 0 0 1 3.27 20.875L5.999 12Zm0 0h7.5" />
</svg>
</button>
</div>
</form>
</div>
</div>
<script>
// Fonction pour faire défiler vers le bas
function scrollToBottom() {
const chatMessages = document.getElementById('chat-messages');
chatMessages.scrollTop = chatMessages.scrollHeight;
}
// Fonction pour mettre à jour le label du fichier
function updateFileLabel(input) {
const fileLabel = document.getElementById('file-label-text');
if (input.files && input.files.length > 0) {
fileLabel.textContent = input.files[0].name;
} else {
fileLabel.textContent = 'Joindre un fichier';
}
}
// Faire défiler vers le bas au chargement initial
window.addEventListener('load', scrollToBottom);
// Optionnel : Faire défiler après un léger délai pour s'assurer que tout est rendu (peut aider sur certains navigateurs)
setTimeout(scrollToBottom, 100);
// Optionnel : Si vous vouliez effacer le champ après envoi (ne fonctionne pas bien avec le rechargement de page standard)
/*
const chatForm = document.getElementById('chat-form');
const promptInput = document.getElementById('prompt-input');
chatForm.addEventListener('submit', function() {
// Déjà géré par Alpine.js : document.getElementById('send-button').disabled = true;
// Peut causer des problèmes si la soumission échoue avant le rechargement
// setTimeout(() => { promptInput.value = ''; }, 50);
});
*/
</script>
</body>
</html> |