whoa/src/app/web/shell.html
Alex Tiernan-Berry d4d359acea feat(web): add Emscripten/WASM build infrastructure
Adds the platform layer for building whoa as a WebAssembly application:

Working:
- CMake configuration for WHOA_SYSTEM_WEB with pthreads and ASYNCIFY
- Web entry point and HTML shell template
- Event loop adapted for emscripten_set_main_loop callback model
- WebSocket-based networking (WowConnection over JS WebSocket API)
- Sound system stubs (audio not yet implemented)
- FetchFS for async file loading from web server
- Freetype fixes for WASM compatibility (type mismatches)
- Input handling for web canvas

Missing (in separate commits):
- WebGPU graphics backend (CGxDeviceWebGPU)
- WGSL shaders
- API selection in Device.cpp
2026-02-06 02:21:20 +00:00

145 lines
4.3 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Whoa</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html, body {
width: 100%;
height: 100%;
overflow: hidden;
background: #000;
}
#canvas {
width: 100%;
height: 100%;
display: block;
}
#loading {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #fff;
font-family: Arial, sans-serif;
font-size: 18px;
text-align: center;
}
#loading.hidden {
display: none;
}
#progress {
width: 300px;
height: 20px;
background: #333;
border-radius: 10px;
margin-top: 20px;
overflow: hidden;
}
#progress-bar {
width: 0%;
height: 100%;
background: linear-gradient(90deg, #4a90d9, #67b26f);
transition: width 0.3s;
}
#error {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #f66;
font-family: Arial, sans-serif;
font-size: 16px;
text-align: center;
max-width: 80%;
display: none;
}
</style>
</head>
<body>
<canvas id="canvas" oncontextmenu="event.preventDefault()"></canvas>
<div id="loading">
<div>Loading...</div>
<div id="progress">
<div id="progress-bar"></div>
</div>
<div id="status"></div>
</div>
<div id="error"></div>
<script>
// Check for WebGPU support
if (!navigator.gpu) {
document.getElementById('loading').style.display = 'none';
document.getElementById('error').style.display = 'block';
document.getElementById('error').innerHTML =
'<h2>WebGPU Not Supported</h2>' +
'<p>Your browser does not support WebGPU.</p>' +
'<p>Please use a recent version of Chrome, Edge, or Firefox with WebGPU enabled.</p>';
}
var Module = {
canvas: (function() {
var canvas = document.getElementById('canvas');
// Set canvas size to match window
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
return canvas;
})(),
onRuntimeInitialized: function() {
document.getElementById('loading').classList.add('hidden');
},
setStatus: function(text) {
var statusElement = document.getElementById('status');
if (statusElement) {
statusElement.innerHTML = text;
}
// Parse progress if available
var match = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
if (match) {
var progress = parseInt(match[2]) / parseInt(match[4]) * 100;
document.getElementById('progress-bar').style.width = progress + '%';
}
},
print: function(text) {
console.log(text);
},
printErr: function(text) {
console.error(text);
},
totalDependencies: 0,
monitorRunDependencies: function(left) {
this.totalDependencies = Math.max(this.totalDependencies, left);
if (left) {
Module.setStatus('Loading... (' + (this.totalDependencies - left) + '/' + this.totalDependencies + ')');
} else {
Module.setStatus('');
}
}
};
// Handle window resize
window.addEventListener('resize', function() {
var canvas = document.getElementById('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
});
Module.setStatus('Downloading...');
</script>
{{{ SCRIPT }}}
</body>
</html>