|
|
<!DOCTYPE html> |
|
|
<html lang="en"> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<title>D3 Python Visualizer</title> |
|
|
<link rel="stylesheet" href="/static/style.css"> |
|
|
<script src="https://d3js.org/d3.v7.min.js"></script> |
|
|
</head> |
|
|
<body> |
|
|
<h2>🐍 Python Visualizer with D3.js</h2> |
|
|
|
|
|
<form method="post" action="/run"> |
|
|
<textarea name="code" rows="10" cols="70" placeholder="Enter Python code here...">{{ code or '' }}</textarea><br> |
|
|
<button type="submit">Run Code</button> |
|
|
</form> |
|
|
|
|
|
{% if output %} |
|
|
<h3>Output:</h3> |
|
|
<pre>{{ output }}</pre> |
|
|
{% endif %} |
|
|
|
|
|
<div id="viz"></div> |
|
|
|
|
|
<script> |
|
|
const traceData = {{ trace_json | safe if trace_json else '[]' }}; |
|
|
if (traceData.length > 0) { |
|
|
const width = 700, height = 400; |
|
|
const svg = d3.select("#viz") |
|
|
.append("svg") |
|
|
.attr("width", width) |
|
|
.attr("height", height) |
|
|
.style("background", "#f8f8f8") |
|
|
.style("border-radius", "10px"); |
|
|
|
|
|
let currentStep = 0; |
|
|
|
|
|
const frameGroup = svg.append("g").attr("transform", "translate(50,80)"); |
|
|
|
|
|
function renderStep(step) { |
|
|
frameGroup.selectAll("*").remove(); |
|
|
|
|
|
const stepData = traceData[step]; |
|
|
if (stepData.error) { |
|
|
frameGroup.append("text") |
|
|
.text("Error: " + stepData.error) |
|
|
.attr("fill", "red") |
|
|
.attr("x", 50) |
|
|
.attr("y", 20); |
|
|
return; |
|
|
} |
|
|
|
|
|
frameGroup.append("text") |
|
|
.text("Line: " + stepData.line) |
|
|
.attr("x", 0) |
|
|
.attr("y", -30) |
|
|
.attr("font-weight", "bold"); |
|
|
|
|
|
const vars = Object.entries(stepData.locals); |
|
|
vars.forEach((v, i) => { |
|
|
const [name, val] = v; |
|
|
const boxY = i * 50; |
|
|
frameGroup.append("rect") |
|
|
.attr("x", 0) |
|
|
.attr("y", boxY) |
|
|
.attr("width", 200) |
|
|
.attr("height", 40) |
|
|
.attr("fill", "#fff") |
|
|
.attr("stroke", "#000"); |
|
|
|
|
|
frameGroup.append("text") |
|
|
.text(name + " = " + val) |
|
|
.attr("x", 10) |
|
|
.attr("y", boxY + 25) |
|
|
.attr("font-family", "monospace"); |
|
|
}); |
|
|
} |
|
|
|
|
|
function addButtons() { |
|
|
const controls = d3.select("body").append("div").attr("id", "controls"); |
|
|
controls.append("button").text("◀ Prev").on("click", () => { |
|
|
if (currentStep > 0) { currentStep--; renderStep(currentStep); } |
|
|
}); |
|
|
controls.append("button").text("▶ Next").on("click", () => { |
|
|
if (currentStep < traceData.length - 1) { currentStep++; renderStep(currentStep); } |
|
|
}); |
|
|
controls.append("span") |
|
|
.text(" Step " + (currentStep+1) + " of " + traceData.length) |
|
|
.attr("id", "stepInfo"); |
|
|
} |
|
|
|
|
|
addButtons(); |
|
|
renderStep(0); |
|
|
} |
|
|
</script> |
|
|
</body> |
|
|
</html> |
|
|
|
|
|
|