/* CorrectBench Results Browser */ const ResultsApp = { data: [], currentDetail: null, init() { var self = this; document.querySelectorAll(".tab-btn").forEach(function(btn) { btn.addEventListener("click", function(e) { self.switchTab(e.target.dataset.tab); }); }); document.getElementById("results-search").addEventListener("input", function(e) { self.filterResults(e.target.value); }); document.getElementById("btn-refresh-results").addEventListener("click", function() { self.loadResults(); }); document.getElementById("btn-back-results").addEventListener("click", function() { self.showResultsList(); }); }, switchTab(tabName) { document.querySelectorAll(".tab-btn").forEach(function(btn) { btn.classList.toggle("active", btn.dataset.tab === tabName); }); document.querySelectorAll(".tab-content").forEach(function(el) { el.classList.toggle("active", el.id === "tab-" + tabName); }); if (tabName === "results" && ResultsApp.data.length === 0) { ResultsApp.loadResults(); } }, loadResults() { var container = document.getElementById("results-list"); container.innerHTML = ""; fetch("/api/results/runs") .then(function(resp) { return resp.json(); }) .then(function(data) { ResultsApp.data = data; ResultsApp.renderResults(data); }) .catch(function() { container.innerHTML = '
加载失败
'; }); }, renderResults(data) { var container = document.getElementById("results-list"); var countEl = document.getElementById("results-count"); countEl.textContent = data.length + " 条记录"; if (data.length === 0) { container.innerHTML = '
暂无运行记录
'; return; } var html = ""; for (var i = 0; i < data.length; i++) { var run = data[i]; var cov = run.coverage; var covClass = "low"; if (cov !== null && cov !== undefined) { if (cov >= 80) covClass = "high"; else if (cov >= 50) covClass = "medium"; } var covText = (cov !== null && cov !== undefined) ? cov + "%" : "-"; var passClass = run.full_pass ? "pass" : "fail"; var passText = run.full_pass ? "✓ PASS" : "✗ FAIL"; var timeText = run.time ? run.time.toFixed(1) + "s" : ""; var costText = run.token_cost ? "$" + run.token_cost.toFixed(3) : ""; html += '
' ; html += '' + run.task_id + ''; html += '' + covText + ''; html += '' + passText + ''; html += '' + run.path + ''; html += '' + timeText + " " + costText + ''; html += "
"; } container.innerHTML = html; }, filterResults(query) { var q = query.toLowerCase().trim(); if (!q) { this.renderResults(this.data); return; } var filtered = this.data.filter(function(r) { return r.task_id.toLowerCase().indexOf(q) >= 0 || r.path.toLowerCase().indexOf(q) >= 0; }); this.renderResults(filtered); }, showResultsList() { document.getElementById("results-list-view").style.display = ""; document.getElementById("task-detail-view").style.display = "none"; this.currentDetail = null; }, showTaskDetail(taskPath) { var self = this; document.getElementById("results-list-view").style.display = "none"; document.getElementById("task-detail-view").style.display = ""; document.getElementById("detail-task-title").textContent = "加载中..."; document.getElementById("detail-info").innerHTML = ""; document.getElementById("code-tabs").innerHTML = ""; document.getElementById("code-viewer").textContent = ""; fetch("/api/results/task/" + encodeURIComponent(taskPath)) .then(function(resp) { return resp.json(); }) .then(function(data) { self.currentDetail = data; self.renderTaskDetail(data); }) .catch(function() { document.getElementById("detail-task-title").textContent = "加载失败"; }); }, renderTaskDetail(data) { document.getElementById("detail-task-title").textContent = data.task_id; var info = data.run_info || {}; var infoContainer = document.getElementById("detail-info"); var rows = [ ["Task ID", info.task_id || data.task_id, ""], ["Coverage", info.coverage !== undefined ? info.coverage + "%" : "-", info.coverage >= 80 ? "pass" : (info.coverage >= 50 ? "" : "fail")], ["Full Pass", info.full_pass ? "✓ Yes" : "✗ No", info.full_pass ? "pass" : "fail"], ["Circuit Type", info.circuit_type || "-", ""], ["Time", info.time ? info.time.toFixed(1) + "s" : "-", ""], ["Token Cost", info.token_cost ? "$" + info.token_cost.toFixed(3) : "-", ""], ["Prompt Tokens", info.prompt_tokens || "-", ""], ["Completion Tokens", info.completion_tokens || "-", ""], ["Max Iter", info.max_iter || "-", ""], ["Scenario Num", info.scenario_num || "-", ""], ["Reboot Times", info.reboot_times || "0", ""], ["Eval0 Pass", info.Eval0_pass ? "✓" : "✗", info.Eval0_pass ? "pass" : "fail"], ["Eval1 Pass", info.Eval1_pass ? "✓" : "✗", info.Eval1_pass ? "pass" : "fail"], ["Eval2 Pass", info.Eval2_pass ? "✓" : "✗", info.Eval2_pass ? "pass" : "fail"], ["Eval2 Ratio", info.Eval2_ratio || "-", ""], ["TB Corrected", info.TB_corrected ? "Yes" : "No", ""], ["Incomplete", info["ERROR(incomplete)"] ? "Yes" : "No", ""] ]; var infoHtml = ""; for (var i = 0; i < rows.length; i++) { var r = rows[i]; infoHtml += '
' + r[0] + '' + r[1] + '
'; } infoContainer.innerHTML = infoHtml; var tabs = []; if (data.final_tb) tabs.push({id:"final_tb", label:"final_TB.v", code:data.final_tb, lang:"verilog"}); if (data.dut) tabs.push({id:"dut", label:"DUT.v", code:data.dut, lang:"verilog"}); if (data.final_tb_py) tabs.push({id:"final_tb_py", label:"final_TB.py", code:data.final_tb_py, lang:"python"}); var tabsContainer = document.getElementById("code-tabs"); var tabsHtml = ""; for (var j = 0; j < tabs.length; j++) { tabsHtml += ''; } tabsContainer.innerHTML = tabsHtml; var self = this; tabsContainer.querySelectorAll(".code-tab").forEach(function(tab) { tab.addEventListener("click", function(e) { tabsContainer.querySelectorAll(".code-tab").forEach(function(t) { t.classList.remove("active"); }); e.target.classList.add("active"); var codeId = e.target.dataset.codeId; var tabData = tabs.find(function(t) { return t.id === codeId; }); if (tabData) self.renderCode(tabData.code, tabData.lang); }); }); if (tabs.length > 0) { this.renderCode(tabs[0].code, tabs[0].lang); } else { document.getElementById("code-viewer").textContent = "// 无代码文件"; } }, renderCode(code, lang) { var viewer = document.getElementById("code-viewer"); if (lang === "verilog") viewer.innerHTML = this.highlightVerilog(code); else if (lang === "python") viewer.innerHTML = this.highlightPython(code); else viewer.textContent = code; }, escapeHtml(text) { return text.replace(/&/g, "&").replace(//g, ">"); }, highlightVerilog(code) { var html = this.escapeHtml(code); html = html.replace(/(\\/\\.*$)/gm, '$1'); html = html.replace(/(\`\w+)/g, '$1'); html = html.replace(/(".*?")/g, '$1'); html = html.replace(/\b(\d+'[bBhHdDoO]\w+|\d+)\b/g, '$1'); var kw = ["module","endmodule","input","output","reg","wire","integer","always","begin","end","if","else","case","endcase","for","while","initial","final","assign","parameter","localparam","function","endfunction","task","endtask","posedge","negedge","or","and","not","xor","forever","repeat","disable","wait","fork","join"]; html = html.replace(new RegExp(\b( + kw.join("|") + )\b, "g"), '$1'); html = html.replace(/(\$\w+)/g, '$1'); return html; }, highlightPython(code) { var html = this.escapeHtml(code); html = html.replace(/(#.*$)/gm, '$1'); html = html.replace(/("""[\s\S]*?"""|'''[\s\S]*?''')/g, '$1'); html = html.replace(/("[^"\n]*"|'[^'\n]*')/g, '$1'); var kw = ["def","class","import","from","return","if","elif","else","for","while","try","except","finally","with","as","yield","lambda","pass","break","continue","raise","assert","global","nonlocal","del","in","not","and","or","is","True","False","None"]; html = html.replace(new RegExp(\b( + kw.join("|") + )\b, "g"), '$1'); html = html.replace(/\b(\d+\.\?\d*)\b/g, '$1'); html = html.replace(/(@\w+)/g, '$1'); return html; } };