681 lines
23 KiB
HTML
681 lines
23 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="fr">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Test Interface Modulaire - SEO Generator</title>
|
|
<style>
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
body {
|
|
font-family: 'SF Pro Display', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
min-height: 100vh;
|
|
color: #333;
|
|
}
|
|
|
|
.container {
|
|
max-width: 1400px;
|
|
margin: 0 auto;
|
|
padding: 20px;
|
|
display: grid;
|
|
grid-template-columns: 400px 1fr;
|
|
gap: 20px;
|
|
min-height: 100vh;
|
|
}
|
|
|
|
.controls-panel {
|
|
background: white;
|
|
border-radius: 15px;
|
|
padding: 25px;
|
|
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
|
|
height: fit-content;
|
|
position: sticky;
|
|
top: 20px;
|
|
}
|
|
|
|
.results-panel {
|
|
background: white;
|
|
border-radius: 15px;
|
|
padding: 25px;
|
|
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
h1 {
|
|
color: #4a5568;
|
|
margin-bottom: 25px;
|
|
text-align: center;
|
|
font-size: 1.8em;
|
|
}
|
|
|
|
h2 {
|
|
color: #2d3748;
|
|
margin: 20px 0 15px 0;
|
|
font-size: 1.2em;
|
|
border-bottom: 2px solid #e2e8f0;
|
|
padding-bottom: 8px;
|
|
}
|
|
|
|
.form-group {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
label {
|
|
display: block;
|
|
font-weight: 600;
|
|
margin-bottom: 8px;
|
|
color: #4a5568;
|
|
}
|
|
|
|
select, input[type="number"] {
|
|
width: 100%;
|
|
padding: 12px;
|
|
border: 2px solid #e2e8f0;
|
|
border-radius: 8px;
|
|
font-size: 14px;
|
|
transition: border-color 0.2s;
|
|
}
|
|
|
|
select:focus, input[type="number"]:focus {
|
|
outline: none;
|
|
border-color: #667eea;
|
|
}
|
|
|
|
.option-group {
|
|
background: #f7fafc;
|
|
padding: 15px;
|
|
border-radius: 8px;
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
.option-group h3 {
|
|
margin-bottom: 10px;
|
|
color: #2d3748;
|
|
font-size: 1em;
|
|
}
|
|
|
|
.option-description {
|
|
font-size: 12px;
|
|
color: #718096;
|
|
margin-top: 5px;
|
|
font-style: italic;
|
|
}
|
|
|
|
.button-group {
|
|
display: flex;
|
|
gap: 10px;
|
|
margin-top: 25px;
|
|
}
|
|
|
|
button {
|
|
flex: 1;
|
|
padding: 15px;
|
|
border: none;
|
|
border-radius: 8px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.btn-primary {
|
|
background: linear-gradient(135deg, #667eea, #764ba2);
|
|
color: white;
|
|
}
|
|
|
|
.btn-primary:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
|
|
}
|
|
|
|
.btn-secondary {
|
|
background: #e2e8f0;
|
|
color: #4a5568;
|
|
}
|
|
|
|
.btn-secondary:hover {
|
|
background: #cbd5e0;
|
|
}
|
|
|
|
.status {
|
|
padding: 12px;
|
|
border-radius: 8px;
|
|
margin-bottom: 20px;
|
|
font-weight: 500;
|
|
text-align: center;
|
|
display: none;
|
|
}
|
|
|
|
.status.success {
|
|
background: #c6f6d5;
|
|
color: #22543d;
|
|
border: 1px solid #9ae6b4;
|
|
}
|
|
|
|
.status.error {
|
|
background: #fed7d7;
|
|
color: #822727;
|
|
border: 1px solid #f56565;
|
|
}
|
|
|
|
.status.loading {
|
|
background: #bee3f8;
|
|
color: #2b6cb0;
|
|
border: 1px solid #63b3ed;
|
|
}
|
|
|
|
.logs-container {
|
|
background: #1a202c;
|
|
color: #e2e8f0;
|
|
border-radius: 8px;
|
|
padding: 15px;
|
|
font-family: 'Fira Code', monospace;
|
|
font-size: 12px;
|
|
height: 300px;
|
|
overflow-y: auto;
|
|
margin-bottom: 20px;
|
|
border: 1px solid #2d3748;
|
|
}
|
|
|
|
.log-entry {
|
|
margin-bottom: 4px;
|
|
padding: 2px 0;
|
|
}
|
|
|
|
.log-debug { color: #63b3ed; }
|
|
.log-info { color: #68d391; }
|
|
.log-warn { color: #f6e05e; }
|
|
.log-error { color: #fc8181; }
|
|
.log-trace { color: #a0aec0; }
|
|
|
|
.results-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
gap: 15px;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.stat-card {
|
|
background: #f7fafc;
|
|
padding: 15px;
|
|
border-radius: 8px;
|
|
text-align: center;
|
|
border: 1px solid #e2e8f0;
|
|
}
|
|
|
|
.stat-number {
|
|
font-size: 24px;
|
|
font-weight: 700;
|
|
color: #2d3748;
|
|
}
|
|
|
|
.stat-label {
|
|
font-size: 12px;
|
|
color: #718096;
|
|
margin-top: 5px;
|
|
}
|
|
|
|
.comparison-table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
margin-top: 20px;
|
|
}
|
|
|
|
.comparison-table th,
|
|
.comparison-table td {
|
|
padding: 10px;
|
|
text-align: left;
|
|
border-bottom: 1px solid #e2e8f0;
|
|
}
|
|
|
|
.comparison-table th {
|
|
background: #f7fafc;
|
|
font-weight: 600;
|
|
color: #4a5568;
|
|
}
|
|
|
|
.comparison-table td:last-child {
|
|
font-weight: 600;
|
|
}
|
|
|
|
.clear-logs {
|
|
position: absolute;
|
|
top: 10px;
|
|
right: 15px;
|
|
background: #4a5568;
|
|
color: white;
|
|
border: none;
|
|
padding: 5px 10px;
|
|
border-radius: 4px;
|
|
font-size: 11px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.progress-bar {
|
|
width: 100%;
|
|
height: 4px;
|
|
background: #e2e8f0;
|
|
border-radius: 2px;
|
|
overflow: hidden;
|
|
margin-bottom: 15px;
|
|
display: none;
|
|
}
|
|
|
|
.progress-fill {
|
|
height: 100%;
|
|
background: linear-gradient(90deg, #667eea, #764ba2);
|
|
width: 0%;
|
|
transition: width 0.3s ease;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<div class="controls-panel">
|
|
<h1>🧪 Test Interface Modulaire</h1>
|
|
|
|
<div class="form-group">
|
|
<label for="rowNumber">Ligne Google Sheet</label>
|
|
<input type="number" id="rowNumber" value="2" min="2" max="100">
|
|
<div class="option-description">Ligne à traiter du Google Sheet</div>
|
|
</div>
|
|
|
|
<div class="option-group">
|
|
<h3>🔧 Selective Enhancement</h3>
|
|
<select id="selectiveStack">
|
|
<option value="lightEnhancement">Light Enhancement</option>
|
|
<option value="standardEnhancement" selected>Standard Enhancement</option>
|
|
<option value="fullEnhancement">Full Enhancement</option>
|
|
<option value="personalityFocus">Personality Focus</option>
|
|
<option value="fluidityFocus">Fluidity Focus</option>
|
|
<option value="adaptive">Adaptive</option>
|
|
</select>
|
|
<div class="option-description">Base d'amélioration du contenu généré</div>
|
|
</div>
|
|
|
|
<div class="option-group">
|
|
<h3>🎯 Adversarial Mode</h3>
|
|
<select id="adversarialMode">
|
|
<option value="none" selected>None</option>
|
|
<option value="light">Light</option>
|
|
<option value="standard">Standard</option>
|
|
<option value="heavy">Heavy</option>
|
|
<option value="adaptive">Adaptive</option>
|
|
</select>
|
|
<div class="option-description">Techniques adversariales anti-détection</div>
|
|
</div>
|
|
|
|
<div class="option-group">
|
|
<h3>🧠 Human Simulation</h3>
|
|
<select id="humanSimulationMode">
|
|
<option value="none" selected>None</option>
|
|
<option value="lightSimulation">Light Simulation</option>
|
|
<option value="standardSimulation">Standard Simulation</option>
|
|
<option value="heavySimulation">Heavy Simulation</option>
|
|
<option value="adaptiveSimulation">Adaptive Simulation</option>
|
|
<option value="personalityFocus">Personality Focus</option>
|
|
<option value="temporalFocus">Temporal Focus</option>
|
|
</select>
|
|
<div class="option-description">Simulation comportement humain (fatigue, erreurs)</div>
|
|
</div>
|
|
|
|
<div class="option-group">
|
|
<h3>🔧 Pattern Breaking</h3>
|
|
<select id="patternBreakingMode">
|
|
<option value="none" selected>None</option>
|
|
<option value="lightPatternBreaking">Light Pattern Breaking</option>
|
|
<option value="standardPatternBreaking">Standard Pattern Breaking</option>
|
|
<option value="heavyPatternBreaking">Heavy Pattern Breaking</option>
|
|
<option value="adaptivePatternBreaking">Adaptive Pattern Breaking</option>
|
|
<option value="syntaxFocus">Syntax Focus</option>
|
|
<option value="connectorsFocus">Connectors Focus</option>
|
|
</select>
|
|
<div class="option-description">Cassage patterns syntaxiques LLM</div>
|
|
</div>
|
|
|
|
<div class="button-group">
|
|
<button class="btn-primary" onclick="runTest()">
|
|
🚀 Lancer Test
|
|
</button>
|
|
<button class="btn-secondary" onclick="runBenchmark()">
|
|
📊 Benchmark
|
|
</button>
|
|
</div>
|
|
|
|
<div class="button-group">
|
|
<button class="btn-secondary" onclick="clearResults()">
|
|
🗑️ Clear
|
|
</button>
|
|
<button class="btn-secondary" onclick="saveConfig()">
|
|
💾 Save Config
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="results-panel">
|
|
<h2>📊 Résultats</h2>
|
|
|
|
<div class="status" id="status"></div>
|
|
<div class="progress-bar" id="progressBar">
|
|
<div class="progress-fill" id="progressFill"></div>
|
|
</div>
|
|
|
|
<div class="results-grid" id="statsGrid">
|
|
<!-- Stats cards will be inserted here -->
|
|
</div>
|
|
|
|
<h3 style="margin-top: 20px;">🔍 Logs Temps Réel</h3>
|
|
<div class="logs-container" id="logsContainer" style="position: relative;">
|
|
<button class="clear-logs" onclick="clearLogs()">Clear</button>
|
|
<div id="logs"></div>
|
|
</div>
|
|
|
|
<div id="comparisonResults" style="display: none;">
|
|
<h3>📈 Comparaison Résultats</h3>
|
|
<table class="comparison-table" id="comparisonTable">
|
|
<thead>
|
|
<tr>
|
|
<th>Configuration</th>
|
|
<th>Durée (ms)</th>
|
|
<th>Selective</th>
|
|
<th>Adversarial</th>
|
|
<th>Human</th>
|
|
<th>Pattern</th>
|
|
<th>Total Modifs</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="comparisonBody">
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
let ws = null;
|
|
let testResults = [];
|
|
|
|
// WebSocket connection for real-time logs
|
|
function connectWebSocket() {
|
|
ws = new WebSocket('ws://localhost:8081');
|
|
|
|
ws.onopen = function() {
|
|
console.log('WebSocket connected');
|
|
};
|
|
|
|
ws.onmessage = function(event) {
|
|
try {
|
|
const logData = JSON.parse(event.data);
|
|
displayLog(logData);
|
|
} catch (e) {
|
|
displayLog({ level: 'info', msg: event.data });
|
|
}
|
|
};
|
|
|
|
ws.onclose = function() {
|
|
console.log('WebSocket disconnected');
|
|
setTimeout(connectWebSocket, 3000);
|
|
};
|
|
}
|
|
|
|
// Display log entry
|
|
function displayLog(logData) {
|
|
const logsDiv = document.getElementById('logs');
|
|
const logEntry = document.createElement('div');
|
|
logEntry.className = 'log-entry';
|
|
|
|
let levelClass = 'log-info';
|
|
if (logData.level <= 10) levelClass = 'log-trace';
|
|
else if (logData.level === 20) levelClass = 'log-debug';
|
|
else if (logData.level === 30) levelClass = 'log-info';
|
|
else if (logData.level === 40) levelClass = 'log-warn';
|
|
else if (logData.level >= 50) levelClass = 'log-error';
|
|
|
|
logEntry.innerHTML = `<span class="${levelClass}">${logData.msg}</span>`;
|
|
logsDiv.appendChild(logEntry);
|
|
|
|
// Auto-scroll to bottom
|
|
document.getElementById('logsContainer').scrollTop = document.getElementById('logsContainer').scrollHeight;
|
|
}
|
|
|
|
// Clear logs
|
|
function clearLogs() {
|
|
document.getElementById('logs').innerHTML = '';
|
|
}
|
|
|
|
// Show status
|
|
function showStatus(message, type) {
|
|
const statusDiv = document.getElementById('status');
|
|
statusDiv.textContent = message;
|
|
statusDiv.className = `status ${type}`;
|
|
statusDiv.style.display = 'block';
|
|
}
|
|
|
|
// Hide status
|
|
function hideStatus() {
|
|
document.getElementById('status').style.display = 'none';
|
|
}
|
|
|
|
// Show progress
|
|
function showProgress(percentage) {
|
|
const progressBar = document.getElementById('progressBar');
|
|
const progressFill = document.getElementById('progressFill');
|
|
|
|
progressBar.style.display = 'block';
|
|
progressFill.style.width = percentage + '%';
|
|
}
|
|
|
|
// Hide progress
|
|
function hideProgress() {
|
|
document.getElementById('progressBar').style.display = 'none';
|
|
}
|
|
|
|
// Display stats
|
|
function displayStats(stats) {
|
|
const statsGrid = document.getElementById('statsGrid');
|
|
statsGrid.innerHTML = '';
|
|
|
|
const statCards = [
|
|
{ label: 'Durée (ms)', value: stats.totalDuration || 0 },
|
|
{ label: 'Éléments Générés', value: stats.elementsGenerated || 0 },
|
|
{ label: 'Selective', value: stats.selectiveEnhancements || 0 },
|
|
{ label: 'Adversarial', value: stats.adversarialModifications || 0 },
|
|
{ label: 'Human Sim', value: stats.humanSimulationModifications || 0 },
|
|
{ label: 'Pattern Breaking', value: stats.patternBreakingModifications || 0 },
|
|
{ label: 'Taille Finale', value: stats.finalLength || 0 },
|
|
{ label: 'Personnalité', value: stats.personality || 'N/A' }
|
|
];
|
|
|
|
statCards.forEach(stat => {
|
|
const card = document.createElement('div');
|
|
card.className = 'stat-card';
|
|
card.innerHTML = `
|
|
<div class="stat-number">${stat.value}</div>
|
|
<div class="stat-label">${stat.label}</div>
|
|
`;
|
|
statsGrid.appendChild(card);
|
|
});
|
|
}
|
|
|
|
// Run single test
|
|
async function runTest() {
|
|
const config = {
|
|
rowNumber: parseInt(document.getElementById('rowNumber').value),
|
|
selectiveStack: document.getElementById('selectiveStack').value,
|
|
adversarialMode: document.getElementById('adversarialMode').value,
|
|
humanSimulationMode: document.getElementById('humanSimulationMode').value,
|
|
patternBreakingMode: document.getElementById('patternBreakingMode').value,
|
|
source: 'web_interface'
|
|
};
|
|
|
|
showStatus('🚀 Lancement du test en cours...', 'loading');
|
|
showProgress(10);
|
|
clearLogs();
|
|
|
|
try {
|
|
const response = await fetch('/api/test-modulaire', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(config)
|
|
});
|
|
|
|
showProgress(50);
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
}
|
|
|
|
const result = await response.json();
|
|
|
|
showProgress(100);
|
|
setTimeout(() => hideProgress(), 500);
|
|
|
|
if (result.success) {
|
|
showStatus('✅ Test terminé avec succès!', 'success');
|
|
displayStats(result.stats);
|
|
testResults.push({ config, result });
|
|
updateComparison();
|
|
} else {
|
|
showStatus('❌ Erreur: ' + (result.error || 'Erreur inconnue'), 'error');
|
|
}
|
|
|
|
} catch (error) {
|
|
hideProgress();
|
|
showStatus('❌ Erreur: ' + error.message, 'error');
|
|
console.error('Test failed:', error);
|
|
}
|
|
}
|
|
|
|
// Run benchmark
|
|
async function runBenchmark() {
|
|
showStatus('📊 Lancement benchmark...', 'loading');
|
|
showProgress(0);
|
|
clearLogs();
|
|
|
|
try {
|
|
const response = await fetch('/api/benchmark-modulaire', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
rowNumber: parseInt(document.getElementById('rowNumber').value)
|
|
})
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
}
|
|
|
|
const result = await response.json();
|
|
|
|
showProgress(100);
|
|
setTimeout(() => hideProgress(), 500);
|
|
|
|
showStatus(`✅ Benchmark terminé: ${result.results.length} tests`, 'success');
|
|
|
|
// Add all benchmark results
|
|
result.results.forEach(benchResult => {
|
|
testResults.push({
|
|
config: {
|
|
selectiveStack: benchResult.stack,
|
|
adversarialMode: benchResult.adversarial,
|
|
humanSimulationMode: benchResult.humanSimulation,
|
|
patternBreakingMode: benchResult.patternBreaking || 'none'
|
|
},
|
|
result: { stats: benchResult }
|
|
});
|
|
});
|
|
|
|
updateComparison();
|
|
|
|
} catch (error) {
|
|
hideProgress();
|
|
showStatus('❌ Erreur benchmark: ' + error.message, 'error');
|
|
console.error('Benchmark failed:', error);
|
|
}
|
|
}
|
|
|
|
// Update comparison table
|
|
function updateComparison() {
|
|
if (testResults.length === 0) return;
|
|
|
|
const comparisonDiv = document.getElementById('comparisonResults');
|
|
const tbody = document.getElementById('comparisonBody');
|
|
|
|
comparisonDiv.style.display = 'block';
|
|
tbody.innerHTML = '';
|
|
|
|
testResults.forEach((test, index) => {
|
|
const row = document.createElement('tr');
|
|
const config = `${test.config.selectiveStack} + ${test.config.adversarialMode} + ${test.config.humanSimulationMode} + ${test.config.patternBreakingMode}`;
|
|
const stats = test.result.stats;
|
|
|
|
const totalMods = (stats.selectiveEnhancements || 0) +
|
|
(stats.adversarialModifications || 0) +
|
|
(stats.humanSimulationModifications || 0) +
|
|
(stats.patternBreakingModifications || 0);
|
|
|
|
row.innerHTML = `
|
|
<td>${config}</td>
|
|
<td>${stats.totalDuration || stats.duration || 0}</td>
|
|
<td>${stats.selectiveEnhancements || 0}</td>
|
|
<td>${stats.adversarialModifications || 0}</td>
|
|
<td>${stats.humanSimulationModifications || 0}</td>
|
|
<td>${stats.patternBreakingModifications || 0}</td>
|
|
<td><strong>${totalMods}</strong></td>
|
|
`;
|
|
tbody.appendChild(row);
|
|
});
|
|
}
|
|
|
|
// Clear results
|
|
function clearResults() {
|
|
testResults = [];
|
|
document.getElementById('statsGrid').innerHTML = '';
|
|
document.getElementById('comparisonResults').style.display = 'none';
|
|
hideStatus();
|
|
hideProgress();
|
|
clearLogs();
|
|
}
|
|
|
|
// Save configuration
|
|
function saveConfig() {
|
|
const config = {
|
|
rowNumber: document.getElementById('rowNumber').value,
|
|
selectiveStack: document.getElementById('selectiveStack').value,
|
|
adversarialMode: document.getElementById('adversarialMode').value,
|
|
humanSimulationMode: document.getElementById('humanSimulationMode').value,
|
|
patternBreakingMode: document.getElementById('patternBreakingMode').value
|
|
};
|
|
|
|
localStorage.setItem('modulaire-config', JSON.stringify(config));
|
|
showStatus('💾 Configuration sauvegardée', 'success');
|
|
setTimeout(hideStatus, 2000);
|
|
}
|
|
|
|
// Load configuration
|
|
function loadConfig() {
|
|
const saved = localStorage.getItem('modulaire-config');
|
|
if (saved) {
|
|
const config = JSON.parse(saved);
|
|
document.getElementById('rowNumber').value = config.rowNumber;
|
|
document.getElementById('selectiveStack').value = config.selectiveStack;
|
|
document.getElementById('adversarialMode').value = config.adversarialMode;
|
|
document.getElementById('humanSimulationMode').value = config.humanSimulationMode;
|
|
document.getElementById('patternBreakingMode').value = config.patternBreakingMode;
|
|
}
|
|
}
|
|
|
|
// Initialize
|
|
window.onload = function() {
|
|
connectWebSocket();
|
|
loadConfig();
|
|
};
|
|
</script>
|
|
</body>
|
|
</html> |