/* comparison-view.js — Renderização visual (tabelas + gráficos) de uma
comparação de benchmarks. Não sabe nada sobre como a comparação foi
montada nem sobre relatórios — recebe só { tables, charts, summary } e
devolve HTML. Reaproveitado pela pré-visualização (Benchmarks) e pelo
detalhe de um relatório salvo (Relatórios). */
const ComparisonView = (() => {
const esc = UI.escapeHtml;
const dash = (v, suf) => (v === null || v === undefined ? "indisponível" : `${v}${suf || ""}`);
function renderGeneralTable(rows) {
return `
| Perfil | Modelo | Tipo | Data | Status | Duração total |
${rows.map((r) => `
| ${esc(r.profile)} |
${esc(r.model)} |
${esc(r.type)} |
${esc(UI.dt(r.date))} |
${UI.benchBadge(r.status)} |
${UI.duration(r.durationS)} |
`).join("")}
`;
}
function renderPerformanceTable(rows) {
return `
| Perfil | Tokens/s | Tempo total | Latência média |
CPU médio | RAM máx | GPU médio | VRAM máx |
${rows.map((r) => `
| ${esc(r.profile)} |
${dash(r.tokensPerSecond, " tok/s")} |
${UI.duration(r.totalS)} |
${dash(r.avgLatencyS, " s")} |
${dash(r.avgCpuPct, "%")} |
${dash(r.maxRamGb, " GB")} |
${dash(r.avgGpuPct, "%")} |
${dash(r.maxVramMib, " MiB")} |
`).join("")}
`;
}
function renderConfigTable(rows) {
return `
| Perfil | Modelo | Quantização | Camadas GPU |
Contexto | Threads | Batch size | Comando |
${rows.map((r) => `
| ${esc(r.profile)} |
${esc(r.model)} |
${dash(r.quantization)} |
${dash(r.nGpuLayers)} |
${dash(r.ctxSize)} |
${dash(r.threads)} |
${dash(r.batchSize)} |
${esc(r.command)} |
`).join("")}
`;
}
function renderTables(tables) {
return `
Tabela geral
${renderGeneralTable(tables.general || [])}
Tabela de desempenho
${renderPerformanceTable(tables.performance || [])}
Tabela de configuração
${renderConfigTable(tables.config || [])}
`;
}
// Gráfico de barras simples (sem dependências externas).
function renderBarChart(title, series, unit) {
if (!series || !series.available) {
return `${esc(title)}
Métrica indisponível para os benchmarks selecionados.
`;
}
const points = series.points || [];
const max = Math.max(1, ...points.map((p) => (p.value != null ? p.value : 0)));
return `${esc(title)}
${points.map((p) => {
const pct = p.value != null ? Math.round((p.value / max) * 100) : 0;
const label = p.value != null ? `${p.value}${unit || ""}` : "indisponível";
return `
${esc(p.label)}
${esc(label)}
`;
}).join("") || `
Sem dados.
`}
`;
}
function renderCharts(charts) {
if (!charts) return "";
return `Gráficos comparativos
${renderBarChart("Tokens/s", charts.tokensPerSecond, " tok/s")}
${renderBarChart("Tempo total", charts.totalTimeS, " s")}
${renderBarChart("RAM máxima", charts.maxRamGb, " GB")}
${renderBarChart("VRAM máxima", charts.maxVramMib, " MiB")}
${renderBarChart("CPU médio", charts.avgCpuPct, "%")}
${renderBarChart("GPU médio", charts.avgGpuPct, "%")}
`;
}
function renderSummary(summary) {
return `Resumo automático
${esc(summary || "Sem resumo disponível.")}
`;
}
// Monta a comparação completa (tabelas + gráficos + resumo) em um bloco.
function render(data) {
return `${renderSummary(data.summary)}${renderTables(data.tables || {})}${renderCharts(data.charts)}`;
}
return { render, renderTables, renderCharts, renderSummary };
})();