Amélioration complète de l'application web Journal de Conception

- Refonte complète du design avec système de panneaux latéraux rétractables
- Ajout de templates de projets par domaine (recherche, informatique, mathématiques, etc.)
- Implémentation système d'export PDF avec Puppeteer
- Amélioration de l'API REST avec nouvelles routes d'export et templates
- Ajout de JavaScript client pour interactions dynamiques
- Configuration environnement étendue pour futures fonctionnalités IA
- Amélioration responsive design et expérience utilisateur

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Augustin 2025-09-25 12:09:49 +02:00
parent 750e2c4fe8
commit 4a8b6d1cf8
20 changed files with 4605 additions and 78 deletions

11
app.js
View File

@ -3,6 +3,8 @@ const path = require('path');
const indexRoutes = require('./routes/index');
const apiRoutes = require('./routes/api');
const templatesRoutes = require('./routes/templates');
const exportRoutes = require('./routes/export');
const app = express();
const port = process.env.PORT || 3000;
@ -12,8 +14,17 @@ app.use(express.urlencoded({ extended: true }));
app.use('/assets', express.static(path.join(__dirname, 'assets')));
// Créer le dossier data s'il n'existe pas
const fs = require('fs');
const dataDir = path.join(__dirname, 'data');
if (!fs.existsSync(dataDir)) {
fs.mkdirSync(dataDir);
}
app.use('/', indexRoutes);
app.use('/api', apiRoutes);
app.use('/api/templates', templatesRoutes);
app.use('/api/export', exportRoutes);
app.listen(port, () => {
console.log(`Server running on port ${port}`);

View File

@ -0,0 +1,998 @@
/* Variables CSS */
:root {
--primary-color: #2c3e50;
--secondary-color: #3498db;
--accent-color: #e74c3c;
--success-color: #27ae60;
--warning-color: #f39c12;
--background-color: #ecf0f1;
--surface-color: #ffffff;
--text-color: #2c3e50;
--text-light: #7f8c8d;
--border-color: #bdc3c7;
--shadow: 0 2px 4px rgba(0,0,0,0.1);
--shadow-hover: 0 4px 8px rgba(0,0,0,0.15);
--border-radius: 8px;
--transition: all 0.3s ease;
}
/* Mode sombre */
body.dark-theme {
--primary-color: #ecf0f1;
--secondary-color: #3498db;
--background-color: #1a1a1a;
--surface-color: #2c2c2c;
--text-color: #ecf0f1;
--text-light: #95a5a6;
--border-color: #34495e;
}
/* Reset et base */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
line-height: 1.6;
color: var(--text-color);
background-color: var(--background-color);
transition: var(--transition);
}
/* Layout principal */
.container {
max-width: 1400px;
margin: 0 auto;
padding: 0 20px;
}
/* Header professionnel */
header {
background: var(--surface-color);
border-bottom: 1px solid var(--border-color);
box-shadow: var(--shadow);
position: sticky;
top: 0;
z-index: 100;
}
header > div {
display: flex;
align-items: center;
justify-content: center;
padding: 2rem;
max-width: 1400px;
margin: 0 auto;
}
header h1 {
color: var(--primary-color);
font-size: 2.2rem;
font-weight: 600;
margin: 0;
display: flex;
align-items: center;
gap: 0.75rem;
text-align: center;
letter-spacing: -0.5px;
}
header h1::before {
content: "📝";
font-size: 2.4rem;
}
/* Panneaux latéraux rétractables */
.side-panel {
position: fixed;
top: 50%;
transform: translateY(-50%);
background: var(--surface-color);
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
box-shadow: var(--shadow-hover);
z-index: 90;
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
backdrop-filter: blur(10px);
}
/* Panneau gauche - Configuration */
.left-panel {
left: -320px;
width: 320px;
border-left: none;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.left-panel.open {
transform: translateY(-50%) translateX(320px);
}
/* Panneau droit - Navigation */
.right-panel {
right: -320px;
width: 320px;
border-right: none;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.right-panel.open {
transform: translateY(-50%) translateX(-320px);
}
/* Contenu des panneaux */
.panel-content {
padding: 2rem;
}
.panel-header {
margin: 0 0 1.5rem 0;
color: var(--primary-color);
font-size: 1.2rem;
font-weight: 600;
display: flex;
align-items: center;
gap: 0.75rem;
border-bottom: 2px solid var(--border-color);
padding-bottom: 1rem;
}
/* Flèches d'ouverture */
.panel-toggle {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 40px;
height: 80px;
background: var(--surface-color);
border: 1px solid var(--border-color);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.2rem;
color: var(--text-color);
transition: all 0.3s ease;
box-shadow: var(--shadow);
z-index: 91;
}
.left-panel .panel-toggle {
right: -40px;
border-left: none;
border-radius: 0 var(--border-radius) var(--border-radius) 0;
}
.right-panel .panel-toggle {
left: -40px;
border-right: none;
border-radius: var(--border-radius) 0 0 var(--border-radius);
}
.panel-toggle:hover {
background: var(--secondary-color);
color: white;
transform: translateY(-50%) scale(1.05);
box-shadow: var(--shadow-hover);
}
.panel-toggle:active {
transform: translateY(-50%) scale(0.95);
}
/* Rotation de la flèche selon l'état */
.left-panel .panel-toggle .arrow {
transition: transform 0.3s ease;
}
.left-panel.open .panel-toggle .arrow {
transform: rotate(180deg);
}
.right-panel .panel-toggle .arrow {
transition: transform 0.3s ease;
}
.right-panel.open .panel-toggle .arrow {
transform: rotate(180deg);
}
/* Styles pour le formulaire de templates */
.template-form {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.form-group {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.form-group label {
font-size: 0.9rem;
color: var(--text-light);
font-weight: 500;
}
.form-group select {
padding: 0.75rem;
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
background: var(--surface-color);
color: var(--text-color);
font-size: 0.9rem;
cursor: pointer;
transition: var(--transition);
}
.form-group select:hover:not(:disabled) {
border-color: var(--secondary-color);
}
.form-group select:focus {
outline: none;
border-color: var(--secondary-color);
box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2);
}
.form-group select:disabled {
opacity: 0.5;
cursor: not-allowed;
background: var(--background-color);
}
.template-preview {
padding: 1rem;
background: var(--background-color);
border-radius: var(--border-radius);
border: 1px solid var(--border-color);
}
.template-preview h4 {
margin: 0 0 0.75rem 0;
color: var(--primary-color);
font-size: 0.95rem;
}
.preview-content {
font-size: 0.85rem;
color: var(--text-light);
line-height: 1.4;
}
.form-actions {
display: flex;
gap: 0.75rem;
flex-wrap: wrap;
}
.form-actions button {
flex: 1;
min-width: 120px;
}
/* Styles spécifiques au panneau de navigation */
.nav-section {
margin-bottom: 2rem;
}
.nav-section h4 {
margin: 0 0 1rem 0;
font-size: 0.95rem;
color: var(--text-light);
text-transform: uppercase;
letter-spacing: 0.5px;
}
.nav-buttons {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.nav-btn {
width: 100%;
padding: 1rem 1.5rem;
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
background: transparent;
color: var(--text-color);
cursor: pointer;
transition: var(--transition);
display: flex;
align-items: center;
gap: 1rem;
font-size: 0.9rem;
text-align: left;
}
.nav-btn:hover {
background: var(--background-color);
border-color: var(--secondary-color);
color: var(--secondary-color);
transform: translateX(5px);
}
.nav-btn .icon {
font-size: 1.2rem;
width: 24px;
text-align: center;
}
/* File input dans le panneau */
.panel-file-input {
width: 100%;
padding: 1rem 1.5rem;
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
background: transparent;
color: var(--text-color);
cursor: pointer;
transition: var(--transition);
display: flex;
align-items: center;
gap: 1rem;
font-size: 0.9rem;
text-align: left;
overflow: hidden;
}
.panel-file-input:hover {
background: var(--background-color);
border-color: var(--warning-color);
color: var(--warning-color);
transform: translateX(5px);
}
.panel-file-input .icon {
font-size: 1.2rem;
width: 24px;
text-align: center;
}
/* Animation d'overlay quand les panneaux sont ouverts */
.panel-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.1);
z-index: 89;
opacity: 0;
visibility: hidden;
transition: all 0.3s ease;
backdrop-filter: blur(2px);
}
.panel-overlay.active {
opacity: 1;
visibility: visible;
}
#skeleton-select {
flex: 1;
padding: 0.75rem 1rem;
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
background: var(--surface-color);
color: var(--text-color);
font-size: 0.9rem;
cursor: pointer;
transition: var(--transition);
}
#skeleton-select:hover {
border-color: var(--secondary-color);
}
#skeleton-select:focus {
outline: none;
border-color: var(--secondary-color);
box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2);
}
/* Responsive design */
@media (max-width: 768px) {
.side-panel {
width: 280px;
top: 60%;
}
.left-panel {
left: -280px;
}
.left-panel.open {
transform: translateY(-60%) translateX(280px);
}
.right-panel {
right: -280px;
}
.right-panel.open {
transform: translateY(-60%) translateX(-280px);
}
.panel-content {
padding: 1.5rem;
}
.panel-toggle {
width: 35px;
height: 70px;
font-size: 1rem;
}
header h1 {
font-size: 1.8rem;
}
header h1::before {
font-size: 2rem;
}
header > div {
padding: 1.5rem;
}
main {
grid-template-columns: 1fr;
padding: 1rem;
}
#table-of-contents,
#ai-assistant {
position: static;
max-height: none;
}
}
@media (max-width: 480px) {
.side-panel {
width: calc(100vw - 40px);
left: -100vw;
right: -100vw;
}
.left-panel.open {
transform: translateY(-60%) translateX(calc(100vw - 20px));
}
.right-panel.open {
transform: translateY(-60%) translateX(calc(-100vw + 20px));
}
}
/* Boutons */
button, .btn {
padding: 0.6rem 1.2rem;
border: none;
border-radius: 25px;
background: var(--secondary-color);
color: white;
font-size: 0.85rem;
font-weight: 600;
cursor: pointer;
transition: var(--transition);
display: inline-flex;
align-items: center;
gap: 0.5rem;
text-decoration: none;
text-transform: uppercase;
letter-spacing: 0.5px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
button:hover, .btn:hover {
background: #2980b9;
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
transform: translateY(-2px);
}
button:active, .btn:active {
transform: translateY(0);
}
/* Boutons dans le header */
header button, header .btn {
background: rgba(255,255,255,0.9);
color: var(--primary-color);
border: 1px solid rgba(255,255,255,0.3);
backdrop-filter: blur(10px);
}
header button:hover, header .btn:hover {
background: white;
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
color: var(--secondary-color);
}
button.secondary {
background: rgba(255,255,255,0.9);
color: var(--primary-color);
border: 1px solid rgba(255,255,255,0.3);
}
button.secondary:hover {
background: white;
color: var(--secondary-color);
}
button.danger {
background: var(--accent-color);
}
button.danger:hover {
background: #c0392b;
}
button.success {
background: var(--success-color);
}
button.success:hover {
background: #229954;
}
/* Input file personnalis<69> */
.file-input-label {
padding: 0.6rem 1.2rem;
border-radius: var(--border-radius);
background: var(--warning-color);
color: white;
font-size: 0.9rem;
font-weight: 500;
cursor: pointer;
transition: var(--transition);
display: inline-flex;
align-items: center;
gap: 0.5rem;
}
.file-input-label:hover {
background: #e67e22;
box-shadow: var(--shadow-hover);
transform: translateY(-1px);
}
/* Main content */
main {
display: grid;
grid-template-columns: 300px 1fr 350px;
gap: 2rem;
padding: 2rem;
max-width: 1400px;
margin: 0 auto;
min-height: calc(100vh - 140px);
}
@media (max-width: 1200px) {
main {
grid-template-columns: 250px 1fr;
gap: 1.5rem;
}
#ai-assistant {
grid-column: 1 / -1;
margin-top: 2rem;
}
}
@media (max-width: 768px) {
main {
grid-template-columns: 1fr;
gap: 1rem;
padding: 1rem;
}
}
/* Sections */
section {
background: var(--surface-color);
border-radius: var(--border-radius);
box-shadow: var(--shadow);
overflow: hidden;
}
section h2 {
background: var(--secondary-color);
color: white;
padding: 1rem 1.5rem;
margin: 0;
font-size: 1.2rem;
font-weight: 600;
}
/* Table des mati<74>res */
#table-of-contents {
position: sticky;
top: 100px;
max-height: calc(100vh - 120px);
overflow-y: auto;
}
#toc-nav {
padding: 1.5rem;
}
#toc-nav ul {
list-style: none;
}
#toc-nav li {
margin-bottom: 0.5rem;
}
#toc-nav a {
color: var(--text-color);
text-decoration: none;
padding: 0.3rem 0.5rem;
border-radius: 4px;
display: block;
transition: var(--transition);
font-size: 0.9rem;
}
#toc-nav a:hover {
background: var(--background-color);
color: var(--secondary-color);
}
#toc-nav ul ul {
margin-left: 1rem;
margin-top: 0.5rem;
}
#toc-nav ul ul a {
font-size: 0.85rem;
color: var(--text-light);
}
/* Zone d'<27>criture */
#design-journal {
min-height: 600px;
}
#journal-editor {
min-height: 500px;
padding: 2rem;
font-size: 1rem;
line-height: 1.8;
outline: none;
border: none;
background: var(--surface-color);
color: var(--text-color);
resize: vertical;
white-space: pre-wrap;
word-wrap: break-word;
overflow-wrap: break-word;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
}
#journal-editor:focus {
background: var(--background-color);
}
#journal-editor:empty::before {
content: "Commencez à écrire votre journal de conception...\A\A# Titre de votre projet\A\A## Contexte et objectifs\A\ADécrivez ici le contexte de votre projet...\A\A## Architecture\A\A### Composants principaux\A\A### Technologies utilisées\A\A## Décisions de conception\A\A### Décision 1\A\A**Problème :**\A**Options considérées :**\A**Décision :**\A**Justification :**\A\A## Prochaines étapes\A\A- [ ] Tâche 1\A- [ ] Tâche 2";
color: var(--text-light);
font-style: italic;
white-space: pre-wrap;
pointer-events: none;
}
#journal-editor p {
margin-bottom: 1rem;
}
#journal-editor h1, #journal-editor h2, #journal-editor h3 {
color: var(--primary-color);
margin: 1.5rem 0 1rem 0;
}
#journal-editor h1 {
font-size: 2rem;
border-bottom: 3px solid var(--secondary-color);
padding-bottom: 0.5rem;
}
#journal-editor h2 {
font-size: 1.6rem;
border-bottom: 2px solid var(--border-color);
padding-bottom: 0.3rem;
}
#journal-editor h3 {
font-size: 1.3rem;
color: var(--secondary-color);
}
/* Assistant IA */
#ai-assistant {
position: sticky;
top: 100px;
max-height: calc(100vh - 120px);
display: flex;
flex-direction: column;
}
/* Styles pour "Coming Soon" */
.ai-coming-soon {
padding: 1.5rem;
background: linear-gradient(135deg, var(--background-color), var(--surface-color));
}
.coming-soon-badge {
display: inline-flex;
align-items: center;
gap: 0.5rem;
background: linear-gradient(135deg, var(--warning-color), #f39c12);
color: white;
padding: 0.5rem 1rem;
border-radius: 50px;
font-size: 0.85rem;
font-weight: 600;
margin-bottom: 1.5rem;
box-shadow: 0 2px 8px rgba(243, 156, 18, 0.3);
animation: glow 2s ease-in-out infinite alternate;
}
@keyframes glow {
from { box-shadow: 0 2px 8px rgba(243, 156, 18, 0.3); }
to { box-shadow: 0 4px 16px rgba(243, 156, 18, 0.5); }
}
.badge-icon {
font-size: 1rem;
animation: bounce 1s ease-in-out infinite;
}
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-2px); }
}
.ai-preview h3 {
color: var(--primary-color);
font-size: 1rem;
margin: 0 0 1rem 0;
font-weight: 600;
}
.ai-features-list {
list-style: none;
padding: 0;
margin: 0 0 1.5rem 0;
}
.ai-features-list li {
padding: 0.75rem 0;
border-bottom: 1px solid var(--border-color);
font-size: 0.9rem;
line-height: 1.4;
display: flex;
align-items: flex-start;
gap: 0.5rem;
}
.ai-features-list li:last-child {
border-bottom: none;
}
.ai-tech-stack {
background: var(--surface-color);
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
padding: 1rem;
margin-top: 1rem;
}
.ai-tech-stack p {
margin: 0.25rem 0;
font-size: 0.85rem;
color: var(--text-light);
}
.ai-tech-stack strong {
color: var(--primary-color);
}
#ai-assistant-feedback {
flex: 1;
padding: 1.5rem;
border-top: 1px solid var(--border-color);
background: var(--background-color);
overflow-y: auto;
min-height: 200px;
}
.feedback-message {
background: var(--surface-color);
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
padding: 1rem;
margin-bottom: 1rem;
font-size: 0.9rem;
line-height: 1.5;
}
.coming-soon-message {
background: linear-gradient(135deg, var(--surface-color), var(--background-color));
border: 2px dashed var(--warning-color);
text-align: center;
}
.feedback-message.loading {
background: var(--background-color);
border-color: var(--secondary-color);
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.7; }
}
/* Footer */
footer {
background: var(--secondary-color);
color: white;
text-align: center;
padding: 2rem;
margin-top: 3rem;
}
footer a {
color: white;
text-decoration: underline;
}
footer a:hover {
color: var(--secondary-color);
}
/* <20>tats de chargement */
.loading {
position: relative;
opacity: 0.7;
pointer-events: none;
}
.loading::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 20px;
height: 20px;
margin: -10px 0 0 -10px;
border: 2px solid var(--border-color);
border-top: 2px solid var(--secondary-color);
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Notifications */
.notification {
position: fixed;
top: 20px;
right: 20px;
padding: 1rem 1.5rem;
background: var(--success-color);
color: white;
border-radius: var(--border-radius);
box-shadow: var(--shadow-hover);
z-index: 1000;
transform: translateX(400px);
transition: var(--transition);
}
.notification.show {
transform: translateX(0);
}
.notification.error {
background: var(--accent-color);
}
.notification.warning {
background: var(--warning-color);
}
/* Responsive design */
@media (max-width: 768px) {
header > div {
flex-direction: column;
gap: 1rem;
}
.header-controls {
flex-wrap: wrap;
justify-content: center;
}
nav {
flex-wrap: wrap;
justify-content: center;
}
button, .btn {
padding: 0.5rem 1rem;
font-size: 0.85rem;
}
}
/* Scrollbars personnalis<69>es */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: var(--background-color);
}
::-webkit-scrollbar-thumb {
background: var(--border-color);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--text-light);
}
/* S<>lection de texte */
::selection {
background: var(--secondary-color);
color: white;
}
/* Focus outline */
button:focus-visible,
input:focus-visible,
select:focus-visible {
outline: 2px solid var(--secondary-color);
outline-offset: 2px;
}
/* Utilitaires */
.hidden {
display: none !important;
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
}
.text-center {
text-align: center;
}
.mb-1 { margin-bottom: 0.5rem; }
.mb-2 { margin-bottom: 1rem; }
.mb-3 { margin-bottom: 1.5rem; }
.mt-1 { margin-top: 0.5rem; }
.mt-2 { margin-top: 1rem; }
.mt-3 { margin-top: 1.5rem; }

583
assets/js/app.js Normal file
View File

@ -0,0 +1,583 @@
// 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 `<button class="journal-item btn secondary mb-1" data-id="${journal.id}">${preview}...</button>`;
}).join('');
const feedback = document.getElementById('ai-assistant-feedback');
feedback.innerHTML = `
<div class="feedback-message">
<h3>📂 Sélectionner un journal</h3>
<div class="journal-list">
<button class="btn mb-2" onclick="app.createNewJournal()">+ Nouveau journal</button>
${journalList}
</div>
</div>
`;
// 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 = '<ul>';
let currentLevel = 0;
for (const item of toc) {
if (item.level > currentLevel) {
for (let i = currentLevel; i < item.level - 1; i++) {
tocHtml += '<ul>';
}
} else if (item.level < currentLevel) {
for (let i = item.level; i < currentLevel; i++) {
tocHtml += '</ul>';
}
}
tocHtml += `<li><a href="#${item.id}" onclick="app.scrollToHeading('${item.id}')">${item.title}</a></li>`;
currentLevel = item.level;
}
tocHtml += '</ul>';
} else {
tocHtml = '<div class="toc-placeholder"><p>Ajoutez des titres (# ## ###) à votre journal pour générer la table des matières.</p></div>';
}
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:<br><em>"${selection.substring(0, 100)}..."</em><br><br><strong>Version reformulée :</strong><br>"${this.simulateRephrase(selection)}"`;
}
break;
case 'inconsistencies':
message = '🔍 Analyse des incohérences...<br><br>✅ Aucune incohérence majeure détectée dans le document.';
break;
case 'duplications':
message = '📋 Vérification des duplications...<br><br>✅ Aucune duplication significative trouvée.';
break;
case 'advice':
message = '💡 Conseils pour améliorer votre journal :<br><br>• Ajoutez plus de détails sur les alternatives considérées<br>• Documentez les raisons des choix techniques<br>• Incluez des diagrammes ou schémas<br>• Ajoutez une section "Lessons learned"';
break;
case 'liberty':
const count = document.getElementById('liberty-repeat-count').value || 3;
message = `🚀 Mode Liberté activé (${count} itérations)<br><br>Génération de contenus automatiques basée sur le contexte existant...<br><br><em>Cette fonctionnalité nécessite une intégration IA complète.</em>`;
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 = `<div class="feedback-message">${message}</div>`;
}
clearFeedback() {
const feedback = document.getElementById('ai-assistant-feedback');
feedback.innerHTML = `
<div class="feedback-message">
<strong>🎯 Assistant prêt</strong><br>
Sélectionnez du texte dans l'éditeur et cliquez sur une action pour commencer.
</div>
`;
}
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 = '<option value="">-- Choisir d\'abord un domaine --</option>';
loadTemplateBtn.disabled = true;
templatePreview.style.display = 'none';
return;
}
// Activer le select de niveau
levelSelect.disabled = false;
levelSelect.innerHTML = `
<option value="">-- Choisir le niveau --</option>
<option value="simple">📄 Simple</option>
<option value="detaille">📋 Détaillé</option>
<option value="complet">📚 Complet</option>
`;
// 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 = '<option value="">-- Choisir d\'abord un domaine --</option>';
templatePreview.style.display = 'none';
loadTemplateBtn.disabled = true;
});
}
}

View File

@ -1,2 +1,21 @@
# Server Configuration
PORT=3000
PORT=3000
# Mistral AI Configuration
MISTRAL_API_KEY=your_mistral_api_key_here
MISTRAL_MODEL=mistral-large-latest
MISTRAL_BASE_URL=https://api.mistral.ai/v1
# AI Features Configuration
AI_ENABLED=false
AI_MAX_TOKENS=4000
AI_TEMPERATURE=0.7
AI_TOP_P=0.95
# Rate Limiting for AI
AI_RATE_LIMIT_REQUESTS=10
AI_RATE_LIMIT_WINDOW=60000
# PDF Export Configuration
PDF_MAX_SIZE=10485760
PDF_TIMEOUT=30000

View File

@ -7,7 +7,8 @@
"test": "node tests/all.test.js"
},
"dependencies": {
"uuid": "^13.0.0",
"express": "^4.18.2"
"express": "^4.18.2",
"puppeteer": "^24.22.3",
"uuid": "^13.0.0"
}
}
}

View File

@ -4,6 +4,9 @@ const fs = require('fs');
const path = require('path');
const { v4: uuidv4 } = require('uuid');
// Importer le module d'export
const exportRouter = require('./export');
function modifMd(id, modifications) {
if (id === undefined) throw new Error('id obligatoire');
if (!Array.isArray(modifications) || modifications.length === 0) throw new Error('modifications requises');
@ -101,7 +104,7 @@ function readMd(id = undefined) {
}
}
function modifMd(id, newMarkdownContent) {
function updateMd(id, newMarkdownContent) {
if (id === undefined) throw new Error('id obligatoire');
const dataDir = path.resolve(__dirname, '../data');
const mapPath = path.join(dataDir, 'uuid_map.json');
@ -112,7 +115,7 @@ function modifMd(id, newMarkdownContent) {
if (!uuid) throw new Error(`Aucun fichier trouvé pour l'id ${id}`);
const mdPath = path.join(dataDir, `${uuid}.md`);
if (!fs.existsSync(mdPath)) throw new Error('Le fichier markdown nexiste pas');
if (!fs.existsSync(mdPath)) throw new Error('Le fichier markdown n\'existe pas');
fs.writeFileSync(mdPath, newMarkdownContent, { encoding: 'utf8', flag: 'w' });
return { id, uuid, path: mdPath, newMarkdownContent };
}
@ -166,12 +169,33 @@ router.get('/journals/:id', (req, res) => {
// PUT /api/journals/:id - Mettre à jour un journal
router.put('/journals/:id', (req, res) => {
const { id } = req.params;
const { start, end, content } = req.body;
const { content, modifications } = req.body;
res.json({
success: true,
data: modifMd(id, {start, end, content})
});
try {
let result;
if (content) {
// Mise à jour complète du contenu
result = updateMd(id, content);
} else if (modifications) {
// Modifications partielles (pour compatibilité future)
result = modifMd(id, modifications);
} else {
return res.status(400).json({
success: false,
error: 'Content ou modifications requis'
});
}
res.json({
success: true,
data: result
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// DELETE /api/journals/:id - Supprimer un journal
@ -184,4 +208,7 @@ router.delete('/journals/:id', (req, res) => {
});
});
// Intégrer les routes d'export
router.use('/export', exportRouter);
module.exports = router;

512
routes/export.js Normal file
View File

@ -0,0 +1,512 @@
const express = require('express');
const router = express.Router();
const puppeteer = require('puppeteer');
const path = require('path');
// POST /api/export/pdf - Générer un PDF depuis le contenu markdown
router.post('/pdf', async (req, res) => {
const { content, title = 'Journal de Conception' } = req.body;
if (!content || content.trim() === '') {
return res.status(400).json({
success: false,
error: 'Contenu requis pour générer le PDF'
});
}
let browser;
try {
// Convertir le markdown en HTML avec styles
const htmlContent = generateStyledHTML(content, title);
// Lancer Puppeteer
browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox']
});
const page = await browser.newPage();
// Définir le contenu HTML
await page.setContent(htmlContent, {
waitUntil: 'domcontentloaded',
timeout: parseInt(process.env.PDF_TIMEOUT) || 30000
});
// Attendre que Mermaid soit chargé et les diagrammes rendus
await new Promise(resolve => setTimeout(resolve, 2000));
// Vérifier si des diagrammes Mermaid existent et attendre qu'ils se chargent
try {
await page.waitForFunction(() => {
const mermaidElements = document.querySelectorAll('.mermaid');
if (mermaidElements.length === 0) return true;
return Array.from(mermaidElements).every(el =>
el.querySelector('svg') || el.textContent.trim() === '' || el.classList.contains('error')
);
}, { timeout: 10000 });
} catch (e) {
console.log('Timeout waiting for Mermaid, proceeding...');
}
// Générer le PDF
const pdfBuffer = await page.pdf({
format: 'A4',
margin: {
top: '2cm',
right: '2cm',
bottom: '2cm',
left: '2cm'
},
printBackground: true,
displayHeaderFooter: true,
headerTemplate: `
<div style="font-size: 10px; width: 100%; text-align: center; color: #666; margin-top: 1cm;">
<span>${title}</span>
</div>
`,
footerTemplate: `
<div style="font-size: 10px; width: 100%; text-align: center; color: #666; margin-bottom: 1cm;">
<span>Page <span class="pageNumber"></span> sur <span class="totalPages"></span> - Généré le ${new Date().toLocaleDateString('fr-FR')}</span>
</div>
`
});
await browser.close();
// Vérifier la taille du PDF
const maxSize = parseInt(process.env.PDF_MAX_SIZE) || 10485760; // 10MB par défaut
if (pdfBuffer.length > maxSize) {
return res.status(413).json({
success: false,
error: 'Le PDF généré dépasse la taille maximale autorisée'
});
}
// Envoyer le PDF
res.setHeader('Content-Type', 'application/pdf');
res.setHeader('Content-Disposition', `attachment; filename="${sanitizeFilename(title)}.pdf"`);
res.setHeader('Content-Length', pdfBuffer.length);
res.send(pdfBuffer);
} catch (error) {
console.error('Erreur génération PDF:', error);
if (browser) {
await browser.close();
}
res.status(500).json({
success: false,
error: 'Erreur lors de la génération du PDF'
});
}
});
// Fonction utilitaire pour échapper le HTML
function escapeHtml(text) {
const map = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#039;'
};
return text.replace(/[&<>"']/g, function(m) { return map[m]; });
}
// Fonction pour convertir le markdown en HTML stylé avec support Mermaid
function generateStyledHTML(content, title) {
let html = content;
// Traitement des diagrammes Mermaid AVANT les blocs de code
html = html.replace(/```mermaid\n([\s\S]*?)```/g, '<div class="mermaid">$1</div>');
// Traitement des blocs de code avec langage
html = html.replace(/```(\w+)?\n([\s\S]*?)```/g, (match, lang, code) => {
const language = lang ? ` class="language-${lang}"` : '';
return `<pre><code${language}>${escapeHtml(code)}</code></pre>`;
});
// Code en ligne
html = html.replace(/`([^`]+)`/g, '<code>$1</code>');
// Traitement des tableaux amélioré
html = html.replace(/(\|.*\|\s*\n)+/g, (match) => {
const lines = match.trim().split('\n').filter(line => line.trim());
let tableHtml = '<table>\n';
let inHeader = true;
lines.forEach((line, index) => {
// Ignorer les lignes de séparation
if (line.match(/^\s*\|[\s\-:]*\|\s*$/)) {
inHeader = false;
return;
}
const cells = line.split('|')
.map(cell => cell.trim())
.filter((cell, idx, arr) => idx > 0 && idx < arr.length - 1);
if (cells.length === 0) return;
const tag = inHeader ? 'th' : 'td';
tableHtml += ' <tr>\n';
cells.forEach(cell => {
tableHtml += ` <${tag}>${cell}</${tag}>\n`;
});
tableHtml += ' </tr>\n';
if (inHeader && index === 0) inHeader = false;
});
tableHtml += '</table>\n';
return tableHtml;
});
// Conversion markdown vers HTML
html = html
// Titres (en ordre décroissant)
.replace(/^#{6}\s+(.*$)/gm, '<h6>$1</h6>')
.replace(/^#{5}\s+(.*$)/gm, '<h5>$1</h5>')
.replace(/^#{4}\s+(.*$)/gm, '<h4>$1</h4>')
.replace(/^#{3}\s+(.*$)/gm, '<h3>$1</h3>')
.replace(/^#{2}\s+(.*$)/gm, '<h2>$1</h2>')
.replace(/^#{1}\s+(.*$)/gm, '<h1>$1</h1>')
// Citations
.replace(/^>\s+(.*$)/gm, '<blockquote>$1</blockquote>')
// Gras et italique (ordre important)
.replace(/\*\*\*(.*?)\*\*\*/g, '<strong><em>$1</em></strong>')
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
.replace(/\*(.*?)\*/g, '<em>$1</em>')
// Barré
.replace(/~~(.*?)~~/g, '<del>$1</del>')
// Listes numérotées
.replace(/^(\s*)(\d+)\. (.*$)/gm, '<li class="numbered">$3</li>')
// Listes à puces et tâches
.replace(/^(\s*)- \[x\] (.*$)/gm, '<li class="todo-done">✅ $2</li>')
.replace(/^(\s*)- \[ \] (.*$)/gm, '<li class="todo">☐ $2</li>')
.replace(/^(\s*)[*\-+] (.*$)/gm, '<li>$2</li>')
// Liens
.replace(/\[([^\]]+)\]\(([^\)]+)\)/g, '<a href="$2" target="_blank">$1</a>')
// Ligne horizontale
.replace(/^---+$/gm, '<hr>');
// Grouper les listes consécutives
html = html.replace(/(<li[^>]*>.*?<\/li>\s*)+/gs, (match) => {
if (match.includes('class="numbered"')) {
return '<ol>' + match.replace(/ class="numbered"/g, '') + '</ol>';
} else {
return '<ul>' + match + '</ul>';
}
});
// Nettoyer les listes multiples consécutives
html = html.replace(/(<\/[uo]l>\s*<[uo]l>)/g, '');
// Grouper les citations consécutives
html = html.replace(/(<blockquote>.*?<\/blockquote>\s*)+/gs, (match) => {
const content = match.replace(/<\/?blockquote>/g, '');
return '<blockquote>' + content + '</blockquote>';
});
// Traiter les paragraphes
html = html.split('\n\n').map(paragraph => {
paragraph = paragraph.trim();
if (!paragraph) return '';
// Ne pas entourer les éléments de bloc dans des paragraphes
if (paragraph.match(/^<(h[1-6]|div|table|ul|ol|blockquote|hr|pre)/)) {
return paragraph;
}
return '<p>' + paragraph.replace(/\n/g, '<br>') + '</p>';
}).join('\n\n');
const styledHTML = `
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>${title}</title>
<script src="https://cdn.jsdelivr.net/npm/mermaid@10.6.1/dist/mermaid.min.js"></script>
<script>
mermaid.initialize({
startOnLoad: true,
theme: 'default',
themeVariables: {
primaryColor: '#0969da',
primaryTextColor: '#24292f',
primaryBorderColor: '#0969da',
lineColor: '#656d76',
sectionBkgColor: '#f6f8fa',
altSectionBkgColor: '#ffffff',
gridColor: '#d1d9e0',
secondaryColor: '#f6f8fa',
tertiaryColor: '#0969da'
},
fontFamily: '-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, sans-serif',
fontSize: 14
});
</script>
<style>
@page {
size: A4;
margin: 2cm;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif;
line-height: 1.6;
color: #24292f;
font-size: 11pt;
background: white;
word-wrap: break-word;
}
/* Titres style GitHub */
h1 {
color: #24292f;
font-size: 2em;
font-weight: 600;
margin: 0 0 16pt 0;
padding-bottom: 8pt;
border-bottom: 1px solid #d1d9e0;
page-break-after: avoid;
}
h2 {
color: #24292f;
font-size: 1.5em;
font-weight: 600;
margin: 24pt 0 12pt 0;
padding-bottom: 6pt;
border-bottom: 1px solid #d1d9e0;
page-break-after: avoid;
}
h3 {
color: #24292f;
font-size: 1.25em;
font-weight: 600;
margin: 18pt 0 8pt 0;
page-break-after: avoid;
}
h4 {
color: #24292f;
font-size: 1em;
font-weight: 600;
margin: 16pt 0 6pt 0;
page-break-after: avoid;
}
h5, h6 {
color: #24292f;
font-size: 0.875em;
font-weight: 600;
margin: 14pt 0 6pt 0;
page-break-after: avoid;
}
/* Paragraphes */
p {
margin: 0 0 12pt 0;
orphans: 2;
widows: 2;
}
/* Listes */
ul, ol {
margin: 0 0 12pt 0;
padding-left: 2em;
}
ul {
list-style-type: disc;
}
ol {
list-style-type: decimal;
}
li {
margin: 0 0 4pt 0;
line-height: 1.5;
}
/* Tâches */
li.todo {
list-style: none;
margin-left: -2em;
padding-left: 1.5em;
}
li.todo-done {
list-style: none;
margin-left: -2em;
padding-left: 1.5em;
color: #656d76;
}
/* Mise en forme du texte */
strong {
font-weight: 600;
}
em {
font-style: italic;
}
del {
text-decoration: line-through;
color: #656d76;
}
/* Liens */
a {
color: #0969da;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
/* Citations */
blockquote {
margin: 12pt 0;
padding: 0 12pt;
border-left: 4px solid #d1d9e0;
color: #656d76;
}
blockquote > :first-child {
margin-top: 0;
}
blockquote > :last-child {
margin-bottom: 0;
}
/* Ligne horizontale */
hr {
margin: 24pt 0;
border: none;
border-top: 1px solid #d1d9e0;
height: 0;
}
/* Tableaux style GitHub */
table {
border-collapse: collapse;
width: 100%;
margin: 12pt 0;
font-size: 10.5pt;
border: 1px solid #d1d9e0;
}
th, td {
border: 1px solid #d1d9e0;
padding: 6pt 10pt;
text-align: left;
}
th {
background-color: #f6f8fa;
font-weight: 600;
}
tr:nth-child(even) {
background-color: #f6f8fa;
}
/* Code style GitHub */
code {
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
background-color: rgba(175, 184, 193, 0.2);
padding: 1pt 4pt;
border-radius: 3pt;
font-size: 85%;
}
pre {
background-color: #f6f8fa;
padding: 12pt;
border-radius: 6pt;
border: 1px solid #d1d9e0;
margin: 12pt 0;
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
font-size: 85%;
line-height: 1.45;
overflow: visible;
}
pre code {
background: transparent;
padding: 0;
border-radius: 0;
font-size: inherit;
}
/* Diagrammes Mermaid */
.mermaid {
text-align: center;
margin: 16pt 0;
page-break-inside: avoid;
}
.mermaid svg {
max-width: 100%;
height: auto;
}
/* Éviter les coupures de page */
h1, h2, h3, h4, h5, h6 {
page-break-after: avoid;
}
p, li {
page-break-inside: avoid;
orphans: 2;
widows: 2;
}
pre, table {
page-break-inside: avoid;
}
/* Améliorer la lisibilité */
body {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
</style>
</head>
<body>
${html}
</body>
</html>
`;
return styledHTML;
}
// Fonction pour nettoyer le nom de fichier
function sanitizeFilename(filename) {
return filename
.replace(/[^\w\s-]/g, '') // Supprimer caractères spéciaux
.replace(/\s+/g, '-') // Remplacer espaces par tirets
.toLowerCase() // Minuscules
.substring(0, 100); // Limiter la longueur
}
module.exports = router;

View File

@ -9,6 +9,9 @@ router.get('/', (req, res) => {
// Route à propos
router.get('/about', (req, res) => {
const { getHeader } = require('../views/header');
const { getFooter } = require('../views/footer');
res.send(`
<!DOCTYPE html>
<html lang="fr">
@ -19,31 +22,60 @@ router.get('/about', (req, res) => {
<link rel="stylesheet" href="/assets/css/style.css">
</head>
<body>
<h1>Journal de Conception</h1>
<p>
Cette application aide les équipes à réaliser un suivi structuré et collaboratif de la conception de leurs projets. Elle permet darchiver les étapes clés, dassurer la traçabilité des décisions, et de simplifier la coordination.
</p>
<h2>Historique</h2>
<p>
Ce projet est du besoin de centraliser et dorganiser les notes de conception lors du développement de projets techniques.
</p>
<h2>Équipe</h2>
<ul>
<li>Augustin ROUX Développeur et Concepteur principal</li>
</ul>
<h2>Contact</h2>
<p>
Email : augustin.j.l.roux@gmail.com<br>
Dépôt Git : https://gitea.legion-muyue.fr/Muyue/conception-assistant
</p>
<footer>
<p>
© 2025 Journal de Conception.
</p>
</footer>
${getHeader()}
<main style="display: block; max-width: 800px; margin: 0 auto; padding: 2rem;">
<section>
<h2>À propos de l'application</h2>
<div style="padding: 2rem;">
<p>
Cette application aide les équipes à réaliser un suivi structuré et collaboratif de la conception de leurs projets. Elle permet d'archiver les étapes clés, d'assurer la traçabilité des décisions, et de simplifier la coordination.
</p>
</div>
</section>
<section style="margin-top: 2rem;">
<h2>Historique</h2>
<div style="padding: 2rem;">
<p>
Ce projet est du besoin de centraliser et d'organiser les notes de conception lors du développement de projets techniques. Il offre un environnement intuitif pour documenter les décisions architecturales et suivre l'évolution des projets.
</p>
</div>
</section>
<section style="margin-top: 2rem;">
<h2>Équipe</h2>
<div style="padding: 2rem;">
<ul style="list-style: none; padding: 0;">
<li style="padding: 0.5rem 0; border-bottom: 1px solid var(--border-color);">
<strong>Augustin ROUX</strong> Développeur et Concepteur principal
</li>
</ul>
</div>
</section>
<section style="margin-top: 2rem;">
<h2>Contact</h2>
<div style="padding: 2rem;">
<p>
<strong>Email :</strong> <a href="mailto:augustin.j.l.roux@gmail.com" style="color: var(--secondary-color);">augustin.j.l.roux@gmail.com</a><br><br>
<strong>Dépôt Git :</strong> <a href="https://gitea.legion-muyue.fr/Muyue/conception-assistant" target="_blank" style="color: var(--secondary-color);">https://gitea.legion-muyue.fr/Muyue/conception-assistant</a>
</p>
</div>
</section>
<div style="text-align: center; margin: 3rem 0 2rem 0;">
<a href="/" style="background: var(--secondary-color); color: white; padding: 0.8rem 2rem; text-decoration: none; border-radius: 25px; display: inline-block; transition: all 0.3s ease;">
Retour à l'application
</a>
</div>
</main>
${getFooter()}
<script src="/assets/js/theme.js"></script>
</body>
</html>
`);
});

117
routes/templates.js Normal file
View File

@ -0,0 +1,117 @@
const express = require('express');
const router = express.Router();
const fs = require('fs');
const path = require('path');
// GET /api/templates/:domain/:level - Récupérer un template spécifique
router.get('/:domain/:level', (req, res) => {
const { domain, level } = req.params;
try {
const templatePath = path.join(__dirname, '../templates', domain, `${level}.md`);
if (!fs.existsSync(templatePath)) {
return res.status(404).json({
success: false,
error: `Template non trouvé : ${domain}/${level}`
});
}
const templateContent = fs.readFileSync(templatePath, 'utf8');
res.json({
success: true,
data: {
domain,
level,
content: templateContent
}
});
} catch (error) {
console.error('Erreur lors de la lecture du template:', error);
res.status(500).json({
success: false,
error: 'Erreur serveur lors de la lecture du template'
});
}
});
// GET /api/templates/:domain - Lister les niveaux disponibles pour un domaine
router.get('/:domain', (req, res) => {
const { domain } = req.params;
try {
const domainPath = path.join(__dirname, '../templates', domain);
if (!fs.existsSync(domainPath)) {
return res.status(404).json({
success: false,
error: `Domaine non trouvé : ${domain}`
});
}
const files = fs.readdirSync(domainPath);
const levels = files
.filter(file => file.endsWith('.md'))
.map(file => file.replace('.md', ''));
res.json({
success: true,
data: {
domain,
levels
}
});
} catch (error) {
console.error('Erreur lors de la lecture du domaine:', error);
res.status(500).json({
success: false,
error: 'Erreur serveur lors de la lecture du domaine'
});
}
});
// GET /api/templates - Lister tous les domaines disponibles
router.get('/', (req, res) => {
try {
const templatesPath = path.join(__dirname, '../templates');
if (!fs.existsSync(templatesPath)) {
return res.json({
success: true,
data: []
});
}
const domains = fs.readdirSync(templatesPath, { withFileTypes: true })
.filter(dirent => dirent.isDirectory())
.map(dirent => dirent.name);
// Pour chaque domaine, récupérer les niveaux disponibles
const domainsWithLevels = domains.map(domain => {
const domainPath = path.join(templatesPath, domain);
const files = fs.readdirSync(domainPath);
const levels = files
.filter(file => file.endsWith('.md'))
.map(file => file.replace('.md', ''));
return {
domain,
levels
};
});
res.json({
success: true,
data: domainsWithLevels
});
} catch (error) {
console.error('Erreur lors de la lecture des templates:', error);
res.status(500).json({
success: false,
error: 'Erreur serveur lors de la lecture des templates'
});
}
});
module.exports = router;

View File

@ -0,0 +1,97 @@
# Projet Business
## 1. Présentation du projet
### Contexte
[Description du contexte économique/business]
### Opportunité
[Opportunité de marché identifiée]
### Proposition de valeur
[En quoi votre solution est unique]
## 2. Analyse de marché
### Marché cible
- Segment principal :
- Taille du marché :
- Tendances :
### Concurrence
- **Concurrent 1** : [Forces/Faiblesses]
- **Concurrent 2** : [Forces/Faiblesses]
### Positionnement
[Comment vous vous différenciez]
## 3. Modèle économique
### Sources de revenus
- Revenue stream 1 :
- Revenue stream 2 :
### Structure de coûts
- Coûts fixes :
- Coûts variables :
### Pricing
[Stratégie de tarification]
## 4. Stratégie
### Objectifs
- Court terme (6 mois) :
- Moyen terme (18 mois) :
- Long terme (3 ans) :
### Stratégie marketing
- Canaux d'acquisition :
- Message marketing :
### Stratégie opérationnelle
- Ressources clés :
- Partenaires :
## 5. Planning
### Phase 1 : Lancement (3 mois)
- [ ] Développement MVP
- [ ] Tests utilisateurs
- [ ] Go-to-market
### Phase 2 : Croissance (6 mois)
- [ ] Acquisition clients
- [ ] Amélioration produit
- [ ] Expansion équipe
### Phase 3 : Scale (12 mois)
- [ ] Expansion géographique
- [ ] Nouveaux produits
- [ ] Levée de fonds
## 6. Risques et opportunités
### Risques
- Risque 1 :
- Risque 2 :
### Opportunités
- Opportunité 1 :
- Opportunité 2 :
## 7. Métriques clés
### KPIs business
- Chiffre d'affaires :
- Nombre de clients :
- Coût d'acquisition :
### KPIs produit
- Utilisateurs actifs :
- Rétention :
- NPS :
## 8. Notes et actions
[Espace pour suivi des actions et notes]

View File

@ -0,0 +1,96 @@
# Projet Design
## 1. Brief créatif
### Contexte du projet
[Description du projet et du client]
### Objectifs
- Objectif principal :
- Cible :
- Message à transmettre :
### Contraintes
- Budget :
- Délais :
- Contraintes techniques :
## 2. Recherche et inspiration
### Analyse de la concurrence
[Étude des solutions existantes]
### Mood board
[Sources d'inspiration visuelles]
### Références
- Style graphique :
- Palette couleur :
- Typographie :
## 3. Concept créatif
### Idée directrice
[Concept principal du design]
### Parti pris esthétique
- Style : [Moderne, classique, minimaliste...]
- Ambiance : [Chaleureuse, professionnelle, fun...]
- Tonalité : [Sérieuse, décalée, premium...]
## 4. Éléments graphiques
### Palette de couleurs
- Couleur principale : #XXXXXX
- Couleur secondaire : #XXXXXX
- Couleurs d'accent : #XXXXXX, #XXXXXX
### Typographie
- Titre : [Police]
- Texte : [Police]
- Accent : [Police]
### Iconographie
- Style d'icônes :
- Illustrations :
- Photos :
## 5. Déclinaisons
### Supports à créer
- [ ] Logo et identité
- [ ] Site web
- [ ] Print
- [ ] Réseaux sociaux
- [ ] Packaging
### Guidelines
[Règles d'utilisation des éléments graphiques]
## 6. Validation et itérations
### Feedbacks client
[Retours et ajustements demandés]
### Tests utilisateurs
[Si applicable - tests d'utilisabilité]
## 7. Livrables
### Fichiers sources
- [ ] Fichiers AI/PSD
- [ ] Fichiers vectoriels
- [ ] Polices
### Exports
- [ ] PNG/JPG haute définition
- [ ] Versions web
- [ ] Versions print
### Documentation
- [ ] Guide de style
- [ ] Spécifications techniques
## 8. Notes créatives
[Espace pour notes et idées créatives]

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,386 @@
# Projet Informatique - Spécifications Détaillées
## 1. Introduction
### 1.1 Contexte et problématique
[Description du contexte métier et des problèmes à résoudre]
### 1.2 Objectifs du projet
- **Objectif principal** :
- **Objectifs secondaires** :
- **Critères de réussite** :
### 1.3 Périmètre
#### Inclus dans le projet
-
-
-
#### Exclus du projet
-
-
-
### 1.4 Contraintes
- **Techniques** :
- **Temporelles** :
- **Budgétaires** :
- **Réglementaires** :
## 2. Analyse des besoins
### 2.1 Besoins fonctionnels
#### RF-001 : [Nom du besoin]
- **Description** :
- **Acteur** :
- **Priorité** : Haute/Moyenne/Basse
- **Critères d'acceptation** :
#### RF-002 : [Nom du besoin]
- **Description** :
- **Acteur** :
- **Priorité** :
- **Critères d'acceptation** :
### 2.2 Besoins non-fonctionnels
#### Performance
- Temps de réponse : < 2 secondes
- Charge supportée : X utilisateurs simultanés
- Disponibilité : 99.9%
#### Sécurité
- Authentification : OAuth 2.0
- Autorisation : RBAC
- Chiffrement : TLS 1.3
#### Compatibilité
- Navigateurs : Chrome, Firefox, Safari, Edge
- Appareils : Desktop, Mobile, Tablette
- OS : Windows, macOS, Linux
## 3. Architecture système
### 3.1 Architecture générale
[Diagramme d'architecture global]
### 3.2 Stack technique
#### Frontend
- **Framework** : React 18 / Vue 3 / Angular 15
- **État** : Redux / Pinia / NgRx
- **UI** : Material-UI / Vuetify / Angular Material
- **Build** : Vite / Webpack
- **Tests** : Jest + Testing Library
#### Backend
- **Framework** : Node.js + Express / Python + FastAPI / Java + Spring
- **API** : REST / GraphQL
- **Authentification** : JWT / OAuth2
- **Tests** : Jest / pytest / JUnit
#### Base de données
- **Principale** : PostgreSQL / MySQL / MongoDB
- **Cache** : Redis
- **Recherche** : Elasticsearch (si nécessaire)
#### Infrastructure
- **Cloud** : AWS / Azure / GCP
- **Conteneurs** : Docker + Kubernetes
- **CI/CD** : GitHub Actions / GitLab CI
- **Monitoring** : Prometheus + Grafana
### 3.3 Modèle de données
#### Entités principales
```sql
-- Utilisateurs
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
role VARCHAR(50) DEFAULT 'user',
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
-- Contenu principal
CREATE TABLE content (
id SERIAL PRIMARY KEY,
title VARCHAR(255) NOT NULL,
description TEXT,
user_id INTEGER REFERENCES users(id),
status VARCHAR(50) DEFAULT 'draft',
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
```
#### Relations
[Diagramme ERD]
## 4. Conception détaillée
### 4.1 Frontend - Composants
#### Composant App
```jsx
// Structure principale
<App>
<Router>
<Header />
<Sidebar />
<MainContent />
<Footer />
</Router>
</App>
```
#### Composant Dashboard
- **Props** : user, data
- **State** : loading, filters
- **Actions** : loadData, applyFilter
### 4.2 Backend - API
#### Endpoints principaux
```
GET /api/users - Liste des utilisateurs
POST /api/users - Création utilisateur
GET /api/users/:id - Détail utilisateur
PUT /api/users/:id - Modification utilisateur
DELETE /api/users/:id - Suppression utilisateur
GET /api/content - Liste du contenu
POST /api/content - Création contenu
GET /api/content/:id - Détail contenu
PUT /api/content/:id - Modification contenu
DELETE /api/content/:id - Suppression contenu
```
#### Middlewares
- **Authentication** : Vérification JWT
- **Authorization** : Vérification des rôles
- **Rate limiting** : Limitation des appels
- **Validation** : Validation des données
- **Logging** : Journalisation des requêtes
### 4.3 Sécurité
#### Authentification
```javascript
// Flow OAuth2
1. Redirection vers provider
2. Callback avec code
3. Exchange code -> token
4. Stockage secure du token
5. Utilisation avec Bearer header
```
#### Gestion des erreurs
```javascript
// Structure des erreurs
{
error: {
code: 'USER_NOT_FOUND',
message: 'Utilisateur introuvable',
details: {},
timestamp: '2025-01-01T12:00:00Z'
}
}
```
## 5. Planning détaillé
### Phase 1 : Setup et fondations (3 semaines)
#### Semaine 1
- [ ] Setup repository Git
- [ ] Configuration environnements dev/staging/prod
- [ ] Setup CI/CD pipeline
- [ ] Configuration base de données
- [ ] **Livrable** : Environnements opérationnels
#### Semaine 2
- [ ] Architecture frontend de base
- [ ] Setup backend avec authentification
- [ ] Intégration base de données
- [ ] Tests unitaires de base
- [ ] **Livrable** : Stack technique fonctionnelle
#### Semaine 3
- [ ] Interface utilisateur de base
- [ ] API CRUD principale
- [ ] Tests d'intégration
- [ ] Documentation technique
- [ ] **Livrable** : MVP fonctionnel
### Phase 2 : Développement core (4 semaines)
#### Semaines 4-5
- [ ] Implémentation des fonctionnalités principales
- [ ] Interface utilisateur complète
- [ ] Gestion des erreurs
- [ ] **Livrable** : Version alpha
#### Semaines 6-7
- [ ] Fonctionnalités avancées
- [ ] Optimisation des performances
- [ ] Tests de charge
- [ ] **Livrable** : Version beta
### Phase 3 : Finalisation (2 semaines)
#### Semaine 8
- [ ] Tests utilisateur
- [ ] Corrections de bugs
- [ ] Documentation utilisateur
- [ ] Préparation déploiement production
#### Semaine 9
- [ ] Déploiement production
- [ ] Monitoring et alertes
- [ ] Formation utilisateurs
- [ ] **Livrable** : Version 1.0
## 6. Tests et Qualité
### 6.1 Stratégie de tests
- **Tests unitaires** : > 90% de couverture
- **Tests d'intégration** : Tous les endpoints
- **Tests end-to-end** : Parcours utilisateur critiques
- **Tests de performance** : Charge nominale et pics
### 6.2 Outils de tests
```javascript
// Frontend
- Jest + Testing Library
- Cypress pour E2E
- Lighthouse pour performance
// Backend
- Jest / pytest
- Supertest pour API
- Artillery pour charge
```
### 6.3 Critères de qualité
- **Code coverage** : > 90%
- **Temps de build** : < 5 minutes
- **Temps de déploiement** : < 10 minutes
- **Performance** : Core Web Vitals > 90
## 7. Déploiement et Infrastructure
### 7.1 Environnements
#### Développement
- **URL** : http://localhost:3000
- **Base** : PostgreSQL locale
- **Données** : Jeu de test
#### Staging
- **URL** : https://staging.app.com
- **Base** : PostgreSQL cloud
- **Données** : Réplique anonymisée de prod
#### Production
- **URL** : https://app.com
- **Base** : PostgreSQL cluster
- **Données** : Données réelles
### 7.2 Architecture cloud
```yaml
# Kubernetes deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-frontend
spec:
replicas: 3
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app: frontend
spec:
containers:
- name: frontend
image: app/frontend:latest
ports:
- containerPort: 80
```
### 7.3 Monitoring
- **Application** : Sentry pour erreurs
- **Infrastructure** : Prometheus + Grafana
- **Logs** : ELK Stack
- **Uptime** : Pingdom / UptimeRobot
## 8. Maintenance et Evolution
### 8.1 Maintenance corrective
- **Bugs critiques** : Correction < 4h
- **Bugs majeurs** : Correction < 24h
- **Bugs mineurs** : Correction < 1 semaine
### 8.2 Maintenance évolutive
- **Mises à jour sécurité** : Mensuelle
- **Nouvelles fonctionnalités** : Trimestrielle
- **Refactoring** : Semestrielle
### 8.3 Roadmap
#### V1.1 (3 mois)
- [ ] Amélioration UX
- [ ] API v2
- [ ] Export de données
#### V2.0 (6 mois)
- [ ] Application mobile
- [ ] Intelligence artificielle
- [ ] Intégrations tierces
## 9. Documentation
### 9.1 Documentation technique
- [ ] README détaillé
- [ ] Guide de contribution
- [ ] Documentation API (OpenAPI)
- [ ] Architecture Decision Records (ADR)
### 9.2 Documentation utilisateur
- [ ] Guide d'utilisation
- [ ] FAQ
- [ ] Tutoriels vidéo
- [ ] Support en ligne
## 10. Risques et Mitigation
| ID | Risque | Impact | Probabilité | Mitigation |
|----|--------|---------|-------------|------------|
| R1 | Retard de développement | Élevé | Moyenne | Buffer de temps, équipe élargie |
| R2 | Problème de performance | Moyen | Faible | Tests de charge réguliers |
| R3 | Faille de sécurité | Élevé | Faible | Audits sécurité, code review |
| R4 | Indisponibilité cloud | Moyen | Faible | Multi-zone, backup automatique |
## 11. Journal de Développement
### [Date] - Setup initial
[Notes sur la configuration initiale]
### [Date] - Développement Feature X
[Défis rencontrés et solutions]
### [Date] - Tests et déploiement
[Résultats des tests et déploiement]
## 12. Ressources et Références
### Ressources techniques
- [Documentation React](https://reactjs.org/)
- [Guide Node.js](https://nodejs.org/)
- [Best practices sécurité](https://owasp.org/)
### Outils utilisés
- [GitHub Repository](https://github.com/user/project)
- [Projet Management](https://trello.com/board)
- [Design System](https://figma.com/file)

View File

@ -0,0 +1,78 @@
# Projet Informatique
## 1. Vue d'ensemble
### Description du projet
[Résumé en quelques lignes]
### Objectifs
- Objectif principal :
- Fonctionnalités clés :
## 2. Spécifications techniques
### Technologies utilisées
- **Frontend** :
- **Backend** :
- **Base de données** :
- **Outils** :
### Architecture
[Description simple de l'architecture]
## 3. Fonctionnalités
### Fonctionnalités principales
- [ ] Feature 1
- [ ] Feature 2
- [ ] Feature 3
### Fonctionnalités secondaires
- [ ] Feature A
- [ ] Feature B
## 4. Interface utilisateur
### Pages principales
- Page d'accueil :
- Page utilisateur :
- Page admin :
## 5. Base de données
### Modèles principaux
- **Utilisateur** : id, nom, email
- **Contenu** : id, titre, description
- **Session** : id, token, user_id
## 6. Planning de développement
### Sprint 1 (2 semaines)
- [ ] Setup du projet
- [ ] Authentification
- [ ] Interface de base
### Sprint 2 (2 semaines)
- [ ] CRUD principal
- [ ] Tests unitaires
- [ ] Déploiement
### Sprint 3 (2 semaines)
- [ ] Fonctionnalités avancées
- [ ] Optimisation
- [ ] Documentation
## 7. Tests et déploiement
### Tests
- [ ] Tests unitaires
- [ ] Tests d'intégration
- [ ] Tests utilisateur
### Déploiement
- **Environnement dev** :
- **Environnement prod** :
## 8. Notes techniques
[Espace pour notes de développement]

82
templates/math/simple.md Normal file
View File

@ -0,0 +1,82 @@
# Projet Mathématiques
## 1. Problème étudié
### Énoncé
[Description claire du problème mathématique]
### Contexte
[Domaine mathématique concerné]
### Objectifs
- Objectif principal :
- Applications visées :
## 2. Fondements théoriques
### Concepts clés
- **Concept 1** : [Définition]
- **Concept 2** : [Définition]
- **Concept 3** : [Définition]
### Théorèmes principaux
- **Théorème A** : [Énoncé]
- **Théorème B** : [Énoncé]
## 3. Approche méthodologique
### Stratégie de résolution
[Description de l'approche choisie]
### Outils mathématiques
- Outils analytiques :
- Outils numériques :
- Logiciels utilisés :
## 4. Développement
### Étape 1 : Formalisation
[Mise en équations du problème]
### Étape 2 : Analyse théorique
[Étude des propriétés mathématiques]
### Étape 3 : Résolution
[Méthodes de résolution appliquées]
### Étape 4 : Validation
[Vérification des résultats]
## 5. Résultats
### Résultats théoriques
[Nouveaux théorèmes ou propriétés démontrés]
### Résultats numériques
[Calculs et simulations]
### Applications
[Domaines d'application des résultats]
## 6. Conclusions
### Apports du travail
[Ce que ce travail apporte de nouveau]
### Limitations
[Limites de l'approche ou des résultats]
### Perspectives
[Travaux futurs envisagés]
## 7. Références
[Bibliographie mathématique]
## 8. Annexes
### Annexe A : Démonstrations détaillées
### Annexe B : Calculs numériques
### Annexe C : Code informatique

View File

@ -0,0 +1,197 @@
# Projet de Recherche - Documentation Détaillée
## 1. Introduction et Contexte
### 1.1 Contexte général
[Description du domaine de recherche et du contexte académique/industriel]
### 1.2 Problématique
[Énoncé précis du problème de recherche]
### 1.3 Motivations
[Pourquoi cette recherche est-elle importante ?]
### 1.4 Objectifs
- **Objectif principal** :
- **Objectifs secondaires** :
-
-
-
### 1.5 Contributions attendues
[En quoi cette recherche apportera-t-elle du nouveau ?]
## 2. État de l'art et Positionnement
### 2.1 Revue de littérature
[Analyse détaillée des travaux existants]
### 2.2 Taxonomie des approches
[Classification des méthodes existantes]
### 2.3 Analyse critique
#### Forces des approches existantes
-
-
#### Limitations identifiées
-
-
### 2.4 Positionnement de notre approche
[Comment notre travail se situe par rapport à l'existant]
## 3. Méthodologie de Recherche
### 3.1 Cadre théorique
[Fondements théoriques de l'approche]
### 3.2 Hypothèses de recherche
- H1 :
- H2 :
- H3 :
### 3.3 Méthode expérimentale
#### Design expérimental
[Description du protocole]
#### Variables étudiées
- Variables indépendantes :
- Variables dépendantes :
- Variables de contrôle :
### 3.4 Outils et technologies
- **Outils de développement** :
- **Outils d'analyse** :
- **Plateformes de test** :
### 3.5 Jeux de données
[Description des données utilisées]
## 4. Architecture et Conception
### 4.1 Vue d'ensemble
[Schéma général de l'approche]
### 4.2 Composants principaux
#### Composant 1 : [Nom]
- **Fonction** :
- **Entrées** :
- **Sorties** :
- **Algorithme** :
#### Composant 2 : [Nom]
- **Fonction** :
- **Entrées** :
- **Sorties** :
- **Algorithme** :
### 4.3 Interfaces et flux de données
[Description des interactions entre composants]
## 5. Planning Détaillé
### Phase 1 : Étude préliminaire (Durée : X semaines)
- [ ] Revue exhaustive de littérature
- [ ] Analyse des outils existants
- [ ] Définition précise des spécifications
- [ ] **Livrable** : Rapport d'état de l'art
### Phase 2 : Conception et prototypage (Durée : X semaines)
- [ ] Conception architecturale
- [ ] Développement du prototype
- [ ] Tests unitaires
- [ ] **Livrable** : Prototype fonctionnel
### Phase 3 : Développement (Durée : X semaines)
- [ ] Implémentation complète
- [ ] Tests d'intégration
- [ ] Optimisation des performances
- [ ] **Livrable** : Version beta
### Phase 4 : Validation et évaluation (Durée : X semaines)
- [ ] Tests sur jeux de données réels
- [ ] Évaluation comparative
- [ ] Analyse des résultats
- [ ] **Livrable** : Rapport d'évaluation
### Phase 5 : Finalisation (Durée : X semaines)
- [ ] Rédaction finale
- [ ] Préparation des publications
- [ ] Documentation utilisateur
- [ ] **Livrable** : Document final
## 6. Métriques et Évaluation
### 6.1 Critères de succès
- **Critère 1** : [Métrique] > [Seuil]
- **Critère 2** : [Métrique] < [Seuil]
### 6.2 Protocole d'évaluation
[Description de la méthode d'évaluation]
### 6.3 Benchmarks
[Comparaison avec les solutions existantes]
## 7. Gestion des Risques
### 7.1 Analyse des risques
| Risque | Description | Impact | Probabilité | Score | Plan de mitigation |
|--------|-------------|---------|-------------|-------|-------------------|
| R1 | | Élevé | Moyenne | 6 | |
| R2 | | Moyen | Faible | 3 | |
| R3 | | Faible | Élevée | 3 | |
### 7.2 Plans de contingence
- **Plan A** : [Si risque majeur]
- **Plan B** : [Si retard significatif]
## 8. Ressources et Budget
### 8.1 Ressources humaines
- **Chercheur principal** : [Temps alloué]
- **Collaborateurs** : [Temps alloué]
### 8.2 Ressources matérielles
- **Équipement** :
- **Logiciels** :
- **Infrastructure** :
### 8.3 Budget estimé
[Si applicable]
## 9. Livrables et Publications
### 9.1 Livrables techniques
- [ ] Code source documenté
- [ ] Documentation technique
- [ ] Guide d'utilisation
- [ ] Jeux de test
### 9.2 Publications visées
- [ ] Conférence internationale : [Nom]
- [ ] Journal : [Nom]
- [ ] Workshop : [Nom]
## 10. Journal de Bord
### [Date] - [Étape]
[Description des activités et découvertes]
### [Date] - [Étape]
[Description des activités et découvertes]
## 11. Références et Bibliographie
[1] Auteur, A. (Année). Titre. *Journal*, Volume(Numéro), pages.
[2] Auteur, B. (Année). *Titre du livre*. Éditeur.
## Annexes
### Annexe A : Données complémentaires
### Annexe B : Code source principal
### Annexe C : Résultats détaillés

View File

@ -0,0 +1,55 @@
# Projet de Recherche
## 1. Contexte et Objectifs
### Problématique
[Décrivez le problème de recherche]
### Objectifs
- Objectif principal :
- Objectifs secondaires :
## 2. État de l'art
### Travaux existants
[Résumé des recherches précédentes]
### Lacunes identifiées
[Ce qui manque dans la littérature]
## 3. Méthodologie
### Approche
[Méthode de recherche choisie]
### Outils et ressources
-
-
## 4. Planning
### Phase 1 : Recherche bibliographique
- [ ] Collecte des sources
- [ ] Analyse critique
### Phase 2 : Développement
- [ ] Conception
- [ ] Implémentation
### Phase 3 : Validation
- [ ] Tests
- [ ] Évaluation
## 5. Résultats attendus
[Description des livrables]
## 6. Risques et mitigation
| Risque | Impact | Probabilité | Mitigation |
|--------|---------|-------------|------------|
| | | | |
## 7. Notes et références
[Espace pour notes additionnelles]

View File

@ -3,24 +3,117 @@ function getHeader() {
<header>
<div>
<h1>Mon Journal de Conception</h1>
<select id="skeleton-select" title="Niveau de squelette">
<option value="low">Squelette : Low</option>
<option value="medium">Medium</option>
<option value="high">High</option>
</select>
<button id="validate-skeleton" title="Valider niveau de squelette">Valider</button>
</div>
<nav>
<button id="theme-toggle" title="Basculer Black & White">🌓</button>
<button id="export-pdf" title="Exporter en PDF">📄 PDF</button>
<button id="export-md" title="Exporter en Markdown">📝 MD</button>
<label for="import-md" title="Importer Markdown">
<input id="import-md" type="file" accept=".md" style="display:none;">
MD
</label>
</nav>
</header>
`;
}
module.exports = { getHeader };
function getLeftPanel() {
return `
<div class="side-panel left-panel" id="left-panel">
<div class="panel-toggle" onclick="togglePanel('left')">
<span class="arrow"></span>
</div>
<div class="panel-content">
<h3 class="panel-header"> Templates de Projet</h3>
<div class="template-form">
<div class="form-group">
<label for="domain-select">Domaine :</label>
<select id="domain-select" title="Choisir le domaine">
<option value="">-- Sélectionner un domaine --</option>
<option value="recherche">🔬 Recherche</option>
<option value="math">📐 Mathématiques</option>
<option value="informatique">💻 Informatique</option>
<option value="ingenierie"> Ingénierie</option>
<option value="business">💼 Business</option>
<option value="design">🎨 Design</option>
</select>
</div>
<div class="form-group">
<label for="level-select">Niveau de détail :</label>
<select id="level-select" title="Niveau de détail" disabled>
<option value="">-- Choisir d'abord un domaine --</option>
<option value="simple">📄 Simple</option>
<option value="detaille">📋 Détaillé</option>
<option value="complet">📚 Complet</option>
</select>
</div>
<div class="template-preview" id="template-preview" style="display: none;">
<h4>Aperçu :</h4>
<div class="preview-content" id="preview-content">
<!-- Aperçu du template sélectionné -->
</div>
</div>
<div class="form-actions">
<button id="load-template" class="btn success" title="Charger le template" disabled>
Charger le template
</button>
<button id="reset-template" class="btn secondary" title="Réinitialiser">
🔄 Reset
</button>
</div>
</div>
</div>
</div>
`;
}
function getRightPanel() {
return `
<div class="side-panel right-panel" id="right-panel">
<div class="panel-toggle" onclick="togglePanel('right')">
<span class="arrow"></span>
</div>
<div class="panel-content">
<h3 class="panel-header">🛠 Outils</h3>
<div class="nav-section">
<h4>Apparence</h4>
<div class="nav-buttons">
<button class="nav-btn" id="theme-toggle" title="Basculer mode sombre">
<span class="icon">🌓</span>
<span>Basculer le thème</span>
</button>
</div>
</div>
<div class="nav-section">
<h4>Export</h4>
<div class="nav-buttons">
<button class="nav-btn" id="export-pdf" title="Exporter en PDF">
<span class="icon">📄</span>
<span>Exporter en PDF</span>
</button>
<button class="nav-btn" id="export-md" title="Exporter en Markdown">
<span class="icon">📝</span>
<span>Exporter en Markdown</span>
</button>
</div>
</div>
<div class="nav-section">
<h4>Import</h4>
<div class="nav-buttons">
<label class="panel-file-input" for="import-md" title="Importer Markdown">
<input id="import-md" type="file" accept=".md" style="display:none;">
<span class="icon"></span>
<span>Importer Markdown</span>
</label>
</div>
</div>
</div>
</div>
`;
}
function getPanelOverlay() {
return `
<div class="panel-overlay" id="panel-overlay" onclick="closeAllPanels()"></div>
`;
}
module.exports = { getHeader, getLeftPanel, getRightPanel, getPanelOverlay };

View File

@ -2,45 +2,60 @@ function getMain() {
return `
<main>
<section id="table-of-contents">
<h2>Table des matières</h2>
<h2>📑 Table des matières</h2>
<nav id="toc-nav">
<!--
Exemple de structure hiérarchique:
<ul>
<li><a href="#section1">1. Introduction</a>
<ul>
<li><a href="#subsection1-1">1.1 Objectifs</a></li>
<li><a href="#subsection1-2">1.2 Contexte</a></li>
</ul>
</li>
</ul>
-->
<div class="toc-placeholder">
<p>La table des matières sera générée automatiquement à partir des titres de votre journal.</p>
<button id="refresh-toc" class="btn secondary">🔄 Actualiser</button>
</div>
</nav>
</section>
<section id="design-journal">
<h2>Journal de conception</h2>
<h2>📝 Journal de conception</h2>
<div id="journal-controls" style="padding: 1rem; border-bottom: 1px solid var(--border-color); display: flex; gap: 1rem; align-items: center;">
<button id="save-journal" class="btn success">💾 Sauvegarder</button>
<button id="load-journal" class="btn">📂 Charger</button>
<span id="save-status" class="text-light"></span>
</div>
<div id="journal-editor" contenteditable="true">
<!-- Zone décriture principale -->
<!-- Zone d'écriture principale -->
</div>
</section>
<section id="ai-assistant">
<h2>Assistant IA</h2>
<button id="activate-rephrase">Reformuler un passage</button>
<button id="check-inconsistencies">Vérifier incohérences</button>
<button id="check-duplications">Vérifier duplications</button>
<button id="give-advice">Conseil sur le contenu</button>
<div>
<input type="number" id="liberty-repeat-count" min="1" placeholder="Combien ditérations?" />
<button id="liberty-mode">Mode Liberté : auto-suites</button>
<h2>🤖 Assistant IA</h2>
<div class="ai-coming-soon">
<div class="coming-soon-badge">
<span class="badge-icon">🚧</span>
<span class="badge-text">Fonctionnalités à venir</span>
</div>
<div class="ai-preview">
<h3>Prochainement disponible avec Mistral AI :</h3>
<ul class="ai-features-list">
<li> <strong>Reformulation intelligente</strong> - Amélioration du style et de la clarté</li>
<li>🔍 <strong>Détection d'incohérences</strong> - Analyse sémantique du contenu</li>
<li>📋 <strong>Vérification des doublons</strong> - Identification des répétitions</li>
<li>💡 <strong>Conseils personnalisés</strong> - Suggestions d'amélioration</li>
<li>🚀 <strong>Mode Liberté</strong> - Génération automatique de suites</li>
</ul>
<div class="ai-tech-stack">
<p><strong>Technologie :</strong> Powered by Mistral AI</p>
<p><strong>Modèle :</strong> Mistral Large (multilingue)</p>
</div>
</div>
</div>
<div id="ai-assistant-feedback">
<!-- Résultats et retours IA affichés ici -->
<div class="feedback-message coming-soon-message">
<strong>🔮 Intelligence Artificielle en développement</strong><br>
Ces fonctionnalités seront bientôt disponibles pour améliorer votre expérience de rédaction.
<br><br>
<small>💡 En attendant, utilisez les templates pour structurer efficacement vos journaux de conception.</small>
</div>
</div>
</section>
</main>
`;
}
module.exports = { getMain };
module.exports = { getMain };

View File

@ -1,4 +1,4 @@
const { getHeader } = require('./header');
const { getHeader, getLeftPanel, getRightPanel, getPanelOverlay } = require('./header');
const { getMain } = require('./main');
const { getFooter } = require('./footer');
@ -11,6 +11,7 @@ function getHead(){
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Conception-Assistant</title>
<link rel="stylesheet" href="/assets/css/style.css">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>📝</text></svg>">
</head>
`;
}
@ -19,9 +20,14 @@ function getBody(){
return `
<body>`
+ getHeader()
+ getLeftPanel()
+ getRightPanel()
+ getPanelOverlay()
+ getMain()
+ getFooter() +
`</body>
`
<script src="/assets/js/app.js"></script>
</body>
</html>
`;
}