codeflash-internal/experiments/code_repair_dashboard.html

476 lines
16 KiB
HTML
Raw Permalink Normal View History

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Code Repair Logs Dashboard</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
background: #1a1a2e;
color: #eee;
min-height: 100vh;
padding: 20px;
}
h1 {
text-align: center;
margin-bottom: 20px;
color: #00d9ff;
}
.stats {
display: flex;
gap: 20px;
justify-content: center;
margin-bottom: 30px;
flex-wrap: wrap;
}
.stat-card {
background: #16213e;
padding: 20px 30px;
border-radius: 10px;
text-align: center;
min-width: 150px;
}
.stat-card .number {
font-size: 2em;
font-weight: bold;
color: #00d9ff;
}
.stat-card .label {
color: #888;
font-size: 0.9em;
}
.search-bar {
display: flex;
gap: 10px;
margin-bottom: 20px;
justify-content: center;
flex-wrap: wrap;
}
.search-bar input {
padding: 10px 15px;
border: none;
border-radius: 5px;
background: #16213e;
color: #eee;
width: 300px;
font-size: 1em;
}
.search-bar input:focus {
outline: 2px solid #00d9ff;
}
.trace-group {
background: #16213e;
border-radius: 10px;
margin-bottom: 20px;
overflow: hidden;
}
.trace-header {
background: #0f3460;
padding: 15px 20px;
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
transition: background 0.2s;
}
.trace-header:hover {
background: #1a4a80;
}
.trace-header .trace-id {
font-family: monospace;
color: #00d9ff;
font-size: 0.95em;
}
.trace-header .count-badge {
background: #e94560;
color: white;
padding: 3px 10px;
border-radius: 15px;
font-size: 0.85em;
}
.trace-header .arrow {
transition: transform 0.2s;
}
.trace-header.expanded .arrow {
transform: rotate(180deg);
}
.trace-content {
display: none;
padding: 0;
}
.trace-content.show {
display: block;
}
.log-entry {
border-top: 1px solid #0f3460;
padding: 20px;
}
.log-entry:first-child {
border-top: none;
}
.log-entry-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
flex-wrap: wrap;
gap: 10px;
}
.optimization-id {
font-family: monospace;
font-size: 0.8em;
color: #888;
}
.timestamp {
font-size: 0.8em;
color: #888;
}
.section {
margin-bottom: 15px;
}
.section-title {
font-weight: bold;
color: #00d9ff;
margin-bottom: 8px;
font-size: 0.9em;
text-transform: uppercase;
}
.code-block {
background: #0d1117;
border-radius: 5px;
padding: 15px;
overflow-x: auto;
font-family: 'Fira Code', 'Consolas', monospace;
font-size: 0.85em;
line-height: 1.5;
white-space: pre-wrap;
word-wrap: break-word;
max-height: 400px;
overflow-y: auto;
}
.explanation-block {
background: #1e3a5f;
border-radius: 5px;
padding: 15px;
line-height: 1.6;
font-size: 0.9em;
max-height: 300px;
overflow-y: auto;
}
.refined-code {
background: #0d2818;
border: 1px solid #2ea043;
}
.expand-all-btn {
background: #0f3460;
color: #eee;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
font-size: 0.9em;
transition: background 0.2s;
}
.expand-all-btn:hover {
background: #1a4a80;
}
.no-results {
text-align: center;
padding: 40px;
color: #888;
}
.copy-btn {
background: #333;
color: #888;
border: none;
padding: 5px 10px;
border-radius: 3px;
cursor: pointer;
font-size: 0.75em;
float: right;
margin-bottom: 5px;
}
.copy-btn:hover {
background: #444;
color: #eee;
}
.status-badges {
display: flex;
gap: 8px;
margin-top: 10px;
}
.status-badge {
padding: 4px 12px;
border-radius: 4px;
font-size: 0.8em;
font-weight: 600;
text-transform: uppercase;
}
.status-badge.passed-true {
background: #238636;
color: #fff;
}
.status-badge.passed-false {
background: #da3633;
color: #fff;
}
.status-badge.faster-true {
background: #1f6feb;
color: #fff;
}
.status-badge.faster-false {
background: #6e7681;
color: #fff;
}
.status-badge.pending {
background: #484f58;
color: #8b949e;
}
.trace-status-summary {
display: flex;
gap: 8px;
align-items: center;
}
.trace-status-icon {
font-size: 1.1em;
}
</style>
</head>
<body>
<h1>Code Repair Logs Dashboard</h1>
<div class="stats">
<div class="stat-card">
<div class="number" id="total-traces">0</div>
<div class="label">Trace Groups</div>
</div>
<div class="stat-card">
<div class="number" id="total-logs">0</div>
<div class="label">Total Logs</div>
</div>
<div class="stat-card">
<div class="number" id="avg-per-trace">0</div>
<div class="label">Avg per Trace</div>
</div>
<div class="stat-card">
<div class="number" id="passed-count" style="color: #238636;">0</div>
<div class="label">Passed</div>
</div>
<div class="stat-card">
<div class="number" id="faster-count" style="color: #1f6feb;">0</div>
<div class="label">Faster</div>
</div>
</div>
<div class="search-bar">
<input type="text" id="search" placeholder="Search by trace ID, optimization ID, or content...">
<button class="expand-all-btn" onclick="toggleAll()">Expand All</button>
</div>
<div id="dashboard"></div>
<script>
// Data will be injected here
const data = DATA_PLACEHOLDER;
let allExpanded = false;
function escapeHtml(text) {
if (!text) return '';
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
function formatDate(dateStr) {
if (!dateStr) return '';
const date = new Date(dateStr);
return date.toLocaleString();
}
function copyToClipboard(text, btn) {
navigator.clipboard.writeText(text).then(() => {
const originalText = btn.textContent;
btn.textContent = 'Copied!';
setTimeout(() => btn.textContent = originalText, 1500);
});
}
function getStatusBadge(value, label, type) {
if (value === null || value === undefined) {
return `<span class="status-badge pending">${label}: Pending</span>`;
}
const isTrue = value === 'True' || value === 'true' || value === true || value === 'yes';
const className = `${type}-${isTrue}`;
const displayValue = isTrue ? 'Yes' : 'No';
return `<span class="status-badge ${className}">${label}: ${displayValue}</span>`;
}
function getTraceStatusSummary(logs) {
const withStatus = logs.filter(l => l.passed !== null && l.passed !== undefined);
if (withStatus.length === 0) return '';
const passedCount = withStatus.filter(l => l.passed === 'True' || l.passed === 'true' || l.passed === true).length;
const fasterCount = withStatus.filter(l => l.faster === 'True' || l.faster === 'true' || l.faster === true).length;
const passedIcon = passedCount === withStatus.length ? '✓' : passedCount > 0 ? '◐' : '✗';
const fasterIcon = fasterCount === withStatus.length ? '⚡' : fasterCount > 0 ? '◐' : '';
return `<span class="trace-status-summary">
<span title="Passed: ${passedCount}/${withStatus.length}">${passedIcon}</span>
<span title="Faster: ${fasterCount}/${withStatus.length}">${fasterIcon}</span>
</span>`;
}
function renderDashboard(filteredData = data) {
const dashboard = document.getElementById('dashboard');
// Group by trace_id
const grouped = {};
filteredData.forEach(log => {
if (!grouped[log.trace_id]) {
grouped[log.trace_id] = [];
}
grouped[log.trace_id].push(log);
});
// Sort groups by most recent
const sortedTraces = Object.entries(grouped).sort((a, b) => {
const aDate = new Date(a[1][0].created_at);
const bDate = new Date(b[1][0].created_at);
return bDate - aDate;
});
// Update stats
document.getElementById('total-traces').textContent = sortedTraces.length;
document.getElementById('total-logs').textContent = filteredData.length;
document.getElementById('avg-per-trace').textContent = sortedTraces.length > 0
? (filteredData.length / sortedTraces.length).toFixed(1)
: '0';
// Calculate passed/faster stats
const withStatus = filteredData.filter(l => l.passed !== null && l.passed !== undefined);
const passedCount = withStatus.filter(l => l.passed === 'True' || l.passed === 'true' || l.passed === true).length;
const fasterCount = withStatus.filter(l => l.faster === 'True' || l.faster === 'true' || l.faster === true).length;
document.getElementById('passed-count').textContent = withStatus.length > 0 ? `${passedCount}/${withStatus.length}` : '';
document.getElementById('faster-count').textContent = withStatus.length > 0 ? `${fasterCount}/${withStatus.length}` : '';
if (sortedTraces.length === 0) {
dashboard.innerHTML = '<div class="no-results">No results found</div>';
return;
}
let html = '';
sortedTraces.forEach(([traceId, logs], idx) => {
// Sort logs within group by created_at
logs.sort((a, b) => new Date(a.created_at) - new Date(b.created_at));
html += `
<div class="trace-group">
<div class="trace-header" onclick="toggleTrace(${idx})">
<span class="trace-id">Trace: ${escapeHtml(traceId)}</span>
<div style="display: flex; align-items: center; gap: 10px;">
${getTraceStatusSummary(logs)}
<span class="count-badge">${logs.length} entries</span>
<span class="arrow"></span>
</div>
</div>
<div class="trace-content" id="trace-${idx}">
${logs.map((log, logIdx) => `
<div class="log-entry">
<div class="log-entry-header">
<span class="optimization-id">Optimization: ${escapeHtml(log.optimization_id)}</span>
<span class="timestamp">${formatDate(log.created_at)}</span>
</div>
<div class="status-badges">
${getStatusBadge(log.passed, 'Passed', 'passed')}
${getStatusBadge(log.faster, 'Faster', 'faster')}
</div>
<div class="section" style="margin-top: 15px;">
<div class="section-title">User Prompt</div>
<button class="copy-btn" onclick="copyToClipboard(data.find(d => d.optimization_id === '${log.optimization_id}').user_prompt, this)">Copy</button>
<div class="code-block">${escapeHtml(log.user_prompt)}</div>
</div>
<div class="section">
<div class="section-title">Explanation</div>
<div class="explanation-block">${escapeHtml(log.explanation)}</div>
</div>
<div class="section">
<div class="section-title">Refined Optimization</div>
<button class="copy-btn" onclick="copyToClipboard(data.find(d => d.optimization_id === '${log.optimization_id}').refined_optimization, this)">Copy</button>
<div class="code-block refined-code">${escapeHtml(log.refined_optimization)}</div>
</div>
</div>
`).join('')}
</div>
</div>`;
});
dashboard.innerHTML = html;
}
function toggleTrace(idx) {
const content = document.getElementById(`trace-${idx}`);
const header = content.previousElementSibling;
content.classList.toggle('show');
header.classList.toggle('expanded');
}
function toggleAll() {
const contents = document.querySelectorAll('.trace-content');
const headers = document.querySelectorAll('.trace-header');
allExpanded = !allExpanded;
contents.forEach(c => {
if (allExpanded) {
c.classList.add('show');
} else {
c.classList.remove('show');
}
});
headers.forEach(h => {
if (allExpanded) {
h.classList.add('expanded');
} else {
h.classList.remove('expanded');
}
});
document.querySelector('.expand-all-btn').textContent = allExpanded ? 'Collapse All' : 'Expand All';
}
document.getElementById('search').addEventListener('input', function(e) {
const query = e.target.value.toLowerCase();
if (!query) {
renderDashboard(data);
return;
}
const filtered = data.filter(log =>
(log.trace_id && log.trace_id.toLowerCase().includes(query)) ||
(log.optimization_id && log.optimization_id.toLowerCase().includes(query)) ||
(log.user_prompt && log.user_prompt.toLowerCase().includes(query)) ||
(log.explanation && log.explanation.toLowerCase().includes(query)) ||
(log.refined_optimization && log.refined_optimization.toLowerCase().includes(query))
);
renderDashboard(filtered);
});
// Initial render
renderDashboard();
</script>
</body>
</html>