125 lines
3.8 KiB
HTML
125 lines
3.8 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>The Workshop — The Wizard's Tower</title>
|
|
<style>
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
body { background: #0a0a0f; color: #e0d8c8; font-family: Georgia, serif; }
|
|
#scene {
|
|
width: 100vw;
|
|
height: 100vh;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
.placeholder {
|
|
text-align: center;
|
|
color: #6a6050;
|
|
}
|
|
.placeholder h1 { font-size: 1.2rem; font-weight: normal; margin-bottom: 1rem; }
|
|
.placeholder p { font-size: 0.85rem; margin-bottom: 0.5rem; }
|
|
.placeholder a { color: #8a7f6a; }
|
|
|
|
/* Connection status HUD */
|
|
#status-hud {
|
|
position: fixed;
|
|
top: 12px;
|
|
right: 12px;
|
|
background: rgba(10, 10, 15, 0.85);
|
|
border: 1px solid #2a2520;
|
|
border-radius: 6px;
|
|
padding: 8px 14px;
|
|
font-family: 'Courier New', monospace;
|
|
font-size: 0.75rem;
|
|
color: #6a6050;
|
|
z-index: 100;
|
|
min-width: 180px;
|
|
}
|
|
#status-hud .status-line {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
margin-bottom: 4px;
|
|
}
|
|
#status-hud .status-line:last-child { margin-bottom: 0; }
|
|
#status-hud .dot {
|
|
width: 8px;
|
|
height: 8px;
|
|
border-radius: 50%;
|
|
flex-shrink: 0;
|
|
}
|
|
#status-hud .dot.connecting { background: #b8860b; animation: pulse 1.2s ease-in-out infinite; }
|
|
#status-hud .dot.online { background: #4a9; }
|
|
#status-hud .dot.offline { background: #a44; }
|
|
@keyframes pulse {
|
|
0%, 100% { opacity: 1; }
|
|
50% { opacity: 0.3; }
|
|
}
|
|
#retry-btn {
|
|
display: none;
|
|
margin-top: 6px;
|
|
padding: 3px 10px;
|
|
background: #2a2520;
|
|
border: 1px solid #4a4030;
|
|
border-radius: 3px;
|
|
color: #8a7f6a;
|
|
font-family: 'Courier New', monospace;
|
|
font-size: 0.7rem;
|
|
cursor: pointer;
|
|
}
|
|
#retry-btn:hover { background: #3a3530; color: #c0b8a8; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="scene">
|
|
<div class="placeholder">
|
|
<h1>The Workshop</h1>
|
|
<p>Timmy's world is being built.</p>
|
|
<p><a href="/">← Back to the Tower</a></p>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="status-hud">
|
|
<div class="status-line">
|
|
<span class="dot connecting" id="status-dot"></span>
|
|
<span id="status-text">INITIALIZING</span>
|
|
</div>
|
|
<div class="status-line">AGENTS: <span id="agent-count">0</span></div>
|
|
<button id="retry-btn">Retry connection</button>
|
|
<a id="fallback-link" href="/api/ui" style="display:none; margin-top:6px; color:#8a7f6a; font-size:0.7rem;">Open API dashboard →</a>
|
|
</div>
|
|
|
|
<!-- Reject unknown sub-paths: only /world/ is valid -->
|
|
<script>
|
|
(function() {
|
|
var path = window.location.pathname.replace(/\/+$/, '') || '/';
|
|
if (path !== '/world') {
|
|
window.location.replace('/404.html');
|
|
}
|
|
})();
|
|
</script>
|
|
<!-- Fallback: if main.js fails to load, don't leave user on INITIALIZING forever -->
|
|
<script>
|
|
(function() {
|
|
var INIT_TIMEOUT_MS = 8000;
|
|
window.__workshopBooted = false;
|
|
setTimeout(function() {
|
|
if (window.__workshopBooted) return;
|
|
var dot = document.getElementById('status-dot');
|
|
var text = document.getElementById('status-text');
|
|
var btn = document.getElementById('retry-btn');
|
|
var fallback = document.getElementById('fallback-link');
|
|
if (dot) { dot.className = 'dot offline'; }
|
|
if (text) { text.textContent = 'WORKSHOP UNREACHABLE'; }
|
|
if (btn) { btn.style.display = 'block'; }
|
|
if (fallback) { fallback.style.display = 'block'; }
|
|
}, INIT_TIMEOUT_MS);
|
|
})();
|
|
</script>
|
|
<!-- Three.js scene will mount to #scene -->
|
|
<script type="module" src="main.js"></script>
|
|
</body>
|
|
</html>
|