Spaces:
Running
Running
<html lang="fr"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Mariam AI - Assistant Philosophique</title> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script> | |
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/select2.min.css" rel="stylesheet" /> | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/select2.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/sweetalert2/11.7.3/sweetalert2.all.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/4.3.0/marked.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/locale/fr.js"></script> | |
<link href="https://cdnjs.cloudflare.com/ajax/libs/sweetalert2/11.7.3/sweetalert2.min.css" rel="stylesheet"> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<style> | |
/* ... (tous les styles de votre fichier original restent ici) ... */ | |
.collapsible { cursor: pointer; padding: 18px; width: 100%; border: none; text-align: left; outline: none; font-size: 15px; background-color: #f1f1f1; display: flex; justify-content: space-between; align-items: center; } | |
.prose { max-width: 100% ; } | |
.prose p { margin-top: 1.25em; margin-bottom: 1.25em; line-height: 1.75; } | |
.prose ul { margin-top: 1.25em; margin-bottom: 1.25em; padding-left: 1.625em; } | |
#response .prose { color: #374151; word-wrap: break-word; overflow-wrap: break-word; hyphens: auto; } | |
.content { padding: 0 18px; display: none; overflow: hidden; background-color: white; } | |
.animate-fadeIn { animation: fadeIn 0.5s ease-out forwards; } | |
@keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } | |
.select2-container--default .select2-selection--single { border: 1px solid #e5e7eb; border-radius: 0.75rem; height: auto; padding: 0.625rem 1rem; background-color: white; box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); display: flex; align-items: center; } | |
.select2-container--default .select2-selection--single .select2-selection__rendered { color: #374151; line-height: inherit; padding-right: 1.5rem; } | |
#image-preview { max-height: 200px; border-radius: 0.75rem; box-shadow: 0 4px 6px -1px rgba(0,0,0,.1), 0 2px 4px -2px rgba(0,0,0,.1); } | |
</style> | |
</head> | |
<body class="bg-gradient-to-br from-violet-50 to-indigo-50 min-h-screen"> | |
<!-- Navbar --> | |
<nav class="bg-white/80 backdrop-blur-md border-b border-gray-200 fixed w-full z-50"> | |
<!-- ... (contenu de la navbar inchangé) ... --> | |
</nav> | |
<!-- Main Content --> | |
<div class="pt-24 pb-12 px-4 sm:px-6 lg:px-8"> | |
<div class="max-w-4xl mx-auto"> | |
<div class="bg-white/80 backdrop-blur-md rounded-2xl shadow-xl border border-gray-100 overflow-hidden"> | |
<div class="bg-gradient-to-r from-violet-600 to-indigo-600 p-6 text-white"> | |
<h2 class="text-2xl font-bold">Gen'Dissertation</h2> | |
<p class="mt-2 opacity-90">Créez des dissertations et analyses philosophiques pertinentes et structurées</p> | |
</div> | |
<div class="p-8 space-y-8"> | |
<!-- Type Selection --> | |
<div class="space-y-3"> | |
<label class="block text-sm font-medium text-gray-700">Type de travail</label> | |
<select id="type-select" class="w-full rounded-xl border-gray-200 shadow-sm focus:border-violet-500 focus:ring-violet-500 appearance-none bg-white py-3 px-4 pr-10"> | |
<option value="1">Sujet Type 1</option> | |
<option value="2">Sujet Type 2 (Citation)</option> | |
<option value="3">Analyse d'image</option> | |
</select> | |
</div> | |
<!-- Conteneur pour les champs de texte --> | |
<div id="text-input-container"> | |
<!-- Course Selection --> | |
<div class="space-y-3"> | |
<label class="block text-sm font-medium text-gray-700">Appuyer l'analyse sur un cours (Optionnel)</label> | |
<select id="course-select" class="w-full"> | |
<option value="">Ne pas utiliser de cours</option> | |
</select> | |
</div> | |
<!-- Question Input --> | |
<div class="space-y-3 mt-8"> | |
<label for="question" class="block text-sm font-medium text-gray-700">Sujet de dissertation</label> | |
<textarea id="question" rows="4" class="w-full rounded-xl border-gray-200 shadow-sm focus:border-violet-500 focus:ring-violet-500 resize-none bg-white py-3 px-4" placeholder="Saisissez votre sujet de dissertation..."></textarea> | |
</div> | |
</div> | |
<!-- Conteneur pour l'upload d'image --> | |
<div id="image-input-container" class="hidden"> | |
<div class="space-y-3"> | |
<label for="image-upload" class="block text-sm font-medium text-gray-700">Charger une image pour analyse</label> | |
<input type="file" id="image-upload" accept="image/jpeg, image/png, image/webp" class="block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-violet-50 file:text-violet-700 hover:file:bg-violet-100"/> | |
<div class="mt-4 flex justify-center"> | |
<img id="image-preview" src="" alt="Aperçu de l'image" class="hidden"/> | |
</div> | |
</div> | |
</div> | |
<!-- Submit Button --> | |
<button id="submit-btn" class="w-full py-4 px-6 rounded-xl bg-gradient-to-r from-violet-600 to-indigo-600 text-white font-medium shadow-lg shadow-violet-200 hover:shadow-xl hover:shadow-violet-300 transform hover:-translate-y-0.5 transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-violet-500 focus:ring-offset-2"> | |
Générer | |
</button> | |
<!-- Response Section --> | |
<div id="response" class="hidden mt-8 prose prose-violet max-w-none"> | |
<div class="bg-gradient-to-r from-gray-50 to-white rounded-xl p-6 border border-gray-100"> | |
<!-- La réponse en streaming sera insérée ici --> | |
</div> | |
</div> | |
<!-- Copy Button --> | |
<button id="copy-btn" class="hidden w-full py-3 ...">Copier</button> | |
<!-- Saved Dissertations Section --> | |
<div id="saved-dissertations" class="mt-8"> | |
<!-- ... (section inchangée) ... --> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Bouton flottant DeepThink --> | |
<button id="deepthink-btn" class="fixed bottom-6 right-6 z-50 bg-indigo-600 text-white px-4 py-2 rounded-full shadow-lg hover:bg-indigo-700 focus:outline-none"> | |
DeepThink | |
</button> | |
<script> | |
$(document).ready(function() { | |
// --- Initialisations --- | |
$('#course-select').select2({ /* ... options select2 ... */ }); | |
marked.setOptions({ breaks: true, gfm: true }); | |
moment.locale('fr'); | |
const Toast = Swal.mixin({ toast: true, position: 'top-end', showConfirmButton: false, timer: 3000, timerProgressBar: true }); | |
// --- Gestion de l'interface --- | |
$('#type-select').change(function() { | |
const type = $(this).val(); | |
if (type === '3') { // Analyse d'image | |
$('#text-input-container').hide(); | |
$('#image-input-container').show(); | |
$('#deepthink-btn').hide(); | |
$('#submit-btn span').text("Analyser l'image"); | |
} else { // Dissertation texte | |
$('#text-input-container').show(); | |
$('#image-input-container').hide(); | |
$('#deepthink-btn').show(); | |
$('#submit-btn span').text("Générer la dissertation"); | |
} | |
}).trigger('change'); | |
$('#image-upload').change(function(e) { | |
if (e.target.files && e.target.files[0]) { | |
const reader = new FileReader(); | |
reader.onload = function(event) { | |
$('#image-preview').attr('src', event.target.result).removeClass('hidden'); | |
} | |
reader.readAsDataURL(e.target.files[0]); | |
} | |
}); | |
// --- Chargement des cours (inchangé) --- | |
// ... | |
// --- Logique de Génération en Streaming --- | |
async function handleStreamedGeneration(url, options) { | |
Swal.fire({ title: 'Génération en cours...', html: 'Veuillez patienter...', allowOutsideClick: false, showConfirmButton: false, didOpen: () => { Swal.showLoading() }}); | |
const responseDiv = $('#response > div'); | |
responseDiv.html(''); | |
$('#response').removeClass('hidden'); | |
$('#copy-btn').addClass('hidden'); | |
let fullResponseText = ''; | |
try { | |
const response = await fetch(url, options); | |
if (!response.ok) { | |
const errorText = await response.text(); | |
throw new Error(errorText || `Erreur HTTP: ${response.status}`); | |
} | |
const reader = response.body.getReader(); | |
const decoder = new TextDecoder(); | |
while (true) { | |
const { value, done } = await reader.read(); | |
if (done) break; | |
const chunk = decoder.decode(value, { stream: true }); | |
fullResponseText += chunk; | |
responseDiv.html(marked.parse(fullResponseText)); | |
window.scrollTo(0, document.body.scrollHeight); | |
} | |
Swal.close(); | |
$('#copy-btn').removeClass('hidden'); | |
Toast.fire({ icon: 'success', title: 'Génération terminée !' }); | |
saveDissertation($('#question').val().trim() || "Analyse d'image", fullResponseText); | |
} catch (error) { | |
console.error('Erreur de streaming:', error); | |
Swal.fire({ icon: 'error', title: 'Erreur', text: error.message }); | |
} | |
} | |
// --- Gestion des Clics --- | |
$('#submit-btn, #deepthink-btn').click(function() { | |
const type = $('#type-select').val(); | |
const isDeepThink = $(this).attr('id') === 'deepthink-btn'; | |
if (type === '3') { // Analyse d'image | |
const imageFile = $('#image-upload')[0].files[0]; | |
if (!imageFile) { | |
Swal.fire('Erreur', 'Veuillez sélectionner une image.', 'error'); | |
return; | |
} | |
const formData = new FormData(); | |
formData.append('image', imageFile); | |
handleStreamedGeneration('/stream_philo_image', { method: 'POST', body: formData }); | |
} else { // Dissertation texte | |
const question = $('#question').val().trim(); | |
if (!question) { | |
Swal.fire('Erreur', 'Veuillez saisir un sujet.', 'error'); | |
return; | |
} | |
const data = { | |
question: question, | |
type: type, | |
courseId: $('#course-select').val() || null | |
}; | |
const url = isDeepThink ? '/stream_philo_deepthink' : '/stream_philo'; | |
handleStreamedGeneration(url, { | |
method: 'POST', | |
headers: { 'Content-Type': 'application/json' }, | |
body: JSON.stringify(data) | |
}); | |
} | |
}); | |
// --- Fonctions de sauvegarde et copie (inchangées) --- | |
function saveDissertation(title, content) { /* ... */ } | |
function deleteDissertation(index) { /* ... */ } | |
function updateSavedDissertationsList() { /* ... */ } | |
$('#copy-btn').click(function() { /* ... */ }); | |
updateSavedDissertationsList(); | |
}); | |
</script> | |
</body> | |
</html> |