// Application principale class ConceptionAssistant { constructor() { this.currentJournalId = null; this.editor = null; this.autoSaveTimer = null; this.init(); } async init() { this.setupEditor(); this.setupEventListeners(); this.setupAutoSave(); await this.loadJournalList(); this.generateTOC(); } setupEditor() { this.editor = document.getElementById('journal-editor'); // Générer TOC à chaque modification this.editor.addEventListener('input', () => { this.generateTOC(); this.resetAutoSave(); }); // Gestion des raccourcis clavier this.editor.addEventListener('keydown', (e) => { // Ctrl+S pour sauvegarder if (e.ctrlKey && e.key === 's') { e.preventDefault(); this.saveJournal(); } // Tab pour indentation if (e.key === 'Tab') { e.preventDefault(); document.execCommand('insertText', false, ' '); } }); } showEditorPlaceholder() { // Le placeholder est maintenant géré via CSS ::before // Cette fonction peut être supprimée ou laissée vide pour compatibilité } setupEventListeners() { // Boutons de contrôle des journaux document.getElementById('save-journal')?.addEventListener('click', () => this.saveJournal()); document.getElementById('load-journal')?.addEventListener('click', () => this.showJournalSelector()); // Table des matières document.getElementById('refresh-toc')?.addEventListener('click', () => this.generateTOC()); // Export/Import document.getElementById('export-md')?.addEventListener('click', () => this.exportMarkdown()); document.getElementById('export-pdf')?.addEventListener('click', () => this.exportPDF()); document.getElementById('import-md')?.addEventListener('change', (e) => this.importMarkdown(e)); // Thème document.getElementById('theme-toggle')?.addEventListener('click', () => this.toggleTheme()); // Assistant IA (simulé pour le MVP) document.getElementById('activate-rephrase')?.addEventListener('click', () => this.handleAI('rephrase')); document.getElementById('check-inconsistencies')?.addEventListener('click', () => this.handleAI('inconsistencies')); document.getElementById('check-duplications')?.addEventListener('click', () => this.handleAI('duplications')); document.getElementById('give-advice')?.addEventListener('click', () => this.handleAI('advice')); document.getElementById('liberty-mode')?.addEventListener('click', () => this.handleAI('liberty')); } setupAutoSave() { // Sauvegarde automatique toutes les 30 secondes setInterval(() => { if (this.currentJournalId && this.editor.innerText.trim()) { this.saveJournal(true); } }, 30000); } resetAutoSave() { if (this.autoSaveTimer) { clearTimeout(this.autoSaveTimer); } this.autoSaveTimer = setTimeout(() => { if (this.currentJournalId && this.editor.innerText.trim()) { this.saveJournal(true); } }, 3000); } async saveJournal(isAutoSave = false) { const content = this.editor.innerText; if (!content) return; const statusEl = document.getElementById('save-status'); const saveBtn = document.getElementById('save-journal'); if (!isAutoSave) { saveBtn.classList.add('loading'); statusEl.textContent = 'Sauvegarde...'; } try { let response; if (this.currentJournalId) { // Mise à jour response = await fetch(`/api/journals/${this.currentJournalId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ content }) }); } else { // Création response = await fetch('/api/journals', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ content }) }); } const result = await response.json(); if (result.success) { if (!this.currentJournalId) { this.currentJournalId = result.data.id; } if (!isAutoSave) { statusEl.textContent = '✅ Sauvegardé'; this.showNotification('Journal sauvegardé avec succès', 'success'); setTimeout(() => statusEl.textContent = '', 3000); } else { statusEl.textContent = '💾 Auto-sauvegardé'; setTimeout(() => statusEl.textContent = '', 2000); } } else { throw new Error(result.error || 'Erreur de sauvegarde'); } } catch (error) { console.error('Erreur:', error); statusEl.textContent = '❌ Erreur'; if (!isAutoSave) { this.showNotification('Erreur lors de la sauvegarde: ' + error.message, 'error'); } setTimeout(() => statusEl.textContent = '', 3000); } finally { if (!isAutoSave) { saveBtn.classList.remove('loading'); } } } async loadJournalList() { try { const response = await fetch('/api/journals'); const result = await response.json(); if (result.success && result.data.length > 0) { // Charger le dernier journal automatiquement const lastJournal = result.data[result.data.length - 1]; await this.loadJournal(lastJournal.id); } } catch (error) { console.error('Erreur chargement liste:', error); } } async loadJournal(id) { try { const response = await fetch(`/api/journals/${id}`); const result = await response.json(); if (result.success && result.data.length > 0) { const journal = result.data[0]; this.currentJournalId = id; this.editor.innerText = journal.markdownContent; this.generateTOC(); this.showNotification('Journal chargé', 'success'); } } catch (error) { console.error('Erreur chargement journal:', error); this.showNotification('Erreur lors du chargement', 'error'); } } async showJournalSelector() { try { const response = await fetch('/api/journals'); const result = await response.json(); if (result.success) { const journalList = result.data.map(journal => { const preview = journal.markdownContent.substring(0, 100).replace(/\n/g, ' '); return ``; }).join(''); const feedback = document.getElementById('ai-assistant-feedback'); feedback.innerHTML = `

📂 Sélectionner un journal

${journalList}
`; // Ajouter les event listeners document.querySelectorAll('.journal-item').forEach(btn => { btn.addEventListener('click', () => { this.loadJournal(btn.dataset.id); this.clearFeedback(); }); }); } } catch (error) { console.error('Erreur:', error); this.showNotification('Erreur lors du chargement de la liste', 'error'); } } createNewJournal() { this.currentJournalId = null; this.editor.innerText = ''; this.generateTOC(); this.clearFeedback(); this.showNotification('Nouveau journal créé', 'success'); } generateTOC() { const content = this.editor.innerText; const lines = content.split('\n'); const toc = []; let tocHtml = ''; for (let i = 0; i < lines.length; i++) { const line = lines[i].trim(); if (line.startsWith('#')) { const level = line.match(/^#+/)[0].length; const title = line.replace(/^#+\s*/, ''); const id = 'heading-' + i; toc.push({ level, title, id }); } } if (toc.length > 0) { tocHtml = ''; } else { tocHtml = '

Ajoutez des titres (# ## ###) à votre journal pour générer la table des matières.

'; } document.getElementById('toc-nav').innerHTML = tocHtml; } scrollToHeading(headingId) { // Simulation du scroll vers les titres this.showNotification('Navigation vers la section', 'success'); } exportMarkdown() { const content = this.editor.innerText; if (!content.trim()) { this.showNotification('Aucun contenu à exporter', 'warning'); return; } const blob = new Blob([content], { type: 'text/markdown' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'journal-conception.md'; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); this.showNotification('Fichier Markdown exporté', 'success'); } exportPDF() { // Simulation export PDF (nécessite une vraie implémentation côté serveur) this.showNotification('Export PDF : fonctionnalité à venir', 'warning'); } importMarkdown(event) { const file = event.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = (e) => { this.editor.innerText = e.target.result; this.generateTOC(); this.currentJournalId = null; // Nouveau journal this.showNotification('Fichier Markdown importé', 'success'); }; reader.readAsText(file); } toggleTheme() { document.body.classList.toggle('dark-theme'); const isDark = document.body.classList.contains('dark-theme'); localStorage.setItem('theme', isDark ? 'dark' : 'light'); this.showNotification(`Mode ${isDark ? 'sombre' : 'clair'} activé`, 'success'); } handleAI(action) { const selection = window.getSelection().toString(); let message = ''; switch (action) { case 'rephrase': if (!selection) { message = '⚠️ Sélectionnez du texte à reformuler'; } else { message = `✨ Reformulation suggérée pour:
"${selection.substring(0, 100)}..."

Version reformulée :
"${this.simulateRephrase(selection)}"`; } break; case 'inconsistencies': message = '🔍 Analyse des incohérences...

✅ Aucune incohérence majeure détectée dans le document.'; break; case 'duplications': message = '📋 Vérification des duplications...

✅ Aucune duplication significative trouvée.'; break; case 'advice': message = '💡 Conseils pour améliorer votre journal :

• Ajoutez plus de détails sur les alternatives considérées
• Documentez les raisons des choix techniques
• Incluez des diagrammes ou schémas
• Ajoutez une section "Lessons learned"'; break; case 'liberty': const count = document.getElementById('liberty-repeat-count').value || 3; message = `🚀 Mode Liberté activé (${count} itérations)

Génération de contenus automatiques basée sur le contexte existant...

Cette fonctionnalité nécessite une intégration IA complète.`; break; } this.showAIFeedback(message); } simulateRephrase(text) { // Simulation simple de reformulation const replacements = { 'nous devons': 'il convient de', 'c\'est important': 'cela revêt une importance', 'il faut': 'il est nécessaire de', 'très': 'particulièrement', 'beaucoup': 'considérablement' }; let result = text; Object.entries(replacements).forEach(([from, to]) => { result = result.replace(new RegExp(from, 'gi'), to); }); return result || text; } showAIFeedback(message) { const feedback = document.getElementById('ai-assistant-feedback'); feedback.innerHTML = `
${message}
`; } clearFeedback() { const feedback = document.getElementById('ai-assistant-feedback'); feedback.innerHTML = `
🎯 Assistant prêt
Sélectionnez du texte dans l'éditeur et cliquez sur une action pour commencer.
`; } showNotification(message, type = 'success') { const notification = document.createElement('div'); notification.className = `notification ${type}`; notification.textContent = message; document.body.appendChild(notification); setTimeout(() => notification.classList.add('show'), 100); setTimeout(() => { notification.classList.remove('show'); setTimeout(() => document.body.removeChild(notification), 300); }, 3000); } } // Gestion des panneaux latéraux function togglePanel(side) { const panel = document.getElementById(`${side}-panel`); const overlay = document.getElementById('panel-overlay'); const otherPanel = document.getElementById(side === 'left' ? 'right-panel' : 'left-panel'); // Fermer l'autre panneau s'il est ouvert if (otherPanel && otherPanel.classList.contains('open')) { otherPanel.classList.remove('open'); } // Toggle le panneau actuel if (panel.classList.contains('open')) { panel.classList.remove('open'); overlay.classList.remove('active'); } else { panel.classList.add('open'); overlay.classList.add('active'); } } function closeAllPanels() { const leftPanel = document.getElementById('left-panel'); const rightPanel = document.getElementById('right-panel'); const overlay = document.getElementById('panel-overlay'); if (leftPanel) leftPanel.classList.remove('open'); if (rightPanel) rightPanel.classList.remove('open'); if (overlay) overlay.classList.remove('active'); } // Fermer les panneaux avec Escape document.addEventListener('keydown', (e) => { if (e.key === 'Escape') { closeAllPanels(); } }); // Initialisation de l'application let app; document.addEventListener('DOMContentLoaded', () => { app = new ConceptionAssistant(); // Charger le thème sauvegardé const savedTheme = localStorage.getItem('theme'); if (savedTheme === 'dark') { document.body.classList.add('dark-theme'); } // Initialiser les panneaux initializePanels(); }); function initializePanels() { // Initialiser la gestion des templates initializeTemplateForm(); } function initializeTemplateForm() { const domainSelect = document.getElementById('domain-select'); const levelSelect = document.getElementById('level-select'); const loadTemplateBtn = document.getElementById('load-template'); const resetTemplateBtn = document.getElementById('reset-template'); const templatePreview = document.getElementById('template-preview'); const previewContent = document.getElementById('preview-content'); // Gestion du changement de domaine if (domainSelect) { domainSelect.addEventListener('change', async () => { const domain = domainSelect.value; if (!domain) { levelSelect.disabled = true; levelSelect.innerHTML = ''; loadTemplateBtn.disabled = true; templatePreview.style.display = 'none'; return; } // Activer le select de niveau levelSelect.disabled = false; levelSelect.innerHTML = ` `; // Cacher l'aperçu templatePreview.style.display = 'none'; loadTemplateBtn.disabled = true; }); } // Gestion du changement de niveau if (levelSelect) { levelSelect.addEventListener('change', async () => { const domain = domainSelect.value; const level = levelSelect.value; if (!domain || !level) { templatePreview.style.display = 'none'; loadTemplateBtn.disabled = true; return; } try { // Charger l'aperçu du template const response = await fetch(`/api/templates/${domain}/${level}`); const result = await response.json(); if (result.success) { // Afficher un aperçu (premières lignes) const lines = result.data.content.split('\n'); const preview = lines.slice(0, 8).join('\n'); previewContent.textContent = preview + (lines.length > 8 ? '\n...' : ''); templatePreview.style.display = 'block'; loadTemplateBtn.disabled = false; } else { app.showNotification('Erreur lors du chargement de l\'aperçu', 'error'); } } catch (error) { console.error('Erreur:', error); app.showNotification('Erreur lors du chargement de l\'aperçu', 'error'); } }); } // Gestion du chargement du template if (loadTemplateBtn) { loadTemplateBtn.addEventListener('click', async () => { const domain = domainSelect.value; const level = levelSelect.value; if (!domain || !level) { app.showNotification('Veuillez sélectionner un domaine et un niveau', 'warning'); return; } try { loadTemplateBtn.classList.add('loading'); const response = await fetch(`/api/templates/${domain}/${level}`); const result = await response.json(); if (result.success) { // Charger le template dans l'éditeur app.editor.innerText = result.data.content; app.generateTOC(); app.currentJournalId = null; // Nouveau journal app.showNotification(`Template ${domain}/${level} chargé avec succès`, 'success'); closeAllPanels(); } else { app.showNotification('Erreur lors du chargement du template', 'error'); } } catch (error) { console.error('Erreur:', error); app.showNotification('Erreur lors du chargement du template', 'error'); } finally { loadTemplateBtn.classList.remove('loading'); } }); } // Gestion du reset if (resetTemplateBtn) { resetTemplateBtn.addEventListener('click', () => { domainSelect.value = ''; levelSelect.value = ''; levelSelect.disabled = true; levelSelect.innerHTML = ''; templatePreview.style.display = 'none'; loadTemplateBtn.disabled = true; }); } }