cr / index.html
PaulARNON's picture
Update index.html
6b7e245 verified
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Générateur de Comptes Rendus Pro</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.min.js"></script>
<style>
.file-upload {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
padding: 2rem;
border: 2px dashed #3b82f6;
border-radius: 0.5rem;
background-color: #f8fafc;
transition: all 0.3s ease;
}
.file-upload:hover {
border-color: #2563eb;
background-color: #eff6ff;
}
.file-upload input {
position: absolute;
width: 100%;
height: 100%;
opacity: 0;
cursor: pointer;
}
.progress-bar {
width: 100%;
height: 8px;
background-color: #e5e7eb;
border-radius: 4px;
overflow: hidden;
margin-top: 1rem;
}
.progress {
height: 100%;
background-color: #3b82f6;
transition: width 0.3s ease;
}
.report-preview {
font-family: 'Segoe UI', system-ui, sans-serif;
line-height: 1.6;
color: #334155;
padding: 2rem;
background: white;
border-radius: 0.5rem;
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
}
.report-header {
border-bottom: 3px solid #1d4ed8;
padding-bottom: 1.5rem;
margin-bottom: 2rem;
}
.report-title {
font-size: 1.875rem;
font-weight: 700;
color: #1e3a8a;
margin-bottom: 0.5rem;
}
.report-meta {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
color: #64748b;
font-size: 0.875rem;
}
.report-section {
margin-bottom: 2rem;
page-break-inside: avoid;
}
.section-title {
font-weight: 600;
color: #1e40af;
font-size: 1.25rem;
padding: 0.5rem 0;
border-bottom: 2px solid #e2e8f0;
margin-bottom: 1rem;
position: relative;
}
.section-title::after {
content: "";
position: absolute;
bottom: -2px;
left: 0;
width: 4rem;
height: 2px;
background: #3b82f6;
}
.decision-item {
background: #f8fafc;
border-left: 4px solid #3b82f6;
padding: 1rem;
margin: 1rem 0;
border-radius: 0 6px 6px 0;
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
}
.action-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 1rem;
margin: 1.5rem 0;
}
.action-card {
background: white;
padding: 1.25rem;
border-radius: 8px;
border: 1px solid #e2e8f0;
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
}
.action-header {
display: flex;
align-items: center;
gap: 0.75rem;
margin-bottom: 1rem;
}
.action-icon {
background: #3b82f6;
width: 2rem;
height: 2rem;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
color: white;
}
.action-deadline {
background: #f0fdf4;
color: #15803d;
padding: 0.25rem 0.5rem;
border-radius: 4px;
font-size: 0.875rem;
display: inline-flex;
align-items: center;
gap: 0.25rem;
margin-top: 0.5rem;
}
.participant-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 0.75rem;
margin: 1rem 0;
}
.participant-badge {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem;
background: #f8fafc;
border-radius: 6px;
font-size: 0.875rem;
}
.agenda-list {
counter-reset: agenda;
margin: 1rem 0;
}
.agenda-item {
counter-increment: agenda;
padding: 0.75rem;
margin: 0.5rem 0;
background: #f8fafc;
border-radius: 6px;
}
.agenda-item::before {
content: counter(agenda) ". ";
font-weight: 600;
color: #3b82f6;
}
.toast {
position: fixed;
bottom: 20px;
right: 20px;
background-color: #4CAF50;
color: white;
padding: 16px;
border-radius: 4px;
z-index: 1000;
opacity: 0;
transition: opacity 0.5s;
}
.toast.show {
opacity: 1;
}
.toast.error {
background-color: #f44336;
}
.settings-panel {
transition: all 0.3s ease;
max-height: 0;
overflow: hidden;
}
.settings-panel.open {
max-height: 500px;
padding: 1rem;
border: 1px solid #e5e7eb;
border-radius: 0.5rem;
margin-top: 1rem;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.spinner {
animation: spin 1s linear infinite;
}
</style>
</head>
<body class="bg-gray-50 min-h-screen">
<div class="toast" id="toast"></div>
<div class="container mx-auto px-4 py-8">
<header class="mb-8 text-center">
<h1 class="text-3xl font-bold text-blue-800 mb-2">Générateur de Comptes Rendus Pro</h1>
<p class="text-gray-600">Transformez vos réunions en documents professionnels</p>
</header>
<div class="max-w-4xl mx-auto bg-white rounded-xl shadow-md overflow-hidden">
<!-- Paramètres API -->
<div class="p-6 border-b">
<button id="settings-toggle" class="flex items-center text-blue-600 hover:text-blue-800">
<i class="fas fa-cog mr-2"></i>
Paramètres API
</button>
<div id="settings-panel" class="settings-panel">
<div class="mb-4">
<label class="block text-gray-700 mb-2" for="api-key">Clé API Mistral AI</label>
<input type="password" id="api-key" placeholder="Entrez votre clé API" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
<div class="mb-4">
<label class="block text-gray-700 mb-2" for="model-select">Modèle</label>
<select id="model-select" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
<option value="mistral-tiny">Mistral Tiny</option>
<option value="mistral-small">Mistral Small</option>
<option value="mistral-medium-latest" selected>Mistral Medium</option>
<option value="mistral-large-latest">Mistral Large</option>
</select>
</div>
<button id="save-settings" class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition">
Enregistrer les paramètres
</button>
</div>
</div>
<!-- Zone de dépôt des fichiers -->
<div class="p-6 border-b">
<h2 class="text-xl font-semibold text-gray-800 mb-4">1. Importer votre réunion</h2>
<div class="file-upload mb-4" id="drop-area">
<i class="fas fa-cloud-upload-alt text-4xl text-blue-500 mb-2"></i>
<p class="text-gray-700 mb-2">Glissez-déposez vos fichiers ici</p>
<p class="text-sm text-gray-500 mb-2">ou</p>
<button class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition" id="browse-btn">
Parcourir les fichiers
</button>
<input type="file" id="file-input" accept=".pdf,.doc,.docx,.txt,.mp3,.mp4,.wav" multiple>
</div>
<div id="file-list" class="mt-4 hidden">
<h3 class="font-medium text-gray-700 mb-2">Fichiers sélectionnés :</h3>
<ul id="file-items" class="space-y-2"></ul>
<div class="progress-bar mt-4 hidden" id="upload-progress">
<div class="progress" id="progress-indicator" style="width: 0%"></div>
</div>
</div>
</div>
<!-- Options du compte rendu -->
<div class="p-6 border-b">
<h2 class="text-xl font-semibold text-gray-800 mb-4">2. Personnaliser le compte rendu</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label class="block text-gray-700 mb-2" for="report-length">Longueur</label>
<select id="report-length" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
<option value="short">Court (5-10 points clés)</option>
<option value="medium" selected>Moyen (résumé détaillé)</option>
<option value="long">Long (transcription annotée)</option>
</select>
</div>
<div>
<label class="block text-gray-700 mb-2" for="report-style">Style et ton</label>
<select id="report-style" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
<option value="formal">Formel</option>
<option value="professional" selected>Professionnel</option>
<option value="concise">Concis</option>
<option value="friendly">Amical</option>
</select>
</div>
<div>
<label class="block text-gray-700 mb-2" for="report-audience">Destinataire</label>
<select id="report-audience" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
<option value="executives">Élus</option>
<option value="management" selected>Direction</option>
<option value="technical">Techniciens</option>
<option value="partners">Partenaires</option>
</select>
</div>
<div>
<label class="block text-gray-700 mb-2" for="report-language">Langue</label>
<select id="report-language" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
<option value="fr" selected>Français</option>
<option value="en">Anglais</option>
<option value="de">Allemand</option>
<option value="es">Espagnol</option>
</select>
</div>
</div>
<div class="mt-6">
<label class="block text-gray-700 mb-2" for="additional-notes">Notes supplémentaires</label>
<textarea id="additional-notes" rows="3" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" placeholder="Points spécifiques à inclure, format particulier, etc."></textarea>
</div>
<div class="mt-6">
<label class="block text-gray-700 mb-2" for="meeting-title">Titre de la réunion</label>
<input type="text" id="meeting-title" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" placeholder="Ex: Réunion du comité de direction">
</div>
<div class="mt-6">
<label class="block text-gray-700 mb-2" for="meeting-date">Date de la réunion</label>
<input type="date" id="meeting-date" class="w-full px-3 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
</div>
<!-- Génération et résultat -->
<div class="p-6">
<h2 class="text-xl font-semibold text-gray-800 mb-4">3. Générer et exporter</h2>
<button id="generate-btn" class="bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700 transition flex items-center justify-center w-full mb-6">
<i class="fas fa-magic mr-2"></i> Générer le compte rendu
</button>
<div id="loading-indicator" class="hidden text-center py-4">
<div class="inline-block spinner text-blue-500 text-4xl mb-2">
<i class="fas fa-circle-notch"></i>
</div>
<p class="text-gray-600">Analyse et génération en cours...</p>
<p class="text-sm text-gray-500">Cette opération peut prendre quelques minutes</p>
</div>
<div id="result-container" class="hidden">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-medium text-gray-800">Compte rendu généré</h3>
<div class="flex space-x-2">
<button id="regenerate-btn" class="text-blue-600 hover:text-blue-800 flex items-center">
<i class="fas fa-sync-alt mr-1"></i> Régénérer
</button>
<button id="download-pdf" class="text-blue-600 hover:text-blue-800 flex items-center">
<i class="fas fa-file-pdf mr-1"></i> Télécharger PDF
</button>
<button id="copy-to-clipboard" class="text-blue-600 hover:text-blue-800 flex items-center">
<i class="fas fa-copy mr-1"></i> Copier
</button>
</div>
</div>
<div id="report-preview" class="report-preview">
<!-- Le compte rendu sera inséré ici -->
</div>
<div class="mt-4 flex justify-end">
<button id="download-pdf-full" class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition flex items-center">
<i class="fas fa-file-pdf mr-2"></i> Télécharger en PDF
</button>
</div>
</div>
</div>
</div>
<footer class="mt-12 text-center text-gray-500 text-sm">
<p>Générateur de Comptes Rendus Pro © 2023 - Propulsé par Mistral AI</p>
</footer>
</div>
<script>
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.worker.min.js';
document.addEventListener('DOMContentLoaded', function() {
// Gestion des paramètres API
const settingsToggle = document.getElementById('settings-toggle');
const settingsPanel = document.getElementById('settings-panel');
settingsToggle.addEventListener('click', function() {
settingsPanel.classList.toggle('open');
this.querySelector('i').classList.toggle('fa-spin');
});
// Sauvegarde des paramètres
document.getElementById('save-settings').addEventListener('click', function() {
const apiKey = document.getElementById('api-key').value;
const model = document.getElementById('model-select').value;
if (!apiKey) {
showToast('Veuillez entrer une clé API valide', 'error');
return;
}
// Stockage local des paramètres
localStorage.setItem('apiKey', apiKey);
localStorage.setItem('model', model);
showToast('Paramètres enregistrés avec succès');
});
// Chargement des paramètres sauvegardés
if (localStorage.getItem('apiKey')) {
document.getElementById('api-key').value = localStorage.getItem('apiKey');
}
if (localStorage.getItem('model')) {
document.getElementById('model-select').value = localStorage.getItem('model');
}
// Gestion de l'upload de fichiers
const dropArea = document.getElementById('drop-area');
const fileInput = document.getElementById('file-input');
const browseBtn = document.getElementById('browse-btn');
const fileList = document.getElementById('file-list');
const fileItems = document.getElementById('file-items');
const uploadProgress = document.getElementById('upload-progress');
const progressIndicator = document.getElementById('progress-indicator');
let uploadedFiles = [];
// Empêcher les comportements par défaut pour le drag and drop
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
dropArea.addEventListener(eventName, preventDefaults, false);
});
function preventDefaults(e) {
e.preventDefault();
e.stopPropagation();
}
// Highlight drop area when item is dragged over it
['dragenter', 'dragover'].forEach(eventName => {
dropArea.addEventListener(eventName, highlight, false);
});
['dragleave', 'drop'].forEach(eventName => {
dropArea.addEventListener(eventName, unhighlight, false);
});
function highlight() {
dropArea.classList.add('bg-blue-50');
dropArea.classList.add('border-blue-400');
}
function unhighlight() {
dropArea.classList.remove('bg-blue-50');
dropArea.classList.remove('border-blue-400');
}
// Handle dropped files
dropArea.addEventListener('drop', handleDrop, false);
function handleDrop(e) {
const dt = e.dataTransfer;
const files = dt.files;
handleFiles(files);
}
// Handle file selection via browse button
browseBtn.addEventListener('click', function() {
fileInput.click();
});
fileInput.addEventListener('change', function() {
handleFiles(this.files);
});
// Process selected files
function handleFiles(files) {
fileItems.innerHTML = '';
uploadedFiles = [];
if (files.length > 0) {
fileList.classList.remove('hidden');
for (let i = 0; i < files.length; i++) {
const file = files[i];
const listItem = document.createElement('li');
listItem.className = 'flex items-center justify-between bg-gray-50 p-2 rounded';
const fileInfo = document.createElement('div');
fileInfo.className = 'flex items-center';
const icon = document.createElement('i');
icon.className = 'fas ' + getFileIcon(file.type) + ' text-blue-500 mr-2';
const fileName = document.createElement('span');
fileName.className = 'text-gray-700';
fileName.textContent = file.name;
const fileSize = document.createElement('span');
fileSize.className = 'text-gray-500 text-sm ml-2';
fileSize.textContent = formatFileSize(file.size);
const removeBtn = document.createElement('button');
removeBtn.className = 'text-red-500 hover:text-red-700';
removeBtn.innerHTML = '<i class="fas fa-times"></i>';
removeBtn.addEventListener('click', function() {
listItem.remove();
uploadedFiles = uploadedFiles.filter(f => f.name !== file.name);
if (fileItems.children.length === 0) {
fileList.classList.add('hidden');
}
});
fileInfo.appendChild(icon);
fileInfo.appendChild(fileName);
fileInfo.appendChild(fileSize);
listItem.appendChild(fileInfo);
listItem.appendChild(removeBtn);
fileItems.appendChild(listItem);
uploadedFiles.push(file);
}
// Simuler l'upload
simulateUpload();
}
}
function getFileIcon(fileType) {
if (fileType.match('application/pdf')) return 'fa-file-pdf';
if (fileType.match('application/msword') || fileType.match('application/vnd.openxmlformats-officedocument.wordprocessingml.document'))
return 'fa-file-word';
if (fileType.match('text/plain')) return 'fa-file-alt';
if (fileType.match('audio/')) return 'fa-file-audio';
if (fileType.match('video/')) return 'fa-file-video';
return 'fa-file';
}
function formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
function simulateUpload() {
uploadProgress.classList.remove('hidden');
let progress = 0;
const interval = setInterval(function() {
progress += Math.random() * 10;
if (progress >= 100) {
progress = 100;
clearInterval(interval);
setTimeout(function() {
uploadProgress.classList.add('hidden');
showToast('Fichiers téléchargés avec succès');
}, 500);
}
progressIndicator.style.width = progress + '%';
}, 200);
}
// Fonction pour afficher les notifications
function showToast(message, type = 'success') {
const toast = document.getElementById('toast');
toast.textContent = message;
toast.className = 'toast';
toast.classList.add('show');
if (type === 'error') {
toast.classList.add('error');
}
setTimeout(function() {
toast.classList.remove('show');
}, 3000);
}
// Fonction pour extraire le texte des fichiers
async function extractTextFromFiles(files) {
let combinedText = '';
for (const file of files) {
if (file.type === 'application/pdf') {
const text = await extractTextFromPDF(file);
combinedText += text + '\n\n';
} else if (file.type.match('text/plain') ||
file.type.match('application/msword') ||
file.type.match('application/vnd.openxmlformats-officedocument.wordprocessingml.document')) {
const text = await readTextFile(file);
combinedText += text + '\n\n';
} else if (file.type.match('audio/') || file.type.match('video/')) {
// Pour les fichiers audio/vidéo, nous ne pouvons pas extraire le texte directement
// Dans une vraie application, vous utiliseriez une API de transcription audio
combinedText += `[Contenu audio/vidéo: ${file.name}]\n\n`;
}
}
return combinedText;
}
// Extraire le texte d'un PDF
async function extractTextFromPDF(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = async function(e) {
try {
const typedArray = new Uint8Array(e.target.result);
const pdf = await pdfjsLib.getDocument(typedArray).promise;
let text = '';
for (let i = 1; i <= pdf.numPages; i++) {
const page = await pdf.getPage(i);
const content = await page.getTextContent();
const strings = content.items.map(item => item.str);
text += strings.join(' ') + '\n';
}
resolve(text);
} catch (error) {
console.error('Error extracting PDF text:', error);
resolve(`[Erreur lors de l'extraction du PDF: ${file.name}]`);
}
};
reader.onerror = function(error) {
console.error('Error reading PDF file:', error);
resolve(`[Erreur lors de la lecture du PDF: ${file.name}]`);
};
reader.readAsArrayBuffer(file);
});
}
// Lire un fichier texte
function readTextFile(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = function(e) {
resolve(e.target.result);
};
reader.onerror = function(error) {
console.error('Error reading text file:', error);
resolve(`[Erreur lors de la lecture du fichier: ${file.name}]`);
};
reader.readAsText(file);
});
}
// Générer un prompt pour l'API Mistral
function generatePrompt(textContent, options) {
const meetingTitle = document.getElementById('meeting-title').value || 'Réunion importante';
const meetingDate = document.getElementById('meeting-date').value || new Date().toISOString().split('T')[0];
const additionalNotes = document.getElementById('additional-notes').value || '';
let lengthInstruction = '';
if (options.length === 'short') {
lengthInstruction = 'Résumez en 5 à 10 points clés maximum.';
} else if (options.length === 'medium') {
lengthInstruction = 'Fournissez un résumé détaillé avec les points principaux.';
} else {
lengthInstruction = 'Fournissez une transcription annotée et détaillée.';
}
let styleInstruction = '';
if (options.style === 'formal') {
styleInstruction = 'Utilisez un style formel et soutenu.';
} else if (options.style === 'professional') {
styleInstruction = 'Utilisez un style professionnel et clair.';
} else if (options.style === 'concise') {
styleInstruction = 'Soyez concis et allez droit au but.';
} else {
styleInstruction = 'Utilisez un ton amical mais professionnel.';
}
let audienceInstruction = '';
if (options.audience === 'executives') {
audienceInstruction = 'Adaptez le compte rendu pour des élus, en mettant l\'accent sur les décisions stratégiques.';
} else if (options.audience === 'management') {
audienceInstruction = 'Adaptez pour la direction, avec des détails opérationnels.';
} else if (options.audience === 'technical') {
audienceInstruction = 'Adaptez pour des techniciens, avec des détails techniques.';
} else {
audienceInstruction = 'Adaptez pour des partenaires externes.';
}
return `Créez un compte rendu professionnel de réunion en français avec les consignes suivantes:
Titre: ${meetingTitle}
Date: ${meetingDate}
${additionalNotes ? `Notes supplémentaires: ${additionalNotes}` : ''}
Instructions:
1. ${lengthInstruction}
2. ${styleInstruction}
3. ${audienceInstruction}
4. Structurez le compte rendu avec les sections suivantes: Participants, Ordre du jour, Décisions prises, Actions à mener, Prochaine réunion.
5. Utilisez un format HTML propre avec des classes CSS pour le style.
Contenu de la réunion à analyser:
${textContent}`;
}
// Appeler l'API Mistral
async function callMistralAPI(prompt) {
const apiKey = localStorage.getItem('apiKey');
const model = localStorage.getItem('model') || 'mistral-medium';
if (!apiKey) {
showToast('Veuillez configurer votre clé API dans les paramètres', 'error');
throw new Error('API key not configured');
}
try {
const response = await fetch('https://api.mistral.ai/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`
},
body: JSON.stringify({
model: model,
messages: [
{
role: "user",
content: prompt
}
],
temperature: 0.7,
max_tokens: 4000
})
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error?.message || 'Erreur API');
}
const data = await response.json();
return data.choices[0].message.content;
} catch (error) {
console.error('Error calling Mistral API:', error);
throw error;
}
}
// Bouton de génération
document.getElementById('generate-btn').addEventListener('click', async function() {
if (uploadedFiles.length === 0) {
showToast('Veuvez ajouter au moins un fichier', 'error');
return;
}
const loadingIndicator = document.getElementById('loading-indicator');
const resultContainer = document.getElementById('result-container');
loadingIndicator.classList.remove('hidden');
resultContainer.classList.add('hidden');
try {
// Extraire le texte des fichiers
const textContent = await extractTextFromFiles(uploadedFiles);
// Récupérer les options
const options = {
length: document.getElementById('report-length').value,
style: document.getElementById('report-style').value,
audience: document.getElementById('report-audience').value,
language: document.getElementById('report-language').value
};
// Générer le prompt
const prompt = generatePrompt(textContent, options);
// Appeler l'API
const result = await callMistralAPI(prompt);
// Afficher le résultat
document.getElementById('report-preview').innerHTML = result;
loadingIndicator.classList.add('hidden');
resultContainer.classList.remove('hidden');
showToast('Compte rendu généré avec succès');
} catch (error) {
console.error('Error generating report:', error);
loadingIndicator.classList.add('hidden');
showToast(`Erreur: ${error.message}`, 'error');
}
});
// Bouton de régénération
document.getElementById('regenerate-btn').addEventListener('click', async function() {
showToast('Régénération du compte rendu en cours...');
document.getElementById('generate-btn').click();
});
// Bouton de téléchargement PDF (simulation)
document.getElementById('download-pdf').addEventListener('click', function() {
showToast('Téléchargement du PDF en cours...');
});
document.getElementById('download-pdf-full').addEventListener('click', function() {
showToast('Téléchargement du PDF complet en cours...');
});
// Bouton de copie dans le presse-papier
document.getElementById('copy-to-clipboard').addEventListener('click', function() {
const reportText = document.getElementById('report-preview').textContent;
navigator.clipboard.writeText(reportText)
.then(() => showToast('Compte rendu copié dans le presse-papier'))
.catch(err => showToast('Erreur lors de la copie', 'error'));
});
// Fonction utilitaire pour formater la date
function formatDate(dateString) {
const options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' };
return new Date(dateString).toLocaleDateString('fr-FR', options);
}
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=PaulARNON/cr" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>