neural-os / static /index.html
da03
.
ffcc166
raw
history blame
14.2 kB
<!DOCTYPE html>
<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>