Mariam-cc / templates /index.html
Docfile's picture
Update templates/index.html
d300041 verified
raw
history blame
11.6 kB
<!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 d'Image</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/9.1.6/marked.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/sweetalert2/11.7.32/sweetalert2.all.min.js"></script>
<style>
.loader {
width: 48px;
height: 48px;
border: 5px solid #FFF;
border-bottom-color: #6366F1;
border-radius: 50%;
display: inline-block;
box-sizing: border-box;
animation: rotation 1s linear infinite;
}
@keyframes rotation {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.fade-in {
animation: fadeIn 0.5s ease-in;
}
@keyframes fadeIn {
0% { opacity: 0; }
100% { opacity: 1; }
}
.upload-zone {
border: 2px dashed #6366F1;
transition: all 0.3s ease;
}
.upload-zone:hover {
border-color: #4F46E5;
background-color: #EEF2FF;
}
/* Styles améliorés pour le tableau avec scroll horizontal */
.markdown-content {
overflow-x: auto;
padding-bottom: 1rem; /* Espace pour la scrollbar */
}
.markdown-content table {
border-collapse: collapse;
min-width: 100%;
width: max-content; /* Permet au tableau de dépasser si nécessaire */
margin: 1rem 0;
}
.markdown-content th,
.markdown-content td {
border: 1px solid #e5e7eb;
padding: 0.75rem;
text-align: left;
white-space: nowrap; /* Empêche le retour à la ligne */
min-width: 150px; /* Largeur minimum pour chaque cellule */
}
.markdown-content th {
background-color: #f9fafb;
position: sticky;
top: 0;
z-index: 10;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
/* Style pour la scrollbar horizontale */
.markdown-content::-webkit-scrollbar {
height: 8px;
}
.markdown-content::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 4px;
}
.markdown-content::-webkit-scrollbar-thumb {
background: #6366F1;
border-radius: 4px;
}
.markdown-content::-webkit-scrollbar-thumb:hover {
background: #4F46E5;
}
/* Styles pour SweetAlert2 */
.swal2-popup {
width: 90% !important;
max-width: 1200px !important;
}
.swal2-html-container {
max-height: 80vh !important;
overflow-y: auto !important;
}
/* Indicateur de défilement */
.scroll-indicator {
position: absolute;
bottom: 20px;
right: 20px;
background: rgba(99, 102, 241, 0.9);
color: white;
padding: 8px 12px;
border-radius: 4px;
font-size: 0.875rem;
opacity: 0;
transition: opacity 0.3s ease;
pointer-events: none;
}
.scroll-indicator.visible {
opacity: 1;
}
</style>
</head>
<body class="bg-gray-50">
<!-- [Le reste du HTML reste identique jusqu'au script] -->
<div class="min-h-screen">
<!-- Header -->
<header class="bg-white shadow-sm">
<div class="max-w-7xl mx-auto px-4 py-4 sm:px-6 lg:px-8">
<h1 class="text-3xl font-bold text-indigo-600">Mariam AI</h1>
<p class="mt-1 text-sm text-gray-500">Assistant pour commentaire composé.</p>
</div>
</header>
<!-- Main content -->
<main class="max-w-7xl mx-auto px-4 py-8 sm:px-6 lg:px-8">
<!-- Upload Section -->
<div class="bg-white rounded-lg shadow p-6 mb-8">
<form id="uploadForm" class="space-y-6">
<div class="upload-zone rounded-lg p-8 text-center">
<input type="file" id="imageInput" accept="image/*" required
class="hidden" onchange="updateFileName()">
<label for="imageInput" class="cursor-pointer">
<div class="space-y-2">
<svg class="mx-auto h-12 w-12 text-indigo-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"/>
</svg>
<div id="fileName" class="text-sm text-gray-500">
Cliquez ou glissez une image ici
</div>
</div>
</label>
</div>
<div class="flex justify-center">
<button type="submit"
class="inline-flex items-center px-6 py-3 border border-transparent text-base font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 transition-colors duration-200">
Analyser l'image
</button>
</div>
</form>
</div>
<!-- Loading indicator -->
<div id="loading" class="hidden">
<div class="flex flex-col items-center justify-center space-y-4">
<span class="loader"></span>
<p class="text-gray-500">Analyse en cours, veuillez patienter...</p>
</div>
</div>
<!-- Results Section -->
<div id="results" class="space-y-8 hidden">
<!-- Tableau Button -->
<div class="flex justify-center">
<button id="showTableau"
class="px-6 py-3 bg-indigo-600 text-white rounded-md hover:bg-indigo-700 transition-colors duration-200">
Voir le tableau d'analyse
</button>
</div>
<!-- Dissertation -->
<div class="bg-white rounded-lg shadow p-6">
<h2 class="text-2xl font-bold text-gray-900 mb-4">Dissertation</h2>
<div id="dissertationResult" class="prose max-w-none markdown-content">
</div>
</div>
</div>
</main>
</div>
<script>
let tableauContent = '';
function updateFileName() {
const input = document.getElementById('imageInput');
const fileName = document.getElementById('fileName');
if (input.files.length > 0) {
fileName.textContent = input.files[0].name;
} else {
fileName.textContent = 'Cliquez ou glissez une image ici';
}
}
function showScrollIndicator(container) {
if (container.scrollWidth > container.clientWidth) {
const indicator = document.createElement('div');
indicator.className = 'scroll-indicator visible';
indicator.textContent = '← Faites défiler →';
container.parentElement.appendChild(indicator);
setTimeout(() => {
indicator.classList.remove('visible');
}, 3000);
container.addEventListener('scroll', () => {
if (container.scrollLeft > 0) {
indicator.classList.add('visible');
} else {
indicator.classList.remove('visible');
}
// Cache l'indicateur après 1 seconde de scroll
clearTimeout(container.scrollTimeout);
container.scrollTimeout = setTimeout(() => {
indicator.classList.remove('visible');
}, 1000);
});
}
}
document.getElementById('showTableau').addEventListener('click', () => {
Swal.fire({
title: 'Tableau d\'analyse',
html: marked.parse(tableauContent),
width: '90%',
customClass: {
htmlContainer: 'markdown-content'
},
didRender: () => {
const container = document.querySelector('.markdown-content');
showScrollIndicator(container);
// Ajout des ombres de défilement
container.addEventListener('scroll', () => {
const maxScroll = container.scrollWidth - container.clientWidth;
container.style.boxShadow = container.scrollLeft > 0 ?
'inset 10px 0 5px -5px rgba(0,0,0,0.1)' : '';
container.style.boxShadow += container.scrollLeft < maxScroll ?
', inset -10px 0 5px -5px rgba(0,0,0,0.1)' : '';
});
}
});
});
document.getElementById('uploadForm').addEventListener('submit', async (e) => {
e.preventDefault();
const loading = document.getElementById('loading');
const results = document.getElementById('results');
const dissertationResult = document.getElementById('dissertationResult');
const formData = new FormData();
formData.append('image', document.getElementById('imageInput').files[0]);
loading.classList.remove('hidden');
results.classList.add('hidden');
try {
const response = await fetch('/analyze', {
method: 'POST',
body: formData
});
const data = await response.json();
if (response.ok) {
tableauContent = data.tableau;
dissertationResult.innerHTML = marked.parse(data.dissertation);
results.classList.remove('hidden');
results.classList.add('fade-in');
} else {
Swal.fire({
icon: 'error',
title: 'Erreur',
text: data.error
});
}
} catch (error) {
Swal.fire({
icon: 'error',
title: 'Erreur',
text: 'Une erreur est survenue lors de l\'analyse'
});
} finally {
loading.classList.add('hidden');
}
});
</script>
</body>
</html>