Added plan.md with complete architecture for format-agnostic content generation: - Support for Markdown, HTML, Plain Text, JSON formats - New FormatExporter module with neutral data structure - Integration strategy with existing ContentAssembly and ArticleStorage - Bonus features: SEO metadata generation, readability scoring, WordPress Gutenberg format - Implementation roadmap with 4 phases (6h total estimated) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
511 lines
17 KiB
JavaScript
511 lines
17 KiB
JavaScript
// ========================================
|
|
// FICHIER: TemporalStyles.js
|
|
// RESPONSABILITÉ: Variations temporelles d'écriture
|
|
// Simulation comportement humain selon l'heure
|
|
// ========================================
|
|
|
|
const { logSh } = require('../ErrorReporting');
|
|
|
|
/**
|
|
* STYLES TEMPORELS PAR TRANCHES HORAIRES
|
|
* Simule l'énergie et style d'écriture selon l'heure
|
|
*/
|
|
const TEMPORAL_STYLES = {
|
|
|
|
// ========================================
|
|
// MATIN (6h-11h) - Énergique et Direct
|
|
// ========================================
|
|
morning: {
|
|
period: 'matin',
|
|
timeRange: [6, 11],
|
|
energy: 'high',
|
|
characteristics: {
|
|
sentenceLength: 'short', // Phrases plus courtes
|
|
vocabulary: 'dynamic', // Mots énergiques
|
|
connectors: 'direct', // Connecteurs simples
|
|
rhythm: 'fast' // Rythme soutenu
|
|
},
|
|
vocabularyPreferences: {
|
|
energy: ['dynamique', 'efficace', 'rapide', 'direct', 'actif', 'performant'],
|
|
connectors: ['donc', 'puis', 'ensuite', 'maintenant', 'immédiatement'],
|
|
modifiers: ['très', 'vraiment', 'particulièrement', 'nettement'],
|
|
actions: ['optimiser', 'accélérer', 'améliorer', 'développer', 'créer']
|
|
},
|
|
styleTendencies: {
|
|
shortSentencesBias: 0.7, // 70% chance phrases courtes
|
|
directConnectorsBias: 0.8, // 80% connecteurs simples
|
|
energyWordsBias: 0.6 // 60% mots énergiques
|
|
}
|
|
},
|
|
|
|
// ========================================
|
|
// APRÈS-MIDI (12h-17h) - Équilibré et Professionnel
|
|
// ========================================
|
|
afternoon: {
|
|
period: 'après-midi',
|
|
timeRange: [12, 17],
|
|
energy: 'medium',
|
|
characteristics: {
|
|
sentenceLength: 'medium', // Phrases équilibrées
|
|
vocabulary: 'professional', // Vocabulaire standard
|
|
connectors: 'balanced', // Connecteurs variés
|
|
rhythm: 'steady' // Rythme régulier
|
|
},
|
|
vocabularyPreferences: {
|
|
energy: ['professionnel', 'efficace', 'qualité', 'standard', 'adapté'],
|
|
connectors: ['par ailleurs', 'de plus', 'également', 'ainsi', 'cependant'],
|
|
modifiers: ['assez', 'plutôt', 'relativement', 'suffisamment'],
|
|
actions: ['réaliser', 'développer', 'analyser', 'étudier', 'concevoir']
|
|
},
|
|
styleTendencies: {
|
|
shortSentencesBias: 0.4, // 40% phrases courtes
|
|
directConnectorsBias: 0.5, // 50% connecteurs simples
|
|
energyWordsBias: 0.3 // 30% mots énergiques
|
|
}
|
|
},
|
|
|
|
// ========================================
|
|
// SOIR (18h-23h) - Détendu et Réflexif
|
|
// ========================================
|
|
evening: {
|
|
period: 'soir',
|
|
timeRange: [18, 23],
|
|
energy: 'low',
|
|
characteristics: {
|
|
sentenceLength: 'long', // Phrases plus longues
|
|
vocabulary: 'nuanced', // Vocabulaire nuancé
|
|
connectors: 'complex', // Connecteurs élaborés
|
|
rhythm: 'relaxed' // Rythme posé
|
|
},
|
|
vocabularyPreferences: {
|
|
energy: ['approfondi', 'réfléchi', 'considéré', 'nuancé', 'détaillé'],
|
|
connectors: ['néanmoins', 'cependant', 'par conséquent', 'en outre', 'toutefois'],
|
|
modifiers: ['quelque peu', 'relativement', 'dans une certaine mesure', 'assez'],
|
|
actions: ['examiner', 'considérer', 'réfléchir', 'approfondir', 'explorer']
|
|
},
|
|
styleTendencies: {
|
|
shortSentencesBias: 0.2, // 20% phrases courtes
|
|
directConnectorsBias: 0.2, // 20% connecteurs simples
|
|
energyWordsBias: 0.1 // 10% mots énergiques
|
|
}
|
|
},
|
|
|
|
// ========================================
|
|
// NUIT (0h-5h) - Fatigue et Simplicité
|
|
// ========================================
|
|
night: {
|
|
period: 'nuit',
|
|
timeRange: [0, 5],
|
|
energy: 'very_low',
|
|
characteristics: {
|
|
sentenceLength: 'short', // Phrases courtes par fatigue
|
|
vocabulary: 'simple', // Vocabulaire basique
|
|
connectors: 'minimal', // Connecteurs rares
|
|
rhythm: 'slow' // Rythme lent
|
|
},
|
|
vocabularyPreferences: {
|
|
energy: ['simple', 'basique', 'standard', 'normal', 'classique'],
|
|
connectors: ['et', 'mais', 'ou', 'donc', 'puis'],
|
|
modifiers: ['assez', 'bien', 'pas mal', 'correct'],
|
|
actions: ['faire', 'utiliser', 'prendre', 'mettre', 'avoir']
|
|
},
|
|
styleTendencies: {
|
|
shortSentencesBias: 0.8, // 80% phrases courtes
|
|
directConnectorsBias: 0.9, // 90% connecteurs simples
|
|
energyWordsBias: 0.1 // 10% mots énergiques
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* DÉTERMINER STYLE TEMPOREL SELON L'HEURE
|
|
* @param {number} currentHour - Heure actuelle (0-23)
|
|
* @returns {object} - Style temporel correspondant
|
|
*/
|
|
function getTemporalStyle(currentHour) {
|
|
// Validation heure
|
|
const hour = Math.max(0, Math.min(23, Math.floor(currentHour || new Date().getHours())));
|
|
|
|
logSh(`⏰ Détermination style temporel pour ${hour}h`, 'DEBUG');
|
|
|
|
// Déterminer période
|
|
let selectedStyle;
|
|
|
|
if (hour >= 6 && hour <= 11) {
|
|
selectedStyle = TEMPORAL_STYLES.morning;
|
|
} else if (hour >= 12 && hour <= 17) {
|
|
selectedStyle = TEMPORAL_STYLES.afternoon;
|
|
} else if (hour >= 18 && hour <= 23) {
|
|
selectedStyle = TEMPORAL_STYLES.evening;
|
|
} else {
|
|
selectedStyle = TEMPORAL_STYLES.night;
|
|
}
|
|
|
|
logSh(`⏰ Style temporel sélectionné: ${selectedStyle.period} (énergie: ${selectedStyle.energy})`, 'DEBUG');
|
|
|
|
return {
|
|
...selectedStyle,
|
|
currentHour: hour,
|
|
timestamp: new Date().toISOString()
|
|
};
|
|
}
|
|
|
|
/**
|
|
* APPLICATION STYLE TEMPOREL
|
|
* @param {string} content - Contenu à modifier
|
|
* @param {object} temporalStyle - Style temporel à appliquer
|
|
* @param {object} options - Options { intensity }
|
|
* @returns {object} - { content, modifications }
|
|
*/
|
|
function applyTemporalStyle(content, temporalStyle, options = {}) {
|
|
if (!content || !temporalStyle) {
|
|
return { content, modifications: 0 };
|
|
}
|
|
|
|
const intensity = options.intensity || 1.0;
|
|
|
|
logSh(`⏰ Application style temporel: ${temporalStyle.period} (intensité: ${intensity})`, 'DEBUG');
|
|
|
|
let modifiedContent = content;
|
|
let modifications = 0;
|
|
|
|
// ========================================
|
|
// 1. AJUSTEMENT LONGUEUR PHRASES
|
|
// ========================================
|
|
const sentenceResult = adjustSentenceLength(modifiedContent, temporalStyle, intensity);
|
|
modifiedContent = sentenceResult.content;
|
|
modifications += sentenceResult.count;
|
|
|
|
// ========================================
|
|
// 2. ADAPTATION VOCABULAIRE
|
|
// ========================================
|
|
const vocabularyResult = adaptVocabulary(modifiedContent, temporalStyle, intensity);
|
|
modifiedContent = vocabularyResult.content;
|
|
modifications += vocabularyResult.count;
|
|
|
|
// ========================================
|
|
// 3. MODIFICATION CONNECTEURS
|
|
// ========================================
|
|
const connectorResult = adjustConnectors(modifiedContent, temporalStyle, intensity);
|
|
modifiedContent = connectorResult.content;
|
|
modifications += connectorResult.count;
|
|
|
|
// ========================================
|
|
// 4. AJUSTEMENT RYTHME
|
|
// ========================================
|
|
const rhythmResult = adjustRhythm(modifiedContent, temporalStyle, intensity);
|
|
modifiedContent = rhythmResult.content;
|
|
modifications += rhythmResult.count;
|
|
|
|
logSh(`⏰ Style temporel appliqué: ${modifications} modifications`, 'DEBUG');
|
|
|
|
return {
|
|
content: modifiedContent,
|
|
modifications
|
|
};
|
|
}
|
|
|
|
/**
|
|
* AJUSTEMENT LONGUEUR PHRASES
|
|
*/
|
|
function adjustSentenceLength(content, temporalStyle, intensity) {
|
|
let modified = content;
|
|
let count = 0;
|
|
|
|
const bias = temporalStyle.styleTendencies.shortSentencesBias * intensity;
|
|
const sentences = modified.split('. ');
|
|
|
|
// Probabilité d'appliquer les modifications
|
|
if (Math.random() > intensity * 0.9) { // FIXÉ: Presque toujours appliquer (était 0.7)
|
|
return { content: modified, count };
|
|
}
|
|
|
|
const processedSentences = sentences.map(sentence => {
|
|
if (sentence.length < 20) return sentence; // Ignorer phrases très courtes
|
|
|
|
// Style MATIN/NUIT - Raccourcir phrases longues
|
|
if ((temporalStyle.period === 'matin' || temporalStyle.period === 'nuit') &&
|
|
sentence.length > 100 && Math.random() < bias) {
|
|
|
|
// Chercher point de coupe naturel
|
|
const cutPoints = [', qui', ', que', ', dont', ' et ', ' car ', ' mais '];
|
|
for (const cutPoint of cutPoints) {
|
|
const cutIndex = sentence.indexOf(cutPoint);
|
|
if (cutIndex > 30 && cutIndex < sentence.length - 30) {
|
|
count++;
|
|
logSh(` ✂️ Phrase raccourcie (${temporalStyle.period}): ${sentence.length} → ${cutIndex} chars`, 'DEBUG');
|
|
return sentence.substring(0, cutIndex) + '. ' +
|
|
sentence.substring(cutIndex + cutPoint.length);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Style SOIR - Allonger phrases courtes
|
|
if (temporalStyle.period === 'soir' &&
|
|
sentence.length > 30 && sentence.length < 80 &&
|
|
Math.random() < (1 - bias)) {
|
|
|
|
// Ajouter développements
|
|
const developments = [
|
|
', ce qui constitue un avantage notable',
|
|
', permettant ainsi d\'optimiser les résultats',
|
|
', dans une démarche d\'amélioration continue',
|
|
', contribuant à l\'efficacité globale'
|
|
];
|
|
|
|
const development = developments[Math.floor(Math.random() * developments.length)];
|
|
count++;
|
|
logSh(` 📝 Phrase allongée (soir): ${sentence.length} → ${sentence.length + development.length} chars`, 'DEBUG');
|
|
return sentence + development;
|
|
}
|
|
|
|
return sentence;
|
|
});
|
|
|
|
modified = processedSentences.join('. ');
|
|
return { content: modified, count };
|
|
}
|
|
|
|
/**
|
|
* ADAPTATION VOCABULAIRE
|
|
*/
|
|
function adaptVocabulary(content, temporalStyle, intensity) {
|
|
let modified = content;
|
|
let count = 0;
|
|
|
|
const vocabularyPrefs = temporalStyle.vocabularyPreferences;
|
|
const energyBias = temporalStyle.styleTendencies.energyWordsBias * intensity;
|
|
|
|
// Probabilité d'appliquer
|
|
if (Math.random() > intensity * 0.9) { // FIXÉ: Presque toujours appliquer (était 0.6)
|
|
return { content: modified, count };
|
|
}
|
|
|
|
// Remplacements selon période
|
|
const replacements = buildVocabularyReplacements(temporalStyle.period, vocabularyPrefs);
|
|
|
|
replacements.forEach(replacement => {
|
|
if (Math.random() < Math.max(0.6, energyBias)) { // FIXÉ: Minimum 60% chance
|
|
const regex = new RegExp(`\\b${replacement.from}\\b`, 'gi');
|
|
if (modified.match(regex)) {
|
|
modified = modified.replace(regex, replacement.to);
|
|
count++;
|
|
logSh(` 📚 Vocabulaire adapté (${temporalStyle.period}): "${replacement.from}" → "${replacement.to}"`, 'DEBUG');
|
|
}
|
|
}
|
|
});
|
|
|
|
// AJOUT FIX: Si aucun remplacement, forcer au moins une modification temporelle basique
|
|
if (count === 0 && Math.random() < 0.5) {
|
|
// Modification basique selon période
|
|
if (temporalStyle.period === 'matin' && modified.includes('utiliser')) {
|
|
modified = modified.replace(/\butiliser\b/gi, 'optimiser');
|
|
count++;
|
|
logSh(` 📚 Modification temporelle forcée: utiliser → optimiser`, 'DEBUG');
|
|
}
|
|
}
|
|
|
|
return { content: modified, count };
|
|
}
|
|
|
|
/**
|
|
* CONSTRUCTION REMPLACEMENTS VOCABULAIRE
|
|
*/
|
|
function buildVocabularyReplacements(period, vocabPrefs) {
|
|
const replacements = [];
|
|
|
|
switch (period) {
|
|
case 'matin':
|
|
replacements.push(
|
|
{ from: 'bon', to: 'excellent' },
|
|
{ from: 'intéressant', to: 'dynamique' },
|
|
{ from: 'utiliser', to: 'optimiser' },
|
|
{ from: 'faire', to: 'créer' }
|
|
);
|
|
break;
|
|
|
|
case 'soir':
|
|
replacements.push(
|
|
{ from: 'bon', to: 'considérable' },
|
|
{ from: 'faire', to: 'examiner' },
|
|
{ from: 'utiliser', to: 'exploiter' },
|
|
{ from: 'voir', to: 'considérer' }
|
|
);
|
|
break;
|
|
|
|
case 'nuit':
|
|
replacements.push(
|
|
{ from: 'excellent', to: 'bien' },
|
|
{ from: 'optimiser', to: 'utiliser' },
|
|
{ from: 'considérable', to: 'correct' },
|
|
{ from: 'examiner', to: 'regarder' }
|
|
);
|
|
break;
|
|
|
|
default: // après-midi
|
|
// Vocabulaire équilibré - pas de remplacements drastiques
|
|
break;
|
|
}
|
|
|
|
return replacements;
|
|
}
|
|
|
|
/**
|
|
* AJUSTEMENT CONNECTEURS
|
|
*/
|
|
function adjustConnectors(content, temporalStyle, intensity) {
|
|
let modified = content;
|
|
let count = 0;
|
|
|
|
const connectorBias = temporalStyle.styleTendencies.directConnectorsBias * intensity;
|
|
const preferredConnectors = temporalStyle.vocabularyPreferences.connectors;
|
|
|
|
if (Math.random() > intensity * 0.5) {
|
|
return { content: modified, count };
|
|
}
|
|
|
|
// Connecteurs selon période
|
|
const connectorMappings = {
|
|
matin: [
|
|
{ from: /par conséquent/gi, to: 'donc' },
|
|
{ from: /néanmoins/gi, to: 'mais' },
|
|
{ from: /en outre/gi, to: 'aussi' }
|
|
],
|
|
soir: [
|
|
{ from: /donc/gi, to: 'par conséquent' },
|
|
{ from: /mais/gi, to: 'néanmoins' },
|
|
{ from: /aussi/gi, to: 'en outre' }
|
|
],
|
|
nuit: [
|
|
{ from: /par conséquent/gi, to: 'donc' },
|
|
{ from: /néanmoins/gi, to: 'mais' },
|
|
{ from: /cependant/gi, to: 'mais' }
|
|
]
|
|
};
|
|
|
|
const mappings = connectorMappings[temporalStyle.period] || [];
|
|
|
|
mappings.forEach(mapping => {
|
|
if (Math.random() < connectorBias) {
|
|
if (modified.match(mapping.from)) {
|
|
modified = modified.replace(mapping.from, mapping.to);
|
|
count++;
|
|
logSh(` 🔗 Connecteur adapté (${temporalStyle.period}): "${mapping.from}" → "${mapping.to}"`, 'DEBUG');
|
|
}
|
|
}
|
|
});
|
|
|
|
return { content: modified, count };
|
|
}
|
|
|
|
/**
|
|
* AJUSTEMENT RYTHME
|
|
*/
|
|
function adjustRhythm(content, temporalStyle, intensity) {
|
|
let modified = content;
|
|
let count = 0;
|
|
|
|
// Le rythme affecte la ponctuation et les pauses
|
|
if (Math.random() > intensity * 0.3) {
|
|
return { content: modified, count };
|
|
}
|
|
|
|
switch (temporalStyle.characteristics.rhythm) {
|
|
case 'fast': // Matin - moins de virgules, plus direct
|
|
if (Math.random() < 0.4) {
|
|
// Supprimer quelques virgules non essentielles
|
|
const originalCommas = (modified.match(/,/g) || []).length;
|
|
modified = modified.replace(/, qui /gi, ' qui ');
|
|
modified = modified.replace(/, que /gi, ' que ');
|
|
const newCommas = (modified.match(/,/g) || []).length;
|
|
count = originalCommas - newCommas;
|
|
if (count > 0) {
|
|
logSh(` ⚡ Rythme accéléré: ${count} virgules supprimées`, 'DEBUG');
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'relaxed': // Soir - plus de pauses
|
|
if (Math.random() < 0.3) {
|
|
// Ajouter quelques pauses réflexives
|
|
modified = modified.replace(/\. ([A-Z])/g, '. Ainsi, $1');
|
|
count++;
|
|
logSh(` 🧘 Rythme ralenti: pauses ajoutées`, 'DEBUG');
|
|
}
|
|
break;
|
|
|
|
case 'slow': // Nuit - simplification
|
|
if (Math.random() < 0.5) {
|
|
// Simplifier structures complexes
|
|
modified = modified.replace(/ ; /g, '. ');
|
|
count++;
|
|
logSh(` 😴 Rythme simplifié: structures allégées`, 'DEBUG');
|
|
}
|
|
break;
|
|
}
|
|
|
|
return { content: modified, count };
|
|
}
|
|
|
|
/**
|
|
* ANALYSE COHÉRENCE TEMPORELLE
|
|
* @param {string} content - Contenu à analyser
|
|
* @param {object} temporalStyle - Style appliqué
|
|
* @returns {object} - Métriques de cohérence
|
|
*/
|
|
function analyzeTemporalCoherence(content, temporalStyle) {
|
|
const sentences = content.split('. ');
|
|
const avgSentenceLength = sentences.reduce((sum, s) => sum + s.length, 0) / sentences.length;
|
|
|
|
const energyWords = temporalStyle.vocabularyPreferences.energy;
|
|
const energyWordCount = energyWords.reduce((count, word) => {
|
|
const regex = new RegExp(`\\b${word}\\b`, 'gi');
|
|
return count + (content.match(regex) || []).length;
|
|
}, 0);
|
|
|
|
return {
|
|
avgSentenceLength,
|
|
energyWordDensity: energyWordCount / sentences.length,
|
|
period: temporalStyle.period,
|
|
coherenceScore: calculateCoherenceScore(avgSentenceLength, temporalStyle),
|
|
expectedCharacteristics: temporalStyle.characteristics
|
|
};
|
|
}
|
|
|
|
/**
|
|
* CALCUL SCORE COHÉRENCE
|
|
*/
|
|
function calculateCoherenceScore(avgLength, temporalStyle) {
|
|
let score = 1.0;
|
|
|
|
// Vérifier cohérence longueur phrases avec période
|
|
const expectedLength = {
|
|
'matin': { min: 40, max: 80 },
|
|
'après-midi': { min: 60, max: 120 },
|
|
'soir': { min: 80, max: 150 },
|
|
'nuit': { min: 30, max: 70 }
|
|
};
|
|
|
|
const expected = expectedLength[temporalStyle.period];
|
|
if (expected) {
|
|
if (avgLength < expected.min || avgLength > expected.max) {
|
|
score *= 0.7;
|
|
}
|
|
}
|
|
|
|
return Math.max(0, Math.min(1, score));
|
|
}
|
|
|
|
// ============= EXPORTS =============
|
|
module.exports = {
|
|
getTemporalStyle,
|
|
applyTemporalStyle,
|
|
adjustSentenceLength,
|
|
adaptVocabulary,
|
|
adjustConnectors,
|
|
adjustRhythm,
|
|
analyzeTemporalCoherence,
|
|
calculateCoherenceScore,
|
|
buildVocabularyReplacements,
|
|
TEMPORAL_STYLES
|
|
}; |