feat: Enhanced mode v2 with Smart Adaptive strategy, comprehensive logging, and internationalization
Major Features:
- Enhanced Mode v2: Smart Adaptive section selection strategy
* AI automatically selects ONE optimal section per iteration
* Intelligent selection based on quality assessment and strategic importance
* Section tracking to avoid duplicate modifications
* Fixed header level preservation (## stays ##, ### stays ###)
* Updated document code block format (document)
- Mermaid Auto-Fix System
* New POST /api/ai/fix-mermaid endpoint for automatic diagram correction
* Automatic error detection in preview and presentation modes
* Inter-window communication for seamless editor updates
* AI-powered syntax error resolution
- Comprehensive Logging System
* Centralized logger utility (utils/logger.js)
* API request/response middleware logging
* AI operations: rephrase, check-inconsistencies, check-duplications, give-advice, liberty-mode, fix-mermaid
* Storage operations: create, read, update, delete journals
* Export operations: PDF and Web ZIP generation
* Structured logging with timestamps, levels, categories, and metadata
Improvements:
- Table of Contents: Clean display with markdown formatting stripped from titles
- Section badges: Fixed visibility with hardcoded blue background (#2563eb)
- Internationalization: Complete English translation (lang, UI text, placeholders, comments)
- Code cleanup: Removed all emojis from codebase
Technical Changes:
- routes/ai.js: Enhanced mode implementation, Mermaid fix endpoint, comprehensive logging
- routes/api.js: Storage operation logging
- routes/export.js: Export operation logging
- routes/index.js: Presentation mode Mermaid auto-fix
- assets/js/app.js: TOC markdown stripping, Mermaid error handling, message listeners
- assets/css/style.css: Dark mode comment, English placeholders, header icon
- views/page.js: English lang attribute, favicon, scroll-to-top button
- views/header.js: Theme toggle icon
- utils/logger.js: New centralized logging system
- app.js: API request/response logging middleware
This commit is contained in:
parent
89c10b14a7
commit
86eb68c0e6
55
README.md
55
README.md
@ -15,10 +15,13 @@ A collaborative design documentation tool with AI-powered assistance for technic
|
||||
- **Rephrase**: Improve clarity and style of selected text
|
||||
- **Analyze**: Detect inconsistencies and duplicates in documents
|
||||
- **Advice**: Get constructive improvement suggestions
|
||||
- **Enhanced Mode**: Iterative document enrichment with configurable precision (30-90%)
|
||||
- **Enhanced Mode (Smart Adaptive)**: AI-driven iterative document enrichment
|
||||
- AI automatically selects which section to improve in each iteration
|
||||
- Smart section selection based on quality assessment, importance, and coverage
|
||||
- Up to 10 iterations of progressive enhancement
|
||||
- Real-time streaming feedback
|
||||
- Precision control for creative vs. conservative improvements
|
||||
- Real-time streaming feedback with section tracking
|
||||
- Precision control for creative vs. conservative improvements (30-90%)
|
||||
- Automatically avoids re-modifying recently enhanced sections
|
||||
|
||||
### User Interface
|
||||
- Dark/Light theme with preference persistence
|
||||
@ -95,10 +98,17 @@ npm start
|
||||
### Using AI Features
|
||||
1. **Rephrase**: Select text → click "Rephrase" button
|
||||
2. **Analysis**: Click "Inconsistencies", "Duplicates", or "Advice" buttons
|
||||
3. **Enhanced Mode**:
|
||||
- Choose iterations (1-10) and precision (30-90%)
|
||||
3. **Enhanced Mode (Smart Adaptive)**:
|
||||
- Choose number of iterations (1-10)
|
||||
- Select precision level (30-90%)
|
||||
- Click "Enhanced Mode"
|
||||
- Watch real-time streaming improvements
|
||||
- AI automatically analyzes the entire document
|
||||
- At each iteration, AI selects ONE section to improve based on:
|
||||
- Quality assessment (incomplete sections, lacking details)
|
||||
- Strategic importance (introduction, architecture, APIs)
|
||||
- Coverage (avoids recently modified sections)
|
||||
- Watch real-time streaming with section badges showing which part is being enhanced
|
||||
- Document updates automatically after each iteration
|
||||
|
||||
### Keyboard Shortcuts
|
||||
- `Ctrl+S` - Save journal
|
||||
@ -149,6 +159,39 @@ conception-assistant/
|
||||
| 70% | Conservative | Primarily based on existing content | Document refinement |
|
||||
| 90% | Very Precise | Only obvious deductions | Technical finalization |
|
||||
|
||||
### Smart Adaptive Section Selection
|
||||
The Enhanced Mode uses an intelligent strategy to automatically choose which section to improve:
|
||||
|
||||
**Selection Criteria (in priority order):**
|
||||
1. **Quality Assessment**: Identifies sections that need improvement
|
||||
- Incomplete content (too short, missing details)
|
||||
- Vague or unclear explanations
|
||||
- Lack of technical specifications
|
||||
- Poor structure or organization
|
||||
|
||||
2. **Strategic Importance**: Prioritizes critical document sections
|
||||
- Introduction and overview sections
|
||||
- Architecture and design decisions
|
||||
- Core APIs and implementation details
|
||||
- Critical workflows and processes
|
||||
|
||||
3. **Coverage Tracking**: Ensures balanced enhancement
|
||||
- Avoids sections recently modified
|
||||
- Tracks modification history across iterations
|
||||
- Ensures all sections receive attention
|
||||
|
||||
4. **Progressive Enhancement**: Builds incrementally
|
||||
- Each iteration improves ONE section only
|
||||
- Maintains document coherence
|
||||
- Preserves all other sections unchanged
|
||||
|
||||
**Output Format Per Iteration:**
|
||||
- **Analyzed Sections**: List of all major sections found in the document
|
||||
- **Selected Section**: The specific section chosen for enhancement (with exact header name)
|
||||
- **Selection Reason**: Explanation of why this section was prioritized
|
||||
- **Improvements Made**: Detailed list of enhancements applied
|
||||
- **Next Recommendations**: Suggestions for future iterations
|
||||
|
||||
## Technology Stack
|
||||
|
||||
### Backend
|
||||
|
||||
19
app.js
19
app.js
@ -1,5 +1,6 @@
|
||||
const express = require('express');
|
||||
const path = require('path');
|
||||
const { logger } = require('./utils/logger');
|
||||
|
||||
const indexRoutes = require('./routes/index');
|
||||
const apiRoutes = require('./routes/api');
|
||||
@ -15,13 +16,28 @@ 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
|
||||
// Create data directory if it doesn't exist
|
||||
const fs = require('fs');
|
||||
const dataDir = path.join(__dirname, 'data');
|
||||
if (!fs.existsSync(dataDir)) {
|
||||
fs.mkdirSync(dataDir);
|
||||
logger.info('SYSTEM', 'Created data directory', { path: dataDir });
|
||||
}
|
||||
|
||||
// API request logging middleware
|
||||
app.use((req, res, next) => {
|
||||
const start = Date.now();
|
||||
|
||||
logger.apiRequest(req.method, req.path, req.body);
|
||||
|
||||
res.on('finish', () => {
|
||||
const duration = Date.now() - start;
|
||||
logger.apiResponse(req.path, res.statusCode, duration);
|
||||
});
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
app.use('/', indexRoutes);
|
||||
app.use('/api', apiRoutes);
|
||||
app.use('/api/templates', templatesRoutes);
|
||||
@ -29,5 +45,6 @@ app.use('/api/export', exportRoutes);
|
||||
app.use('/api/ai', aiRoutes);
|
||||
|
||||
app.listen(port, () => {
|
||||
logger.systemStart(port);
|
||||
console.log(`Server running on port ${port}`);
|
||||
});
|
||||
@ -16,7 +16,7 @@
|
||||
--transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
/* Mode sombre */
|
||||
/* Dark mode */
|
||||
body.dark-theme {
|
||||
--primary-color: #ecf0f1;
|
||||
--secondary-color: #3498db;
|
||||
@ -664,7 +664,7 @@ section h2 {
|
||||
}
|
||||
|
||||
#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";
|
||||
content: "Start writing your design journal...\A\A# Project Title\A\A## Context and Objectives\A\ADescribe your project context here...\A\A## Architecture\A\A### Main Components\A\A### Technologies Used\A\A## Design Decisions\A\A### Decision 1\A\A**Problem:**\A**Options Considered:**\A**Decision:**\A**Justification:**\A\A## Next Steps\A\A- [ ] Task 1\A- [ ] Task 2";
|
||||
color: var(--text-light);
|
||||
font-style: italic;
|
||||
white-space: pre-wrap;
|
||||
|
||||
103
assets/js/app.js
103
assets/js/app.js
@ -14,11 +14,30 @@ class ConceptionAssistant {
|
||||
async init() {
|
||||
this.setupEditor();
|
||||
this.setupEventListeners();
|
||||
this.setupMessageListener();
|
||||
await this.loadJournalList();
|
||||
this.generateTOC();
|
||||
this.updateStatistics();
|
||||
}
|
||||
|
||||
setupMessageListener() {
|
||||
// Listen for messages from presentation mode window
|
||||
window.addEventListener('message', (event) => {
|
||||
if (event.data && event.data.type === 'mermaid-fixed') {
|
||||
// Update the document content with the fixed Mermaid diagram
|
||||
const currentContent = this.editor.innerText;
|
||||
const fixedContent = currentContent.replace(event.data.originalCode, event.data.fixedCode);
|
||||
|
||||
if (fixedContent !== currentContent) {
|
||||
this.editor.innerText = fixedContent;
|
||||
this.generateTOC();
|
||||
this.saveState(true);
|
||||
this.showNotification('Mermaid diagram fixed in document', 'success');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setupEditor() {
|
||||
this.editor = document.getElementById('journal-editor');
|
||||
|
||||
@ -427,6 +446,19 @@ class ConceptionAssistant {
|
||||
}
|
||||
}
|
||||
|
||||
cleanMarkdownFromTitle(title) {
|
||||
// Remove all markdown formatting markers from title
|
||||
return title
|
||||
.replace(/\*\*/g, '') // Remove bold **
|
||||
.replace(/\*/g, '') // Remove italic *
|
||||
.replace(/__/g, '') // Remove bold __
|
||||
.replace(/_/g, '') // Remove italic _
|
||||
.replace(/`/g, '') // Remove code `
|
||||
.replace(/~~/g, '') // Remove strikethrough ~~
|
||||
.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1') // Remove links, keep text
|
||||
.trim();
|
||||
}
|
||||
|
||||
generateTOC() {
|
||||
// Save cursor position
|
||||
const savedRange = this.saveSelection();
|
||||
@ -477,7 +509,8 @@ class ConceptionAssistant {
|
||||
|
||||
// Ajouter l'élément avec indentation CSS
|
||||
const indent = (item.level - 1) * 1; // 1rem par niveau
|
||||
tocHtml += `<li style="margin-left: ${indent}rem;"><a href="#${item.id}" onclick="app.scrollToHeading('${item.title.replace(/'/g, "\\'")}'); return false;">${item.title}</a>`;
|
||||
const cleanTitle = this.cleanMarkdownFromTitle(item.title);
|
||||
tocHtml += `<li style="margin-left: ${indent}rem;"><a href="#${item.id}" onclick="app.scrollToHeading('${item.title.replace(/'/g, "\\'")}'); return false;">${cleanTitle}</a>`;
|
||||
|
||||
previousLevel = item.level;
|
||||
}
|
||||
@ -1100,10 +1133,14 @@ class ConceptionAssistant {
|
||||
progressFill.style.width = `${progressPercent}%`;
|
||||
}
|
||||
|
||||
// Display this iteration's explanation
|
||||
// Display this iteration's explanation with selected section
|
||||
const sectionBadge = data.selectedSection
|
||||
? `<span style="display: inline-block; background: #2563eb; color: #ffffff; padding: 0.25rem 0.75rem; border-radius: 12px; font-size: 0.85rem; margin-left: 0.5rem; font-weight: 500;">${data.selectedSection}</span>`
|
||||
: '';
|
||||
|
||||
const iterationHTML = `
|
||||
<div style="background: var(--surface-color); border-left: 4px solid var(--primary-color); padding: 1rem; border-radius: 8px; margin: 0.5rem 0;">
|
||||
<strong>Iteration ${data.iteration}</strong><br><br>
|
||||
<strong>Iteration ${data.iteration}</strong>${sectionBadge}<br><br>
|
||||
${this.formatAIResponse(data.explanation)}
|
||||
</div>
|
||||
`;
|
||||
@ -1312,6 +1349,45 @@ class ConceptionAssistant {
|
||||
this.editor.contentEditable = true;
|
||||
}
|
||||
|
||||
async fixMermaidDiagram(mermaidCode, errorMessage) {
|
||||
try {
|
||||
this.showNotification('Fixing Mermaid diagram...', 'info');
|
||||
|
||||
const response = await fetch('/api/ai/fix-mermaid', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
mermaidCode: mermaidCode,
|
||||
error: errorMessage
|
||||
})
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success && result.data.fixedCode) {
|
||||
// Replace the erroneous Mermaid code in the editor
|
||||
const currentContent = this.editor.innerText;
|
||||
const fixedContent = currentContent.replace(mermaidCode, result.data.fixedCode);
|
||||
|
||||
this.editor.innerText = fixedContent;
|
||||
this.generateTOC();
|
||||
this.saveState(true);
|
||||
|
||||
this.showNotification('Mermaid diagram fixed automatically', 'success');
|
||||
|
||||
// Return true to indicate successful fix
|
||||
return true;
|
||||
} else {
|
||||
this.showNotification('Could not fix Mermaid diagram', 'error');
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fixing Mermaid:', error);
|
||||
this.showNotification('Error fixing Mermaid diagram: ' + error.message, 'error');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
showNotification(message, type = 'success') {
|
||||
const notification = document.createElement('div');
|
||||
notification.className = `notification ${type}`;
|
||||
@ -1402,9 +1478,26 @@ class ConceptionAssistant {
|
||||
// Render diagram
|
||||
mermaid.render(uniqueId + '-svg', mermaidCode).then(({ svg }) => {
|
||||
mermaidDiv.innerHTML = svg;
|
||||
}).catch(err => {
|
||||
}).catch(async (err) => {
|
||||
console.warn('Mermaid rendering error:', err);
|
||||
mermaidDiv.innerHTML = `<pre style="color: var(--accent-color); padding: 1rem; background: var(--background-color); border-radius: 6px;">Mermaid rendering error: ${err.message}</pre>`;
|
||||
|
||||
// Automatically try to fix the Mermaid diagram
|
||||
this.showNotification('Mermaid error detected. Attempting to fix...', 'warning');
|
||||
|
||||
const fixed = await this.fixMermaidDiagram(mermaidCode, err.message);
|
||||
|
||||
if (fixed) {
|
||||
// Diagram was fixed, re-toggle preview to show the corrected version
|
||||
setTimeout(async () => {
|
||||
// Exit preview mode
|
||||
await this.togglePreview();
|
||||
// Re-enter preview mode to render fixed diagram
|
||||
setTimeout(() => this.togglePreview(), 500);
|
||||
}, 1000);
|
||||
} else {
|
||||
// Show error if fix failed
|
||||
mermaidDiv.innerHTML = `<pre style="color: var(--accent-color); padding: 1rem; background: var(--background-color); border-radius: 6px;">Mermaid rendering error: ${err.message}\n\nAutomatic fix failed. Please check the syntax manually.</pre>`;
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.warn('Mermaid processing error:', error);
|
||||
|
||||
253
routes/ai.js
253
routes/ai.js
@ -1,6 +1,7 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const https = require('https');
|
||||
const { logger } = require('../utils/logger');
|
||||
require('dotenv').config({ path: './config/.env' });
|
||||
|
||||
// Mistral AI Configuration
|
||||
@ -88,10 +89,15 @@ async function callMistralAPI(messages, temperature = null) {
|
||||
|
||||
// POST /api/ai/rephrase - Rephrase text
|
||||
router.post('/rephrase', checkAIEnabled, async (req, res) => {
|
||||
const startTime = Date.now();
|
||||
|
||||
try {
|
||||
const { text, context = '' } = req.body;
|
||||
|
||||
logger.aiRequest('rephrase', MISTRAL_MODEL, text.length);
|
||||
|
||||
if (!text || text.trim().length === 0) {
|
||||
logger.aiError('rephrase', new Error('Text to rephrase is required'));
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Text to rephrase is required'
|
||||
@ -121,6 +127,8 @@ router.post('/rephrase', checkAIEnabled, async (req, res) => {
|
||||
|
||||
const result = await callMistralAPI(messages, 0.2); // Precise rephrasing
|
||||
|
||||
logger.aiResponse('rephrase', true, Date.now() - startTime);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
@ -130,7 +138,7 @@ router.post('/rephrase', checkAIEnabled, async (req, res) => {
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Rephrasing error:', error);
|
||||
logger.aiError('rephrase', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Error during rephrasing: ' + error.message
|
||||
@ -140,10 +148,15 @@ router.post('/rephrase', checkAIEnabled, async (req, res) => {
|
||||
|
||||
// POST /api/ai/check-inconsistencies - Check for inconsistencies
|
||||
router.post('/check-inconsistencies', checkAIEnabled, async (req, res) => {
|
||||
const startTime = Date.now();
|
||||
|
||||
try {
|
||||
const { content } = req.body;
|
||||
|
||||
logger.aiRequest('check-inconsistencies', MISTRAL_MODEL, content.length);
|
||||
|
||||
if (!content || content.trim().length === 0) {
|
||||
logger.aiError('check-inconsistencies', new Error('Content to analyze is required'));
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Content to analyze is required'
|
||||
@ -171,6 +184,8 @@ router.post('/check-inconsistencies', checkAIEnabled, async (req, res) => {
|
||||
|
||||
const result = await callMistralAPI(messages, 0.1); // Precise and factual analysis
|
||||
|
||||
logger.aiResponse('check-inconsistencies', true, Date.now() - startTime);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
@ -179,7 +194,7 @@ router.post('/check-inconsistencies', checkAIEnabled, async (req, res) => {
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Inconsistencies analysis error:', error);
|
||||
logger.aiError('check-inconsistencies', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Error during analysis: ' + error.message
|
||||
@ -189,10 +204,15 @@ router.post('/check-inconsistencies', checkAIEnabled, async (req, res) => {
|
||||
|
||||
// POST /api/ai/check-duplications - Check for duplications
|
||||
router.post('/check-duplications', checkAIEnabled, async (req, res) => {
|
||||
const startTime = Date.now();
|
||||
|
||||
try {
|
||||
const { content } = req.body;
|
||||
|
||||
logger.aiRequest('check-duplications', MISTRAL_MODEL, content.length);
|
||||
|
||||
if (!content || content.trim().length === 0) {
|
||||
logger.aiError('check-duplications', new Error('Content to analyze is required'));
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Content to analyze is required'
|
||||
@ -221,6 +241,8 @@ router.post('/check-duplications', checkAIEnabled, async (req, res) => {
|
||||
|
||||
const result = await callMistralAPI(messages, 0.1); // Precise and factual analysis
|
||||
|
||||
logger.aiResponse('check-duplications', true, Date.now() - startTime);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
@ -229,7 +251,7 @@ router.post('/check-duplications', checkAIEnabled, async (req, res) => {
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Duplications analysis error:', error);
|
||||
logger.aiError('check-duplications', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Error during analysis: ' + error.message
|
||||
@ -239,10 +261,15 @@ router.post('/check-duplications', checkAIEnabled, async (req, res) => {
|
||||
|
||||
// POST /api/ai/give-advice - Give advice
|
||||
router.post('/give-advice', checkAIEnabled, async (req, res) => {
|
||||
const startTime = Date.now();
|
||||
|
||||
try {
|
||||
const { content, domain = 'general' } = req.body;
|
||||
|
||||
logger.aiRequest('give-advice', MISTRAL_MODEL, content.length);
|
||||
|
||||
if (!content || content.trim().length === 0) {
|
||||
logger.aiError('give-advice', new Error('Content to analyze is required'));
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Content to analyze is required'
|
||||
@ -275,6 +302,8 @@ router.post('/give-advice', checkAIEnabled, async (req, res) => {
|
||||
|
||||
const result = await callMistralAPI(messages, 0.4); // Balanced advice
|
||||
|
||||
logger.aiResponse('give-advice', true, Date.now() - startTime);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
@ -283,7 +312,7 @@ router.post('/give-advice', checkAIEnabled, async (req, res) => {
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Advice error:', error);
|
||||
logger.aiError('give-advice', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Error during analysis: ' + error.message
|
||||
@ -293,10 +322,15 @@ router.post('/give-advice', checkAIEnabled, async (req, res) => {
|
||||
|
||||
// POST /api/ai/liberty-mode - Enhanced mode (iterative generation)
|
||||
router.post('/liberty-mode', checkAIEnabled, async (req, res) => {
|
||||
const startTime = Date.now();
|
||||
|
||||
try {
|
||||
const { content, iterations = 1, precision = 70, focus = 'design' } = req.body;
|
||||
|
||||
logger.aiRequest('liberty-mode', MISTRAL_MODEL, content.length);
|
||||
|
||||
if (!content || content.trim().length === 0) {
|
||||
logger.aiError('liberty-mode', new Error('Base content is required'));
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Base content is required'
|
||||
@ -306,6 +340,8 @@ router.post('/liberty-mode', checkAIEnabled, async (req, res) => {
|
||||
const maxIterations = Math.min(parseInt(iterations), 10); // Limit to 10 iterations
|
||||
const precisionPercent = Math.min(Math.max(parseInt(precision), 10), 100); // Between 10% and 100%
|
||||
|
||||
logger.info('AI', `Liberty mode started: ${maxIterations} iterations at ${precisionPercent}% precision`);
|
||||
|
||||
// Configure streaming for real-time responses
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'text/event-stream',
|
||||
@ -315,46 +351,115 @@ router.post('/liberty-mode', checkAIEnabled, async (req, res) => {
|
||||
});
|
||||
|
||||
let currentContent = content;
|
||||
let modifiedSections = []; // Track sections that have been modified
|
||||
|
||||
for (let i = 0; i < maxIterations; i++) {
|
||||
try {
|
||||
const messages = [
|
||||
{
|
||||
role: 'system',
|
||||
content: `You are a technical design expert with "Enhanced Mode".
|
||||
content: `You are a technical design expert with "Enhanced Mode - Smart Adaptive Strategy".
|
||||
|
||||
MISSION: Improve and enrich the document respecting EXACTLY this precision level: ${precisionPercent}%
|
||||
MISSION: Analyze the entire document and choose ONE section to improve in this iteration.
|
||||
|
||||
SMART SECTION SELECTION CRITERIA:
|
||||
1. **Quality Assessment**: Identify sections that need improvement (incomplete, vague, lacking details)
|
||||
2. **Strategic Importance**: Prioritize critical sections (Introduction, Architecture, Core APIs)
|
||||
3. **Coverage**: Avoid sections recently modified: ${modifiedSections.length > 0 ? modifiedSections.join(', ') : 'None yet'}
|
||||
4. **Balance**: Ensure progressive document enhancement
|
||||
|
||||
PRECISION RULES:
|
||||
- At ${precisionPercent}%: You can deduce and add content up to ${precisionPercent}% based on existing information
|
||||
- At ${100 - precisionPercent}%: You can create logical and relevant content even without explicit info in the text
|
||||
|
||||
SECTION BOUNDARIES - CRITICAL UNDERSTANDING:
|
||||
A section is defined by its header and extends until:
|
||||
- The next header of EQUAL or HIGHER level appears
|
||||
- The end of the document is reached
|
||||
|
||||
Example structure:
|
||||
```
|
||||
# Main Title (level 1)
|
||||
content...
|
||||
|
||||
## Section A (level 2) ← Section A starts here
|
||||
content A...
|
||||
|
||||
### Subsection A1 (level 3) ← Part of Section A
|
||||
content A1...
|
||||
|
||||
### Subsection A2 (level 3) ← Part of Section A
|
||||
content A2...
|
||||
|
||||
## Section B (level 2) ← Section A ends, Section B starts
|
||||
content B...
|
||||
```
|
||||
|
||||
If you choose to modify "### Subsection A1":
|
||||
- Include ONLY the content from "### Subsection A1" until "### Subsection A2"
|
||||
- DO NOT include "### Subsection A2" or anything after it
|
||||
- DO NOT include "## Section B" or anything after it
|
||||
- The rest of the document (before and after) must remain EXACTLY as it was
|
||||
|
||||
INSTRUCTIONS:
|
||||
1. Enrich ALL of the document consistently
|
||||
2. Add sections, details, explanations, conceptual diagrams
|
||||
3. Develop existing ideas with the allowed creativity
|
||||
4. Maintain logical structure
|
||||
1. **ANALYZE** all sections in the document
|
||||
2. **CHOOSE** exactly ONE section to improve (based on criteria above)
|
||||
3. **IDENTIFY BOUNDARIES**: Determine where the chosen section starts and ends:
|
||||
- Starts: At the section's header (e.g., "### API Documentation")
|
||||
- Ends: Right BEFORE the next header of equal or higher level, OR at document end
|
||||
4. **ENHANCE** only that chosen section with:
|
||||
- Additional technical details
|
||||
- Better explanations
|
||||
- Code examples if relevant
|
||||
- Mermaid diagrams if relevant (using \`\`\`mermaid blocks)
|
||||
- Best practices
|
||||
5. **PRESERVE** everything else:
|
||||
- ALL content BEFORE the chosen section must remain unchanged
|
||||
- ALL content AFTER the chosen section must remain unchanged
|
||||
- ALL other sections at any level must remain unchanged
|
||||
6. **MAINTAIN** document structure and formatting
|
||||
7. **CRITICAL**: Keep the EXACT same header level for the modified section
|
||||
- If the original section uses ## (level 2), the modified section MUST use ##
|
||||
- If the original section uses ### (level 3), the modified section MUST use ###
|
||||
- DO NOT change header levels (## to ###, or ### to ##, etc.)
|
||||
8. **VERIFY**: Before returning, check that the document contains ALL original sections
|
||||
|
||||
MANDATORY RESPONSE FORMAT - Use code blocks with triple backticks:
|
||||
|
||||
First, write your comment explaining the improvements inside a code block labeled "comment${i + 1}":
|
||||
- What sections were added
|
||||
- What was enhanced
|
||||
- Reasoning behind changes
|
||||
First, write your analysis and decision inside a code block labeled "comment${i + 1}":
|
||||
- **Analyzed Sections**: List all major sections found
|
||||
- **Selected Section**: Which section you chose to improve (exact header name)
|
||||
- **Selection Reason**: Why this section needs improvement most
|
||||
- **Improvements Made**: Detailed list of enhancements
|
||||
- **Next Recommendations**: Sections to consider for future iterations
|
||||
|
||||
Then, write the complete improved markdown document inside a code block labeled "document".
|
||||
Then, write the complete markdown document inside a code block labeled "document".
|
||||
The document must include ALL sections, with ONLY the selected section modified.
|
||||
|
||||
Example format:
|
||||
First block: comment${i + 1}
|
||||
Second block: document
|
||||
\`\`\`comment${i + 1}
|
||||
**Analyzed Sections**: Introduction, Architecture, API Documentation, Conclusion
|
||||
**Selected Section**: ### API Documentation
|
||||
**Selection Reason**: This section lacks implementation details and error handling
|
||||
**Improvements Made**:
|
||||
- Added authentication flow
|
||||
- Included error handling scenarios
|
||||
- Added code examples
|
||||
**Next Recommendations**: Consider enhancing Architecture section
|
||||
\`\`\`
|
||||
|
||||
\`\`\`document
|
||||
[Full document with only API Documentation section improved]
|
||||
\`\`\`document
|
||||
|
||||
Focus: ${focus}
|
||||
Precision: ${precisionPercent}%
|
||||
Iteration: ${i + 1}/${maxIterations}`
|
||||
Iteration: ${i + 1}/${maxIterations}
|
||||
Previously Modified: ${modifiedSections.length > 0 ? modifiedSections.join(', ') : 'None'}`
|
||||
},
|
||||
{
|
||||
role: 'user',
|
||||
content: `Document to improve (Iteration ${i + 1}):\n\n${currentContent}`
|
||||
content: `Document to analyze and improve (Iteration ${i + 1}):\n\n${currentContent}`
|
||||
}
|
||||
];
|
||||
|
||||
@ -365,16 +470,33 @@ router.post('/liberty-mode', checkAIEnabled, async (req, res) => {
|
||||
|
||||
// Extract code blocks
|
||||
const commentRegex = /```comment\d+\s*([\s\S]*?)```/;
|
||||
const documentRegex = /```document\s*([\s\S]*?)```/;
|
||||
const documentRegex = /```document\s*([\s\S]*?)```document/;
|
||||
|
||||
const commentMatch = result.match(commentRegex);
|
||||
const documentMatch = result.match(documentRegex);
|
||||
let documentMatch = result.match(documentRegex);
|
||||
|
||||
// Fallback: if new format not found, try old format
|
||||
if (!documentMatch) {
|
||||
const oldDocumentRegex = /```document\s*([\s\S]*?)```/;
|
||||
documentMatch = result.match(oldDocumentRegex);
|
||||
}
|
||||
|
||||
let explanation = '';
|
||||
let newMarkdown = currentContent; // Default, keep old content
|
||||
let selectedSection = '';
|
||||
|
||||
if (commentMatch && commentMatch[1]) {
|
||||
explanation = commentMatch[1].trim();
|
||||
|
||||
// Extract selected section name from comment
|
||||
const sectionMatch = explanation.match(/\*\*Selected Section\*\*:?\s*(.+?)(?:\n|\*\*|$)/i);
|
||||
if (sectionMatch && sectionMatch[1]) {
|
||||
selectedSection = sectionMatch[1].trim();
|
||||
// Add to modified sections tracker
|
||||
if (!modifiedSections.includes(selectedSection)) {
|
||||
modifiedSections.push(selectedSection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (documentMatch && documentMatch[1]) {
|
||||
@ -399,6 +521,7 @@ router.post('/liberty-mode', checkAIEnabled, async (req, res) => {
|
||||
const iterationData = {
|
||||
iteration: i + 1,
|
||||
explanation: explanation,
|
||||
selectedSection: selectedSection || 'Unknown',
|
||||
markdown: newMarkdown,
|
||||
completed: false
|
||||
};
|
||||
@ -432,8 +555,10 @@ router.post('/liberty-mode', checkAIEnabled, async (req, res) => {
|
||||
res.write(`data: ${JSON.stringify(finalData)}\n\n`);
|
||||
res.end();
|
||||
|
||||
logger.aiResponse('liberty-mode', true, Date.now() - startTime);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Enhanced mode error:', error);
|
||||
logger.aiError('liberty-mode', error);
|
||||
|
||||
const errorData = {
|
||||
error: 'Error during generation: ' + error.message,
|
||||
@ -445,6 +570,90 @@ router.post('/liberty-mode', checkAIEnabled, async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// POST /api/ai/fix-mermaid - Fix Mermaid diagram errors
|
||||
router.post('/fix-mermaid', checkAIEnabled, async (req, res) => {
|
||||
const startTime = Date.now();
|
||||
|
||||
try {
|
||||
const { mermaidCode, error } = req.body;
|
||||
|
||||
logger.aiRequest('fix-mermaid', MISTRAL_MODEL, mermaidCode ? mermaidCode.length : 0);
|
||||
|
||||
if (!mermaidCode || mermaidCode.trim().length === 0) {
|
||||
logger.aiError('fix-mermaid', new Error('Mermaid code is required'));
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Mermaid code is required'
|
||||
});
|
||||
}
|
||||
|
||||
if (!error || error.trim().length === 0) {
|
||||
logger.aiError('fix-mermaid', new Error('Error message is required'));
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Error message is required'
|
||||
});
|
||||
}
|
||||
|
||||
const messages = [
|
||||
{
|
||||
role: 'system',
|
||||
content: `You are a Mermaid diagram syntax expert.
|
||||
|
||||
MISSION: Fix the Mermaid diagram syntax error.
|
||||
|
||||
STRICT RULES:
|
||||
1. Analyze the error message carefully
|
||||
2. Identify the syntax issue in the Mermaid code
|
||||
3. Fix ONLY the syntax error while preserving the diagram's intent and content
|
||||
4. Return ONLY the corrected Mermaid code
|
||||
5. Do NOT add explanations, comments, or any text outside the code block
|
||||
6. Do NOT wrap in \`\`\`mermaid - respond with raw Mermaid code only
|
||||
|
||||
Common Mermaid syntax issues to watch for:
|
||||
- Invalid character sequences in node labels
|
||||
- Missing quotes around special characters
|
||||
- Incorrect bracket/parenthesis syntax for node shapes
|
||||
- Invalid keywords or operators
|
||||
- Line break issues (\n should be used carefully)
|
||||
- Special characters that need escaping: (, ), [, ], {, }, <, >, ", '`
|
||||
},
|
||||
{
|
||||
role: 'user',
|
||||
content: `Fix this Mermaid diagram that has a syntax error.
|
||||
|
||||
ERROR:
|
||||
${error}
|
||||
|
||||
MERMAID CODE:
|
||||
${mermaidCode}
|
||||
|
||||
Return ONLY the corrected Mermaid code without any explanation or markdown formatting.`
|
||||
}
|
||||
];
|
||||
|
||||
const result = await callMistralAPI(messages, 0.1); // Very precise
|
||||
|
||||
logger.aiResponse('fix-mermaid', true, Date.now() - startTime);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
originalCode: mermaidCode,
|
||||
fixedCode: result.trim(),
|
||||
error: error
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.aiError('fix-mermaid', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Error fixing Mermaid diagram: ' + error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// GET /api/ai/status - AI status
|
||||
router.get('/status', (req, res) => {
|
||||
res.json({
|
||||
|
||||
@ -3,6 +3,7 @@ const router = express.Router();
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
const { logger } = require('../utils/logger');
|
||||
|
||||
// Import export module
|
||||
const exportRouter = require('./export');
|
||||
@ -140,30 +141,62 @@ function deteMd(id) {
|
||||
|
||||
// GET /api/journals - Get all journals
|
||||
router.get('/journals', (req, res) => {
|
||||
try {
|
||||
logger.storageRead('get-all-journals', 'all');
|
||||
const data = readMd();
|
||||
res.json({
|
||||
success: true,
|
||||
data: readMd()
|
||||
data: data
|
||||
});
|
||||
} catch (error) {
|
||||
logger.storageError('get-all-journals', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// POST /api/journals - Create a new journal
|
||||
router.post('/journals', (req, res) => {
|
||||
try {
|
||||
const { content } = req.body;
|
||||
|
||||
const result = createMd(content);
|
||||
logger.storageWrite('create-journal', result.uuid);
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
data: createMd(content)
|
||||
data: result
|
||||
});
|
||||
} catch (error) {
|
||||
logger.storageError('create-journal', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// GET /api/journals/:id - Get a specific journal
|
||||
router.get('/journals/:id', (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
logger.storageRead('get-journal', id);
|
||||
const data = readMd(id);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: readMd(id)
|
||||
data: data
|
||||
});
|
||||
} catch (error) {
|
||||
logger.storageError('get-journal', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// PUT /api/journals/:id - Update a journal
|
||||
@ -175,11 +208,14 @@ router.put('/journals/:id', (req, res) => {
|
||||
let result;
|
||||
if (content) {
|
||||
// Complete content update
|
||||
logger.storageWrite('update-journal-content', id);
|
||||
result = updateMd(id, content);
|
||||
} else if (modifications) {
|
||||
// Partial modifications (for future compatibility)
|
||||
logger.storageWrite('update-journal-modifications', id);
|
||||
result = modifMd(id, modifications);
|
||||
} else {
|
||||
logger.storageError('update-journal', new Error('Content or modifications required'));
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Content or modifications required'
|
||||
@ -191,6 +227,7 @@ router.put('/journals/:id', (req, res) => {
|
||||
data: result
|
||||
});
|
||||
} catch (error) {
|
||||
logger.storageError('update-journal', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: error.message
|
||||
@ -200,12 +237,23 @@ router.put('/journals/:id', (req, res) => {
|
||||
|
||||
// DELETE /api/journals/:id - Delete a journal
|
||||
router.delete('/journals/:id', (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
logger.storageWrite('delete-journal', id);
|
||||
const data = deteMd(id);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: deteMd(id)
|
||||
data: data
|
||||
});
|
||||
} catch (error) {
|
||||
logger.storageError('delete-journal', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Integrate export routes
|
||||
|
||||
@ -4,12 +4,17 @@ const puppeteer = require('puppeteer');
|
||||
const path = require('path');
|
||||
const archiver = require('archiver');
|
||||
const fs = require('fs');
|
||||
const { logger } = require('../utils/logger');
|
||||
|
||||
// POST /api/export/pdf - Generate PDF from markdown content
|
||||
router.post('/pdf', async (req, res) => {
|
||||
const startTime = Date.now();
|
||||
const { content, title = 'Design Journal' } = req.body;
|
||||
|
||||
logger.exportOperation('pdf', 'application/pdf', content.length);
|
||||
|
||||
if (!content || content.trim() === '') {
|
||||
logger.error('EXPORT', 'PDF export failed: Content required');
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Content required to generate PDF'
|
||||
@ -90,10 +95,12 @@ router.post('/pdf', async (req, res) => {
|
||||
res.setHeader('Content-Disposition', `attachment; filename="${sanitizeFilename(title)}.pdf"`);
|
||||
res.setHeader('Content-Length', pdfBuffer.length);
|
||||
|
||||
logger.info('EXPORT', `PDF generated successfully in ${Date.now() - startTime}ms`, { size: pdfBuffer.length, title });
|
||||
|
||||
res.send(pdfBuffer);
|
||||
|
||||
} catch (error) {
|
||||
console.error('PDF generation error:', error);
|
||||
logger.error('EXPORT', 'PDF generation error', { error: error.message, title });
|
||||
|
||||
if (browser) {
|
||||
await browser.close();
|
||||
@ -513,9 +520,13 @@ function sanitizeFilename(filename) {
|
||||
|
||||
// POST /api/export/web - Generate ZIP with HTML + CSS
|
||||
router.post('/web', async (req, res) => {
|
||||
const startTime = Date.now();
|
||||
const { content, title = 'Design Journal' } = req.body;
|
||||
|
||||
logger.exportOperation('web-zip', 'application/zip', content.length);
|
||||
|
||||
if (!content || content.trim() === '') {
|
||||
logger.error('EXPORT', 'Web export failed: Content required');
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Content required to generate web export'
|
||||
@ -661,8 +672,10 @@ body {
|
||||
// Finalize archive
|
||||
await archive.finalize();
|
||||
|
||||
logger.info('EXPORT', `Web ZIP generated successfully in ${Date.now() - startTime}ms`, { title });
|
||||
|
||||
} catch (error) {
|
||||
console.error('Web export error:', error);
|
||||
logger.error('EXPORT', 'Web export error', { error: error.message, title });
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: 'Error during web export'
|
||||
|
||||
@ -127,9 +127,57 @@ router.post('/present', (req, res) => {
|
||||
// Render diagram
|
||||
mermaid.render(uniqueId + '-svg', mermaidCode).then(function(result) {
|
||||
mermaidDiv.innerHTML = result.svg;
|
||||
}).catch(function(err) {
|
||||
}).catch(async function(err) {
|
||||
console.warn('Mermaid rendering error:', err);
|
||||
mermaidDiv.innerHTML = '<pre style="color: red; padding: 1rem; background: #ffe6e6; border-radius: 6px;">Mermaid rendering error: ' + err.message + '</pre>';
|
||||
|
||||
// Show error message with auto-fix option
|
||||
mermaidDiv.innerHTML = '<div style="padding: 1.5rem; background: #fff3cd; border: 2px solid #ffc107; border-radius: 8px; margin: 1rem 0;">' +
|
||||
'<h4 style="margin-top: 0; color: #856404;">[!] Mermaid Diagram Error</h4>' +
|
||||
'<p style="margin: 0.5rem 0;"><strong>Error:</strong> ' + err.message + '</p>' +
|
||||
'<p style="margin: 1rem 0 0.5rem 0; color: #666;">Attempting to fix automatically...</p>' +
|
||||
'<div id="fix-status-' + uniqueId + '" style="margin-top: 0.5rem;"></div>' +
|
||||
'</div>';
|
||||
|
||||
const statusDiv = document.getElementById('fix-status-' + uniqueId);
|
||||
|
||||
try {
|
||||
// Call AI API to fix Mermaid diagram
|
||||
const response = await fetch('/api/ai/fix-mermaid', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
mermaidCode: mermaidCode,
|
||||
error: err.message
|
||||
})
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success && result.data.fixedCode) {
|
||||
statusDiv.innerHTML = '<p style="color: #28a745; font-weight: 500;">[OK] Diagram fixed! Please close this window and reopen presentation mode from the editor to see the corrected diagram.</p>';
|
||||
|
||||
// Try to update parent window if available
|
||||
if (window.opener && !window.opener.closed) {
|
||||
try {
|
||||
// Send message to parent window to update content
|
||||
window.opener.postMessage({
|
||||
type: 'mermaid-fixed',
|
||||
originalCode: mermaidCode,
|
||||
fixedCode: result.data.fixedCode
|
||||
}, '*');
|
||||
|
||||
statusDiv.innerHTML += '<p style="color: #28a745; margin-top: 0.5rem;">Document updated in editor. You can now close this window and reopen presentation mode.</p>';
|
||||
} catch (e) {
|
||||
console.warn('Could not update parent window:', e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
statusDiv.innerHTML = '<p style="color: #dc3545;">[ERROR] Automatic fix failed. Please check the syntax in the editor.</p>';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fixing Mermaid:', error);
|
||||
statusDiv.innerHTML = '<p style="color: #dc3545;">[ERROR] Could not fix diagram automatically. Please check your syntax in the editor.</p>';
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.warn('Mermaid processing error:', error);
|
||||
|
||||
193
utils/logger.js
Normal file
193
utils/logger.js
Normal file
@ -0,0 +1,193 @@
|
||||
/**
|
||||
* Centralized logging system for Conception Assistant
|
||||
* Provides structured logging for all application events
|
||||
*/
|
||||
|
||||
const LOG_LEVELS = {
|
||||
ERROR: 'ERROR',
|
||||
WARN: 'WARN',
|
||||
INFO: 'INFO',
|
||||
DEBUG: 'DEBUG'
|
||||
};
|
||||
|
||||
const LOG_CATEGORIES = {
|
||||
API: 'API',
|
||||
AI: 'AI',
|
||||
UI: 'UI',
|
||||
STORAGE: 'STORAGE',
|
||||
EXPORT: 'EXPORT',
|
||||
AUTH: 'AUTH',
|
||||
SYSTEM: 'SYSTEM'
|
||||
};
|
||||
|
||||
class Logger {
|
||||
constructor() {
|
||||
this.enabled = process.env.LOGGING_ENABLED !== 'false';
|
||||
this.logLevel = process.env.LOG_LEVEL || 'INFO';
|
||||
}
|
||||
|
||||
log(level, category, message, metadata = {}) {
|
||||
if (!this.enabled) return;
|
||||
|
||||
const timestamp = new Date().toISOString();
|
||||
const logEntry = {
|
||||
timestamp,
|
||||
level,
|
||||
category,
|
||||
message,
|
||||
...metadata
|
||||
};
|
||||
|
||||
const formattedMessage = `[${timestamp}] [${level}] [${category}] ${message}`;
|
||||
|
||||
switch (level) {
|
||||
case LOG_LEVELS.ERROR:
|
||||
console.error(formattedMessage, metadata);
|
||||
break;
|
||||
case LOG_LEVELS.WARN:
|
||||
console.warn(formattedMessage, metadata);
|
||||
break;
|
||||
case LOG_LEVELS.INFO:
|
||||
console.info(formattedMessage, metadata);
|
||||
break;
|
||||
case LOG_LEVELS.DEBUG:
|
||||
console.log(formattedMessage, metadata);
|
||||
break;
|
||||
default:
|
||||
console.log(formattedMessage, metadata);
|
||||
}
|
||||
|
||||
return logEntry;
|
||||
}
|
||||
|
||||
// API Request logging
|
||||
apiRequest(method, endpoint, payload = {}) {
|
||||
return this.log(LOG_LEVELS.INFO, LOG_CATEGORIES.API, `${method} ${endpoint}`, {
|
||||
method,
|
||||
endpoint,
|
||||
payloadSize: JSON.stringify(payload).length
|
||||
});
|
||||
}
|
||||
|
||||
apiResponse(endpoint, statusCode, duration) {
|
||||
return this.log(LOG_LEVELS.INFO, LOG_CATEGORIES.API, `Response ${endpoint}`, {
|
||||
endpoint,
|
||||
statusCode,
|
||||
duration: `${duration}ms`
|
||||
});
|
||||
}
|
||||
|
||||
apiError(endpoint, error) {
|
||||
return this.log(LOG_LEVELS.ERROR, LOG_CATEGORIES.API, `API Error ${endpoint}`, {
|
||||
endpoint,
|
||||
error: error.message,
|
||||
stack: error.stack
|
||||
});
|
||||
}
|
||||
|
||||
// AI Operations logging
|
||||
aiRequest(operation, model, tokens = 0) {
|
||||
return this.log(LOG_LEVELS.INFO, LOG_CATEGORIES.AI, `AI Request: ${operation}`, {
|
||||
operation,
|
||||
model,
|
||||
tokens
|
||||
});
|
||||
}
|
||||
|
||||
aiResponse(operation, success, duration) {
|
||||
return this.log(LOG_LEVELS.INFO, LOG_CATEGORIES.AI, `AI Response: ${operation}`, {
|
||||
operation,
|
||||
success,
|
||||
duration: `${duration}ms`
|
||||
});
|
||||
}
|
||||
|
||||
aiError(operation, error) {
|
||||
return this.log(LOG_LEVELS.ERROR, LOG_CATEGORIES.AI, `AI Error: ${operation}`, {
|
||||
operation,
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
|
||||
// UI Events logging
|
||||
uiEvent(action, component, details = {}) {
|
||||
return this.log(LOG_LEVELS.DEBUG, LOG_CATEGORIES.UI, `UI Event: ${action}`, {
|
||||
action,
|
||||
component,
|
||||
...details
|
||||
});
|
||||
}
|
||||
|
||||
// Storage operations
|
||||
storageWrite(operation, journalId) {
|
||||
return this.log(LOG_LEVELS.INFO, LOG_CATEGORIES.STORAGE, `Storage Write: ${operation}`, {
|
||||
operation,
|
||||
journalId
|
||||
});
|
||||
}
|
||||
|
||||
storageRead(operation, journalId) {
|
||||
return this.log(LOG_LEVELS.INFO, LOG_CATEGORIES.STORAGE, `Storage Read: ${operation}`, {
|
||||
operation,
|
||||
journalId
|
||||
});
|
||||
}
|
||||
|
||||
storageError(operation, error) {
|
||||
return this.log(LOG_LEVELS.ERROR, LOG_CATEGORIES.STORAGE, `Storage Error: ${operation}`, {
|
||||
operation,
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
|
||||
// Export operations
|
||||
exportOperation(type, format, size) {
|
||||
return this.log(LOG_LEVELS.INFO, LOG_CATEGORIES.EXPORT, `Export: ${type}`, {
|
||||
type,
|
||||
format,
|
||||
size: `${size} bytes`
|
||||
});
|
||||
}
|
||||
|
||||
// System events
|
||||
systemStart(port) {
|
||||
return this.log(LOG_LEVELS.INFO, LOG_CATEGORIES.SYSTEM, 'Application started', {
|
||||
port,
|
||||
nodeVersion: process.version,
|
||||
platform: process.platform
|
||||
});
|
||||
}
|
||||
|
||||
systemError(message, error) {
|
||||
return this.log(LOG_LEVELS.ERROR, LOG_CATEGORIES.SYSTEM, message, {
|
||||
error: error.message,
|
||||
stack: error.stack
|
||||
});
|
||||
}
|
||||
|
||||
// Generic logging methods
|
||||
error(category, message, metadata = {}) {
|
||||
return this.log(LOG_LEVELS.ERROR, category, message, metadata);
|
||||
}
|
||||
|
||||
warn(category, message, metadata = {}) {
|
||||
return this.log(LOG_LEVELS.WARN, category, message, metadata);
|
||||
}
|
||||
|
||||
info(category, message, metadata = {}) {
|
||||
return this.log(LOG_LEVELS.INFO, category, message, metadata);
|
||||
}
|
||||
|
||||
debug(category, message, metadata = {}) {
|
||||
return this.log(LOG_LEVELS.DEBUG, category, message, metadata);
|
||||
}
|
||||
}
|
||||
|
||||
// Export singleton instance
|
||||
const logger = new Logger();
|
||||
|
||||
module.exports = {
|
||||
logger,
|
||||
LOG_LEVELS,
|
||||
LOG_CATEGORIES
|
||||
};
|
||||
@ -5,7 +5,7 @@ const { getFooter } = require('./footer');
|
||||
function getHead(){
|
||||
return `
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
@ -32,7 +32,7 @@ function getBody(){
|
||||
+ getFooter() +
|
||||
`
|
||||
<!-- Bouton scroll to top -->
|
||||
<button id="scroll-to-top" class="scroll-to-top" title="Retour en haut">⬆️</button>
|
||||
<button id="scroll-to-top" class="scroll-to-top" title="Back to top">↑</button>
|
||||
<script src="/assets/js/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user