114 lines
3.8 KiB
HTML
114 lines
3.8 KiB
HTML
<!-- cockpit/fester-module/ui.html -->
|
|
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>Fester Cockpit</title>
|
|
<link rel="stylesheet" href="../style.css">
|
|
<script src="targets.js"></script>
|
|
<script src="../fester.js"></script>
|
|
<style>
|
|
body { font-family: sans-serif; margin:0; display:flex; height:100vh; }
|
|
#sidebar { width:250px; background:#222; color:#eee; padding:10px; overflow-y:auto; }
|
|
#main { flex:1; padding:10px; overflow:auto; }
|
|
.panel { margin-bottom:20px; }
|
|
.tab { cursor:pointer; padding:5px 10px; display:inline-block; color:#eee; background:#444; margin-right:2px; }
|
|
.tab.active { background:#666; }
|
|
#debugger-content { display:none; }
|
|
.debugger-controls button { margin-right:5px; }
|
|
.timeline { border:1px solid #ccc; height:200px; overflow-x:auto; white-space:nowrap; padding:5px; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="sidebar">
|
|
<div class="panel">
|
|
<div class="tab active" onclick="switchTab('targets')">Targets</div>
|
|
<div class="tab" onclick="switchTab('debugger')">Debugger</div>
|
|
</div>
|
|
<!-- Targets panel inserted by targets.js -->
|
|
</div>
|
|
|
|
<div id="main">
|
|
<div id="targets-content"></div>
|
|
<div id="debugger-content">
|
|
<h2>Debugger</h2>
|
|
<div class="debugger-controls">
|
|
<button onclick="debugStepBack()">⏮ Step Back</button>
|
|
<button onclick="debugStepForward()">⏭ Step Forward</button>
|
|
<button onclick="debugPauseResume()">⏸/▶ Pause</button>
|
|
</div>
|
|
<div class="timeline" id="debugger-timeline"></div>
|
|
<pre id="debugger-state" style="border:1px solid #ccc; padding:5px; margin-top:10px;"></pre>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Tab switching
|
|
function switchTab(tab) {
|
|
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
|
|
document.querySelectorAll('#targets-content, #debugger-content').forEach(c => c.style.display = 'none');
|
|
|
|
if(tab === 'targets') {
|
|
document.querySelector('.tab[onclick*="targets"]').classList.add('active');
|
|
document.getElementById('targets-content').style.display = 'block';
|
|
} else if(tab === 'debugger') {
|
|
document.querySelector('.tab[onclick*="debugger"]').classList.add('active');
|
|
document.getElementById('debugger-content').style.display = 'block';
|
|
fetchDebuggerState();
|
|
}
|
|
}
|
|
|
|
// Debugger WebSocket
|
|
let wsDebugger = new WebSocket("ws://localhost:8080/ws-debugger");
|
|
let paused = false;
|
|
|
|
wsDebugger.onmessage = function(msg) {
|
|
const event = JSON.parse(msg.data);
|
|
if(event.type === 'timeline-update') {
|
|
renderTimeline(event.data);
|
|
updateDebuggerState(event.data.current);
|
|
}
|
|
};
|
|
|
|
function renderTimeline(events) {
|
|
const timeline = document.getElementById('debugger-timeline');
|
|
timeline.innerHTML = '';
|
|
events.forEach((ev,i) => {
|
|
const div = document.createElement('span');
|
|
div.style.display = 'inline-block';
|
|
div.style.width = '20px';
|
|
div.style.height = '20px';
|
|
div.style.marginRight = '2px';
|
|
div.style.background = ev.completed ? '#0f0' : '#555';
|
|
div.title = `${i}: ${ev.name}`;
|
|
timeline.appendChild(div);
|
|
});
|
|
}
|
|
|
|
function updateDebuggerState(currentEvent) {
|
|
const stateBox = document.getElementById('debugger-state');
|
|
stateBox.textContent = JSON.stringify(currentEvent, null, 2);
|
|
}
|
|
|
|
// Debugger controls
|
|
function debugStepBack() {
|
|
wsDebugger.send(JSON.stringify({ action: 'step_back' }));
|
|
}
|
|
function debugStepForward() {
|
|
wsDebugger.send(JSON.stringify({ action: 'step_forward' }));
|
|
}
|
|
function debugPauseResume() {
|
|
paused = !paused;
|
|
wsDebugger.send(JSON.stringify({ action: paused ? 'pause' : 'resume' }));
|
|
console.log("Debugger " + (paused ? "paused" : "resumed"));
|
|
}
|
|
|
|
// Initial fetch
|
|
function fetchDebuggerState() {
|
|
document.getElementById('debugger-content').style.display = 'block';
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|