|
<!DOCTYPE html> |
|
<html lang="fr"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>Mariam AI - Analyse Littéraire</title> |
|
<script src="https://cdn.tailwindcss.com"></script> |
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/9.1.6/marked.min.js"></script> |
|
<style> |
|
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;700&family=Inter:wght@300;400;500;600;700&display=swap'); |
|
|
|
.custom-prose h1, .custom-prose h2, .custom-prose h3 { |
|
font-family: 'Playfair Display', serif; |
|
margin-top: 2em; |
|
margin-bottom: 1em; |
|
line-height: 1.3; |
|
} |
|
|
|
.custom-prose p { |
|
margin-bottom: 1.5em; |
|
line-height: 1.8; |
|
} |
|
|
|
.custom-prose ul, .custom-prose ol { |
|
margin-top: 1em; |
|
margin-bottom: 1em; |
|
padding-left: 1.5em; |
|
} |
|
|
|
.custom-prose li { |
|
margin-bottom: 0.5em; |
|
} |
|
|
|
.markdown-table table { |
|
width: 100%; |
|
border-collapse: separate; |
|
border-spacing: 0; |
|
margin: 2em 0; |
|
background: white; |
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); |
|
} |
|
|
|
.markdown-table th { |
|
background: #1a365d; |
|
color: white; |
|
font-weight: 600; |
|
padding: 1rem; |
|
text-align: left; |
|
} |
|
|
|
.markdown-table td { |
|
padding: 1rem; |
|
border-bottom: 1px solid #e2e8f0; |
|
} |
|
|
|
.markdown-table tr:hover { |
|
background: #f8fafc; |
|
} |
|
|
|
.spinner { |
|
border: 4px solid rgba(0, 0, 0, 0.1); |
|
width: 3rem; |
|
height: 3rem; |
|
border-radius: 50%; |
|
border-left-color: #1a365d; |
|
animation: spin 1s linear infinite; |
|
} |
|
|
|
@keyframes spin { |
|
to { transform: rotate(360deg); } |
|
} |
|
|
|
.fade-enter { |
|
opacity: 0; |
|
transform: translateY(20px); |
|
} |
|
|
|
.fade-enter-active { |
|
opacity: 1; |
|
transform: translateY(0); |
|
transition: opacity 300ms, transform 300ms; |
|
} |
|
</style> |
|
</head> |
|
<body class="bg-gradient-to-br from-gray-50 to-gray-100 min-h-screen font-['Inter']"> |
|
<div class="max-w-4xl mx-auto p-6"> |
|
|
|
<header class="text-center mb-12"> |
|
<h1 class="font-['Playfair_Display'] text-5xl font-bold text-gray-900 mb-4">Mariam AI</h1> |
|
<p class="text-xl text-gray-600">Assistant d'Analyse Littéraire</p> |
|
</header> |
|
|
|
<div class="bg-white rounded-xl shadow-lg p-8"> |
|
|
|
<form id="uploadForm" class="space-y-6"> |
|
<div class="relative"> |
|
<label for="imageInput" class="block text-lg font-medium text-gray-700 mb-3"> |
|
Sélectionnez votre document |
|
</label> |
|
<div class="mt-2 flex justify-center px-6 pt-5 pb-6 border-2 border-gray-300 border-dashed rounded-lg hover:border-blue-500 transition-colors cursor-pointer"> |
|
<div class="space-y-2 text-center"> |
|
<svg class="mx-auto h-12 w-12 text-gray-400" stroke="currentColor" fill="none" viewBox="0 0 48 48" aria-hidden="true"> |
|
<path d="M28 8H12a4 4 0 00-4 4v20m32-12v8m0 0v8a4 4 0 01-4 4H12a4 4 0 01-4-4v-4m32-4l-3.172-3.172a4 4 0 00-5.656 0L28 28M8 32l9.172-9.172a4 4 0 015.656 0L28 28m0 0l4 4m4-24h8m-4-4v8m-12 4h.02" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /> |
|
</svg> |
|
<div class="text-sm text-gray-600"> |
|
<label for="imageInput" class="relative cursor-pointer bg-white rounded-md font-medium text-blue-600 hover:text-blue-500 focus-within:outline-none"> |
|
<span>Télécharger un fichier</span> |
|
<input id="imageInput" name="image" type="file" class="sr-only" accept="image/*" required> |
|
</label> |
|
</div> |
|
<p class="text-xs text-gray-500">PNG, JPG jusqu'à 10MB</p> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div class="relative"> |
|
<label for="consignesInput" class="block text-lg font-medium text-gray-700 mb-3"> |
|
Consignes (optionnel) |
|
</label> |
|
<input id="consignesInput" name="consignes" type="text" class="w-full border border-gray-300 rounded-md p-2" placeholder="Entrez vos consignes ici"> |
|
</div> |
|
|
|
|
|
<div id="previewContainer" class="hidden"> |
|
<p class="text-gray-700 font-medium mb-3">Aperçu du document :</p> |
|
<div class="relative rounded-lg overflow-hidden bg-gray-100"> |
|
<img id="previewImage" src="#" alt="Aperçu" class="w-full max-h-80 object-contain"> |
|
</div> |
|
</div> |
|
|
|
<button type="submit" class="w-full bg-blue-600 text-white rounded-lg px-4 py-3 font-semibold hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-colors"> |
|
Lancer l'analyse |
|
</button> |
|
</form> |
|
|
|
|
|
<div id="loading" class="hidden"> |
|
<div class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50"> |
|
<div class="bg-white p-6 rounded-lg shadow-xl text-center"> |
|
<div class="spinner mx-auto mb-4"></div> |
|
<p class="text-gray-700 font-medium">Analyse en cours...</p> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div id="result" class="hidden mt-8 space-y-8"> |
|
|
|
<div class="flex space-x-4 border-b border-gray-200"> |
|
<button id="dissertationTab" class="px-4 py-2 text-sm font-medium text-blue-600 border-b-2 border-blue-600"> |
|
Dissertation |
|
</button> |
|
<button id="tableauTab" class="px-4 py-2 text-sm font-medium text-gray-500 hover:text-gray-700"> |
|
Tableau d'analyse |
|
</button> |
|
</div> |
|
|
|
|
|
<div id="dissertationContent" class="custom-prose prose prose-lg max-w-none"> |
|
</div> |
|
|
|
|
|
<div id="tableauContent" class="hidden markdown-table prose prose-lg max-w-none"> |
|
<div class="overflow-x-auto"> |
|
|
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<script> |
|
const uploadForm = document.getElementById('uploadForm'); |
|
const imageInput = document.getElementById('imageInput'); |
|
const previewContainer = document.getElementById('previewContainer'); |
|
const previewImage = document.getElementById('previewImage'); |
|
const loadingIndicator = document.getElementById('loading'); |
|
const resultDiv = document.getElementById('result'); |
|
const dissertationTab = document.getElementById('dissertationTab'); |
|
const tableauTab = document.getElementById('tableauTab'); |
|
const dissertationContent = document.getElementById('dissertationContent'); |
|
const tableauContent = document.getElementById('tableauContent'); |
|
|
|
|
|
marked.setOptions({ |
|
breaks: true, |
|
gfm: true, |
|
headerIds: true, |
|
langPrefix: 'language-', |
|
}); |
|
|
|
|
|
imageInput.addEventListener('change', function() { |
|
const file = this.files[0]; |
|
if (file) { |
|
const reader = new FileReader(); |
|
reader.onload = function(e) { |
|
previewImage.src = e.target.result; |
|
previewContainer.classList.remove('hidden'); |
|
} |
|
reader.readAsDataURL(file); |
|
} else { |
|
previewContainer.classList.add('hidden'); |
|
} |
|
}); |
|
|
|
|
|
dissertationTab.addEventListener('click', function() { |
|
dissertationTab.classList.add('text-blue-600', 'border-b-2', 'border-blue-600'); |
|
tableauTab.classList.remove('text-blue-600', 'border-b-2', 'border-blue-600'); |
|
dissertationContent.classList.remove('hidden'); |
|
tableauContent.classList.add('hidden'); |
|
}); |
|
|
|
tableauTab.addEventListener('click', function() { |
|
tableauTab.classList.add('text-blue-600', 'border-b-2', 'border-blue-600'); |
|
dissertationTab.classList.remove('text-blue-600', 'border-b-2', 'border-blue-600'); |
|
tableauContent.classList.remove('hidden'); |
|
dissertationContent.classList.add('hidden'); |
|
}); |
|
|
|
|
|
uploadForm.addEventListener('submit', function(e) { |
|
e.preventDefault(); |
|
loadingIndicator.classList.remove('hidden'); |
|
resultDiv.classList.add('hidden'); |
|
|
|
const formData = new FormData(uploadForm); |
|
|
|
fetch('/analyze', { |
|
method: 'POST', |
|
body: formData |
|
}) |
|
.then(response => response.json()) |
|
.then(data => { |
|
loadingIndicator.classList.add('hidden'); |
|
if (data.error) { |
|
throw new Error(data.error); |
|
} |
|
|
|
dissertationContent.innerHTML = marked.parse(data.dissertation); |
|
|
|
tableauContent.querySelector('.overflow-x-auto').innerHTML = marked.parse(data.tableau); |
|
|
|
resultDiv.classList.remove('hidden'); |
|
|
|
dissertationTab.click(); |
|
|
|
|
|
resultDiv.scrollIntoView({ behavior: 'smooth' }); |
|
}) |
|
.catch(error => { |
|
loadingIndicator.classList.add('hidden'); |
|
alert("Une erreur est survenue : " + error.message); |
|
}); |
|
}); |
|
|
|
|
|
document.querySelectorAll('.fade-enter').forEach(element => { |
|
element.classList.add('fade-enter-active'); |
|
setTimeout(() => { |
|
element.classList.remove('fade-enter', 'fade-enter-active'); |
|
}, 300); |
|
}); |
|
</script> |
|
</body> |
|
</html> |
|
|