| <!DOCTYPE html> |
| <html lang="fr"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Exercice de Philosophie</title> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> |
| <script> |
| tailwind.config = { |
| theme: { |
| extend: { |
| colors: { |
| primary: '#4CAF50', |
| 'primary-dark': '#3e8e41', |
| } |
| } |
| } |
| } |
| </script> |
| <style> |
| .markdown-content { |
| width: 100%; |
| overflow-wrap: break-word; |
| word-wrap: break-word; |
| word-break: break-word; |
| hyphens: auto; |
| } |
| .markdown-content pre { |
| white-space: pre-wrap; |
| word-wrap: break-word; |
| padding: 1rem; |
| background-color: #f3f4f6; |
| border-radius: 0.5rem; |
| margin: 1rem 0; |
| overflow-x: auto; |
| } |
| .markdown-content code { |
| background-color: #f3f4f6; |
| padding: 0.2rem 0.4rem; |
| border-radius: 0.25rem; |
| font-family: ui-monospace, monospace; |
| } |
| |
| .markdown-content p { |
| margin-bottom: 1rem; |
| line-height: 1.7; |
| white-space: pre-line; |
| } |
| |
| .markdown-content h1 { |
| font-size: 1.8rem; |
| font-weight: bold; |
| margin-top: 1.5rem; |
| margin-bottom: 1rem; |
| padding-bottom: 0.5rem; |
| border-bottom: 1px solid #e2e8f0; |
| color: #2d3748; |
| } |
| .markdown-content h2 { |
| font-size: 1.5rem; |
| font-weight: bold; |
| margin-top: 1.25rem; |
| margin-bottom: 0.75rem; |
| color: #2d3748; |
| } |
| .markdown-content h3 { |
| font-size: 1.25rem; |
| font-weight: bold; |
| margin-top: 1rem; |
| margin-bottom: 0.5rem; |
| color: #2d3748; |
| } |
| |
| .markdown-content ul, .markdown-content ol { |
| margin-left: 1.5rem; |
| margin-bottom: 1rem; |
| padding-left: 1rem; |
| } |
| .markdown-content li { |
| margin-bottom: 0.5rem; |
| } |
| .markdown-content ul { |
| list-style-type: disc; |
| } |
| .markdown-content ol { |
| list-style-type: decimal; |
| } |
| |
| .markdown-content blockquote { |
| border-left: 4px solid #4CAF50; |
| padding: 0.5rem 1rem; |
| margin: 1rem 0; |
| background-color: #f8f9fa; |
| font-style: italic; |
| color: #4a5568; |
| } |
| |
| .markdown-content table { |
| border-collapse: collapse; |
| width: 100%; |
| margin: 1rem 0; |
| } |
| .markdown-content th, |
| .markdown-content td { |
| padding: 0.5rem; |
| border: 1px solid #e2e8f0; |
| } |
| .markdown-content th { |
| background-color: #f8f9fa; |
| font-weight: bold; |
| } |
| |
| .markdown-content hr { |
| margin: 1.5rem 0; |
| border: 0; |
| border-top: 1px solid #e2e8f0; |
| } |
| |
| .markdown-content a { |
| color: #4CAF50; |
| text-decoration: underline; |
| } |
| .markdown-content a:hover { |
| text-decoration: none; |
| } |
| @keyframes fadeIn { |
| from { opacity: 0; } |
| to { opacity: 1; } |
| } |
| .animate-fade-in { |
| animation: fadeIn 1s ease-out; |
| } |
| |
| #results { |
| transition: all 0.3s ease; |
| } |
| |
| .card-shadow { |
| box-shadow: 0 4px 14px rgba(0, 0, 0, 0.1); |
| } |
| </style> |
| </head> |
| <body class="bg-gray-50 min-h-screen"> |
| <div class="container mx-auto px-4 py-8 max-w-6xl"> |
| |
| <header class="text-center mb-12 animate-fade-in"> |
| <h1 class="text-5xl font-bold text-primary mb-4"> |
| ✨ Exercice de Philosophie (type3) ✨ |
| </h1> |
| <p class="text-gray-600 text-lg"> |
| Bienvenue ! Téléchargez vos images et laissez l'analyse philosophique opérer. |
| </p> |
| </header> |
|
|
| |
| <div class="grid md:grid-cols-2 gap-8"> |
| |
| <div class="bg-white rounded-xl shadow-lg p-6 card-shadow"> |
| <h2 class="text-2xl font-semibold text-primary mb-4">📷 Téléchargement d'images</h2> |
| <div |
| id="upload-zone" |
| class="upload-zone border-2 border-dashed border-primary rounded-lg p-8 text-center cursor-pointer hover:bg-gray-50 transition-colors" |
| onclick="document.getElementById('image-upload').click()"> |
| <input type="file" id="image-upload" multiple accept="image/*" class="hidden"> |
| <div class="flex flex-col items-center"> |
| <svg class="h-12 w-12 text-primary mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"></path> |
| </svg> |
| <span class="text-gray-600">Cliquez ou glissez vos images ici</span> |
| </div> |
| </div> |
| <div id="preview-container" class="mt-4 grid grid-cols-2 gap-4"></div> |
| </div> |
|
|
| |
| <div class="bg-white rounded-xl shadow-lg p-6 flex flex-col justify-center card-shadow"> |
| <h2 class="text-2xl font-semibold text-primary mb-4">🧠 Analyse Philosophique</h2> |
| <p class="text-gray-600 mb-6"> |
| Cette analyse vous fournira une réflexion philosophique approfondie basée sur le(s) document(s) téléchargé(s). |
| </p> |
| <button id="submit-btn" class="w-full mt-6 bg-primary hover:bg-primary-dark text-white font-bold py-3 px-6 rounded-lg transition-all transform hover:-translate-y-1 hover:shadow-lg disabled:opacity-50 disabled:cursor-not-allowed"> |
| 🚀 Soumettre |
| </button> |
| </div> |
| </div> |
|
|
| |
| <div id="results" class="mt-12 bg-white rounded-xl shadow-lg p-6 hidden animate-fade-in card-shadow"> |
| <h2 class="text-2xl font-semibold text-primary mb-4">📝 Résultat de l'analyse</h2> |
| <div id="analysis-result" class="markdown-content prose max-w-none"></div> |
| </div> |
|
|
| |
| <footer class="mt-12 text-center text-gray-600"> |
| <hr class="my-4"> |
| <p>© 2025 Mariam AI - Tous droits réservés.</p> |
| </footer> |
| </div> |
|
|
| <script> |
| document.addEventListener('DOMContentLoaded', () => { |
| const imageUpload = document.getElementById('image-upload'); |
| const previewContainer = document.getElementById('preview-container'); |
| const submitBtn = document.getElementById('submit-btn'); |
| const resultsSection = document.getElementById('results'); |
| const analysisResult = document.getElementById('analysis-result'); |
| const uploadZone = document.getElementById('upload-zone'); |
| |
| let uploadedFiles = []; |
| |
| |
| marked.setOptions({ |
| breaks: true, |
| gfm: true, |
| headerIds: true, |
| mangle: false, |
| smartLists: true, |
| smartypants: true, |
| xhtml: true |
| }); |
| |
| imageUpload.addEventListener('change', handleFileSelect); |
| submitBtn.addEventListener('click', handleSubmit); |
| |
| |
| uploadZone.addEventListener('dragover', (e) => { |
| e.preventDefault(); |
| uploadZone.classList.add('bg-gray-50'); |
| }); |
| |
| uploadZone.addEventListener('dragleave', () => { |
| uploadZone.classList.remove('bg-gray-50'); |
| }); |
| |
| uploadZone.addEventListener('drop', (e) => { |
| e.preventDefault(); |
| uploadZone.classList.remove('bg-gray-50'); |
| const files = Array.from(e.dataTransfer.files).filter(file => file.type.startsWith('image/')); |
| if (files.length > 0) { |
| uploadedFiles = files; |
| updatePreview(); |
| } |
| }); |
| |
| function handleFileSelect(event) { |
| uploadedFiles = Array.from(event.target.files); |
| updatePreview(); |
| } |
| |
| function updatePreview() { |
| previewContainer.innerHTML = ''; |
| uploadedFiles.forEach((file, index) => { |
| const preview = document.createElement('div'); |
| preview.className = 'relative'; |
| preview.innerHTML = ` |
| <img src="${URL.createObjectURL(file)}" alt="Preview" class="w-full h-32 object-cover rounded-lg shadow-md"> |
| <button onclick="removeImage(${index})" class="absolute top-2 right-2 bg-red-500 text-white rounded-full p-1 hover:bg-red-600 shadow-sm transition-all transform hover:scale-110"> |
| <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path> |
| </svg> |
| </button> |
| <div class="mt-1 text-sm text-gray-600 truncate">${file.name}</div> |
| `; |
| previewContainer.appendChild(preview); |
| }); |
| submitBtn.disabled = uploadedFiles.length === 0; |
| } |
| |
| function removeImage(index) { |
| uploadedFiles.splice(index, 1); |
| updatePreview(); |
| } |
| |
| async function handleSubmit() { |
| if (uploadedFiles.length === 0) { |
| alert('Veuillez sélectionner au moins une image.'); |
| return; |
| } |
| |
| const formData = new FormData(); |
| |
| uploadedFiles.forEach(file => formData.append('images', file)); |
| |
| submitBtn.disabled = true; |
| submitBtn.innerHTML = '<span class="animate-pulse">Analyse en cours...</span>'; |
| |
| try { |
| const response = await fetch('/analyze', { |
| method: 'POST', |
| body: formData |
| }); |
| |
| const data = await response.json(); |
| |
| if (data.error) { |
| throw new Error(data.error); |
| } |
| |
| |
| let markdownContent = data.result; |
| |
| |
| markdownContent = markdownContent.replace(/\n\n/g, '\n \n'); |
| |
| |
| markdownContent = markdownContent.replace(/ +/g, match => ' '.repeat(match.length)); |
| |
| resultsSection.classList.remove('hidden'); |
| analysisResult.innerHTML = marked.parse(markdownContent); |
| |
| |
| resultsSection.scrollIntoView({ |
| behavior: 'smooth', |
| block: 'start' |
| }); |
| } catch (error) { |
| alert('Erreur lors de l\'analyse: ' + error.message); |
| } finally { |
| submitBtn.disabled = false; |
| submitBtn.innerHTML = '🚀 Soumettre'; |
| } |
| } |
| |
| window.removeImage = removeImage; |
| }); |
| </script> |
| </body> |
| </html> |