|
|
<!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"> |
|
|
|
|
|
<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> |
|
|
|
|
|
|
|
|
<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> |
|
|
|
|
|
|
|
|
<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> |
|
|
|
|
|
|
|
|
<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"> |
|
|
|
|
|
</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() { |
|
|
|
|
|
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'); |
|
|
}); |
|
|
|
|
|
|
|
|
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; |
|
|
} |
|
|
|
|
|
|
|
|
localStorage.setItem('apiKey', apiKey); |
|
|
localStorage.setItem('model', model); |
|
|
|
|
|
showToast('Paramètres enregistrés avec succè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'); |
|
|
} |
|
|
|
|
|
|
|
|
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 = []; |
|
|
|
|
|
|
|
|
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { |
|
|
dropArea.addEventListener(eventName, preventDefaults, false); |
|
|
}); |
|
|
|
|
|
function preventDefaults(e) { |
|
|
e.preventDefault(); |
|
|
e.stopPropagation(); |
|
|
} |
|
|
|
|
|
|
|
|
['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'); |
|
|
} |
|
|
|
|
|
|
|
|
dropArea.addEventListener('drop', handleDrop, false); |
|
|
|
|
|
function handleDrop(e) { |
|
|
const dt = e.dataTransfer; |
|
|
const files = dt.files; |
|
|
handleFiles(files); |
|
|
} |
|
|
|
|
|
|
|
|
browseBtn.addEventListener('click', function() { |
|
|
fileInput.click(); |
|
|
}); |
|
|
|
|
|
fileInput.addEventListener('change', function() { |
|
|
handleFiles(this.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); |
|
|
} |
|
|
|
|
|
|
|
|
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); |
|
|
} |
|
|
|
|
|
|
|
|
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); |
|
|
} |
|
|
|
|
|
|
|
|
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/')) { |
|
|
|
|
|
|
|
|
combinedText += `[Contenu audio/vidéo: ${file.name}]\n\n`; |
|
|
} |
|
|
} |
|
|
|
|
|
return combinedText; |
|
|
} |
|
|
|
|
|
|
|
|
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); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
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); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
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}`; |
|
|
} |
|
|
|
|
|
|
|
|
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; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
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 { |
|
|
|
|
|
const textContent = await extractTextFromFiles(uploadedFiles); |
|
|
|
|
|
|
|
|
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 |
|
|
}; |
|
|
|
|
|
|
|
|
const prompt = generatePrompt(textContent, options); |
|
|
|
|
|
|
|
|
const result = await callMistralAPI(prompt); |
|
|
|
|
|
|
|
|
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'); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
document.getElementById('regenerate-btn').addEventListener('click', async function() { |
|
|
showToast('Régénération du compte rendu en cours...'); |
|
|
document.getElementById('generate-btn').click(); |
|
|
}); |
|
|
|
|
|
|
|
|
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...'); |
|
|
}); |
|
|
|
|
|
|
|
|
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')); |
|
|
}); |
|
|
|
|
|
|
|
|
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> |