Spaces:
Runtime error
Runtime error
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>NeuralOS Demo</title> | |
<!-- Bootstrap CSS --> | |
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"> | |
<style> | |
body { | |
padding: 20px; | |
background-color: #f8f9fa; | |
} | |
.title-container { | |
text-align: center; | |
margin-bottom: 30px; | |
} | |
.canvas-container { | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
margin-bottom: 20px; | |
} | |
.canvas-wrapper { | |
position: relative; | |
border: 3px solid #007bff; | |
border-radius: 8px; | |
background-color: #f8f9fa; | |
margin-bottom: 15px; | |
} | |
.instruction-box { | |
position: absolute; | |
top: -40px; | |
left: 50%; | |
transform: translateX(-50%); | |
background-color: #007bff; | |
color: white; | |
padding: 5px 15px; | |
border-radius: 5px; | |
font-weight: bold; | |
white-space: nowrap; | |
} | |
#displayCanvas { | |
cursor: none; | |
display: block; | |
} | |
.controls { | |
margin-top: 20px; | |
display: flex; | |
gap: 15px; | |
align-items: center; | |
justify-content: center; | |
flex-wrap: wrap; | |
} | |
.control-button { | |
cursor: pointer; | |
} | |
.step-control { | |
display: flex; | |
align-items: center; | |
gap: 10px; | |
} | |
#samplingSteps { | |
width: 70px; | |
} | |
.footer { | |
margin-top: 30px; | |
text-align: center; | |
} | |
</style> | |
</head> | |
<body> | |
<div class="container"> | |
<div class="title-container"> | |
<h1 class="mb-2">NeuralOS: Towards Simulating Operating Systems<br>via Neural Generative Models</h1> | |
<p class="mb-3"> | |
<a href="https://anonymous.4open.science/r/neural-os" target="_blank" class="text-decoration-none"> | |
Project Code: anonymous.4open.science/r/neural-os | |
</a> | |
</p> | |
</div> | |
<div class="canvas-container"> | |
<div class="canvas-wrapper"> | |
<div class="instruction-box">Move your mouse inside to interact</div> | |
<canvas id="displayCanvas" width="512" height="384"></canvas> | |
</div> | |
<div class="controls"> | |
<button id="resetButton" class="btn btn-primary control-button">Reset Simulation</button> | |
<div class="step-control"> | |
<label for="samplingSteps" class="form-label mb-0">Sampling Steps:</label> | |
<input type="number" id="samplingSteps" class="form-control" min="1" max="100" value="32"> | |
<button id="updateStepsButton" class="btn btn-secondary control-button">Update</button> | |
</div> | |
</div> | |
</div> | |
<div class="row justify-content-center"> | |
<div class="col-md-8"> | |
<div class="card"> | |
<div class="card-body"> | |
<h5 class="card-title">Instructions:</h5> | |
<ul class="mb-0"> | |
<li>Move your mouse inside the blue box to interact with NeuralOS</li> | |
<li>Click to perform left-click actions</li> | |
<li>Right-click to perform context menu actions</li> | |
<li>Use your keyboard to type within the simulated environment</li> | |
<li>Adjust sampling steps to control quality/speed tradeoff</li> | |
</ul> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<script> | |
const canvas = document.getElementById('displayCanvas'); | |
const ctx = canvas.getContext('2d'); | |
let socket; | |
let isConnected = false; | |
let reconnectAttempts = 0; | |
const MAX_RECONNECT_DELAY = 30000; // Maximum delay between reconnection attempts (30 seconds) | |
let isProcessing = false; | |
// Add flag to control trace visibility, default to false (hidden) | |
let showTrace = false; | |
// Function to draw the initial canvas state | |
function drawInitialCanvas() { | |
ctx.fillStyle = "#ffffff"; | |
ctx.fillRect(0, 0, canvas.width, canvas.height); | |
ctx.font = "18px Arial"; | |
ctx.fillStyle = "#666666"; | |
ctx.textAlign = "center"; | |
ctx.fillText("Move your mouse here to interact with NeuralOS", canvas.width/2, canvas.height/2 - 10); | |
ctx.fillText("The neural model will render in this area", canvas.width/2, canvas.height/2 + 20); | |
} | |
function connect() { | |
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; | |
socket = new WebSocket(`${protocol}//${window.location.host}/ws`); | |
socket.onopen = function(event) { | |
console.log("WebSocket connection established"); | |
isConnected = true; | |
reconnectAttempts = 0; | |
//startHeartbeat(); | |
}; | |
socket.onclose = function(event) { | |
console.log("WebSocket connection closed. Attempting to reconnect..."); | |
isConnected = false; | |
clearInterval(heartbeatInterval); | |
scheduleReconnection(); | |
}; | |
socket.onerror = function(error) { | |
console.error("WebSocket error:", error); | |
}; | |
socket.onmessage = function (event) { | |
const data = JSON.parse(event.data); | |
if (data.type === "heartbeat_response") { | |
console.log("Heartbeat response received"); | |
} else if (data.image) { | |
let img = new Image(); | |
img.onload = function() { | |
ctx.clearRect(0, 0, canvas.width, canvas.height); | |
ctx.drawImage(img, 0, 0); | |
//isProcessing = false; // Reset the processing flag when we get a response | |
}; | |
img.src = 'data:image/png;base64,' + data.image; | |
} else if (data.type === "reset_confirmed") { | |
console.log("Simulation reset confirmed by server"); | |
// Clear the canvas and reset to initial state | |
drawInitialCanvas(); | |
} | |
}; | |
} | |
function scheduleReconnection() { | |
const delay = Math.min(1000 * Math.pow(2, reconnectAttempts), MAX_RECONNECT_DELAY); | |
console.log(`Scheduling reconnection in ${delay}ms`); | |
setTimeout(connect, delay); | |
reconnectAttempts++; | |
} | |
let heartbeatInterval; | |
function startHeartbeat() { | |
heartbeatInterval = setInterval(() => { | |
if (isConnected) { | |
try { | |
socket.send(JSON.stringify({ type: "heartbeat" })); | |
console.error("finished sending heartbeat"); | |
} catch (error) { | |
console.error("Error sending heartbeat:", error); | |
} | |
} | |
}, 1000); // Send heartbeat every 15 seconds | |
} | |
// Initial connection | |
connect(); | |
// Draw initial state on the canvas | |
drawInitialCanvas(); | |
let lastSentPosition = null; | |
let lastSentTime = 0; | |
const SEND_INTERVAL = 10; // Send updates every 50ms | |
// Track currently pressed keys | |
const pressedKeys = new Set(); | |
function sendInputState(x, y, isLeftClick = false, isRightClick = false) { | |
const currentTime = Date.now(); | |
if (isConnected && (isLeftClick || isRightClick || !lastSentPosition || currentTime - lastSentTime >= SEND_INTERVAL)) { | |
try { | |
socket.send(JSON.stringify({ | |
"x": x, | |
"y": y, | |
"is_left_click": isLeftClick, | |
"is_right_click": isRightClick, | |
"keys_down": Array.from(pressedKeys), | |
"keys_up": [], | |
})); | |
lastSentPosition = { x, y }; | |
lastSentTime = currentTime; | |
//if (isLeftClick || isRightClick) { | |
// isProcessing = true; // Block further inputs until response | |
//} | |
} catch (error) { | |
console.error("Error sending input state:", error); | |
} | |
} | |
} | |
// Capture mouse movements and clicks | |
canvas.addEventListener("mousemove", function (event) { | |
if (!isConnected || isProcessing) return; | |
let rect = canvas.getBoundingClientRect(); | |
let x = event.clientX - rect.left; | |
let y = event.clientY - rect.top; | |
// Only draw the trace if showTrace is true | |
if (showTrace && lastSentPosition) { | |
ctx.beginPath(); | |
ctx.moveTo(lastSentPosition.x, lastSentPosition.y); | |
ctx.lineTo(x, y); | |
ctx.stroke(); | |
} | |
sendInputState(x, y); | |
}); | |
canvas.addEventListener("click", function (event) { | |
if (!isConnected || isProcessing) return; | |
let rect = canvas.getBoundingClientRect(); | |
let x = event.clientX - rect.left; | |
let y = event.clientY - rect.top; | |
sendInputState(x, y, true, false); | |
}); | |
// Handle right clicks | |
canvas.addEventListener("contextmenu", function (event) { | |
event.preventDefault(); // Prevent default context menu | |
if (!isConnected || isProcessing) return; | |
let rect = canvas.getBoundingClientRect(); | |
let x = event.clientX - rect.left; | |
let y = event.clientY - rect.top; | |
sendInputState(x, y, false, true); | |
}); | |
// Track keyboard events | |
document.addEventListener("keydown", function (event) { | |
if (!isConnected || isProcessing) return; | |
// Add the key to our set of pressed keys | |
pressedKeys.add(event.key); | |
// Get the current mouse position | |
let rect = canvas.getBoundingClientRect(); | |
let x = lastSentPosition ? lastSentPosition.x : canvas.width / 2; | |
let y = lastSentPosition ? lastSentPosition.y : canvas.height / 2; | |
sendInputState(x, y); | |
}); | |
document.addEventListener("keyup", function (event) { | |
if (!isConnected) return; | |
// Remove the key from our set of pressed keys | |
pressedKeys.delete(event.key); | |
// Get the current mouse position | |
let rect = canvas.getBoundingClientRect(); | |
let x = lastSentPosition ? lastSentPosition.x : canvas.width / 2; | |
let y = lastSentPosition ? lastSentPosition.y : canvas.height / 2; | |
// For key up events, we send the key in the keys_up array | |
try { | |
socket.send(JSON.stringify({ | |
"x": x, | |
"y": y, | |
"is_left_click": false, | |
"is_right_click": false, | |
"keys_down": Array.from(pressedKeys), | |
"keys_up": [event.key], | |
})); | |
} catch (error) { | |
console.error("Error sending key up event:", error); | |
} | |
}); | |
// Graceful disconnection | |
window.addEventListener('beforeunload', function (e) { | |
if (isConnected) { | |
try { | |
//socket.send(JSON.stringify({ type: "disconnect" })); | |
//socket.close(); | |
} catch (error) { | |
console.error("Error during disconnection:", error); | |
} | |
} | |
}); | |
// Add event listener for the reset button | |
document.getElementById('resetButton').addEventListener('click', function() { | |
if (socket && socket.readyState === WebSocket.OPEN) { | |
console.log("Sending reset command to server"); | |
socket.send(JSON.stringify({ | |
type: "reset" | |
})); | |
} else { | |
console.error("WebSocket not connected, cannot reset"); | |
} | |
}); | |
// Add event listener for updating sampling steps | |
document.getElementById('updateStepsButton').addEventListener('click', function() { | |
const stepsInput = document.getElementById('samplingSteps'); | |
const newSteps = parseInt(stepsInput.value, 10); | |
if (isNaN(newSteps) || newSteps < 1) { | |
alert("Please enter a valid number of steps (minimum 1)"); | |
return; | |
} | |
if (socket && socket.readyState === WebSocket.OPEN) { | |
console.log(`Sending update to set sampling steps to ${newSteps}`); | |
socket.send(JSON.stringify({ | |
type: "update_sampling_steps", | |
steps: newSteps | |
})); | |
} else { | |
console.error("WebSocket not connected, cannot update steps"); | |
} | |
}); | |
</script> | |
<!-- Bootstrap JS (optional) --> | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script> | |
</body> | |
</html> |