forked from Rockachopa/the-matrix
Compare commits
1 Commits
main
...
claude/iss
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d759416fc6 |
60
index.html
60
index.html
@@ -22,13 +22,56 @@
|
|||||||
position: fixed; inset: 0; z-index: 100;
|
position: fixed; inset: 0; z-index: 100;
|
||||||
display: flex; align-items: center; justify-content: center;
|
display: flex; align-items: center; justify-content: center;
|
||||||
background: #000;
|
background: #000;
|
||||||
color: #00ff41; font-size: 14px; letter-spacing: 4px;
|
|
||||||
text-shadow: 0 0 12px #00ff41;
|
|
||||||
font-family: 'Courier New', monospace;
|
font-family: 'Courier New', monospace;
|
||||||
}
|
}
|
||||||
#loading-screen.hidden { display: none; }
|
#loading-screen.hidden { display: none; }
|
||||||
@keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }
|
|
||||||
#loading-screen span { animation: blink 1.2s ease-in-out infinite; }
|
.loading-content {
|
||||||
|
display: flex; flex-direction: column; align-items: center; gap: 18px;
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ASCII logo */
|
||||||
|
#ascii-logo {
|
||||||
|
white-space: pre;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: clamp(6px, 1.1vw, 13px);
|
||||||
|
line-height: 1.25;
|
||||||
|
color: #002200;
|
||||||
|
text-shadow: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
/* Lit characters glow green */
|
||||||
|
#ascii-logo .ac { color: #002200; transition: color 0.15s, text-shadow 0.15s; }
|
||||||
|
#ascii-logo .ac.lit {
|
||||||
|
color: #00ff41;
|
||||||
|
text-shadow: 0 0 8px #00ff41, 0 0 20px #00cc33;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Progress bar */
|
||||||
|
#loading-bar-track {
|
||||||
|
width: clamp(220px, 50vw, 500px);
|
||||||
|
height: 3px;
|
||||||
|
background: #001800;
|
||||||
|
border: 1px solid #003300;
|
||||||
|
border-radius: 2px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
#loading-bar-fill {
|
||||||
|
height: 100%; width: 0%;
|
||||||
|
background: linear-gradient(90deg, #00aa22, #00ff41);
|
||||||
|
box-shadow: 0 0 10px #00ff41;
|
||||||
|
transition: width 0.3s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Percent + status */
|
||||||
|
#loading-percent {
|
||||||
|
color: #00ff41; font-size: 11px; letter-spacing: 3px;
|
||||||
|
text-shadow: 0 0 8px #00ff41;
|
||||||
|
}
|
||||||
|
#loading-msg {
|
||||||
|
color: #007722; font-size: 10px; letter-spacing: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
#ui-overlay {
|
#ui-overlay {
|
||||||
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
|
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
|
||||||
@@ -162,7 +205,14 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="loading-screen"><span>INITIALIZING...</span></div>
|
<div id="loading-screen">
|
||||||
|
<div class="loading-content">
|
||||||
|
<pre id="ascii-logo"></pre>
|
||||||
|
<div id="loading-bar-track"><div id="loading-bar-fill"></div></div>
|
||||||
|
<div id="loading-percent">0%</div>
|
||||||
|
<div id="loading-msg">INITIALIZING...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<!-- WebGL context loss overlay (iPad PWA, GPU resets) -->
|
<!-- WebGL context loss overlay (iPad PWA, GPU resets) -->
|
||||||
<div id="webgl-recovery-overlay" style="display:none;position:fixed;inset:0;z-index:9999;background:rgba(0,0,0,.9);color:#00ff41;font-family:monospace;align-items:center;justify-content:center;flex-direction:column">
|
<div id="webgl-recovery-overlay" style="display:none;position:fixed;inset:0;z-index:9999;background:rgba(0,0,0,.9);color:#00ff41;font-family:monospace;align-items:center;justify-content:center;flex-direction:column">
|
||||||
<p style="font-size:1.4rem">RECOVERING WebGL CONTEXT…</p>
|
<p style="font-size:1.4rem">RECOVERING WebGL CONTEXT…</p>
|
||||||
|
|||||||
86
js/loading.js
Normal file
86
js/loading.js
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
/**
|
||||||
|
* Loading screen — ASCII art Timmy logo with progressive character reveal.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* import { initLoadingArt, setLoadingProgress } from './loading.js';
|
||||||
|
* initLoadingArt(); // call once on page load
|
||||||
|
* setLoadingProgress(50, 'MSG'); // 0–100, optional status message
|
||||||
|
*/
|
||||||
|
|
||||||
|
const TIMMY_ASCII = `
|
||||||
|
████████╗██╗███╗ ███╗███╗ ███╗██╗ ██╗
|
||||||
|
╚══██╔══╝██║████╗ ████║████╗ ████║╚██╗ ██╔╝
|
||||||
|
██║ ██║██╔████╔██║██╔████╔██║ ╚████╔╝
|
||||||
|
██║ ██║██║╚██╔╝██║██║╚██╔╝██║ ╚██╔╝
|
||||||
|
██║ ██║██║ ╚═╝ ██║██║ ╚═╝ ██║ ██║
|
||||||
|
╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝
|
||||||
|
|
||||||
|
┌───────────────────────────────┐
|
||||||
|
│ SOVEREIGN AI · SOUL ON BTC │
|
||||||
|
└───────────────────────────────┘
|
||||||
|
`.trimStart();
|
||||||
|
|
||||||
|
/** All non-whitespace char indices in the ASCII art, randomised for scatter reveal */
|
||||||
|
let _charSpans = [];
|
||||||
|
let _totalChars = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise the loading screen: build character spans for the ASCII art.
|
||||||
|
* Call once before the first setLoadingProgress().
|
||||||
|
*/
|
||||||
|
export function initLoadingArt() {
|
||||||
|
const logoEl = document.getElementById('ascii-logo');
|
||||||
|
if (!logoEl) return;
|
||||||
|
|
||||||
|
// Build span-per-char markup — whitespace stays as plain text nodes for layout.
|
||||||
|
const chars = [...TIMMY_ASCII];
|
||||||
|
let html = '';
|
||||||
|
let idx = 0;
|
||||||
|
for (const ch of chars) {
|
||||||
|
if (ch === '\n') {
|
||||||
|
html += '\n';
|
||||||
|
} else if (ch === ' ') {
|
||||||
|
html += ' ';
|
||||||
|
} else {
|
||||||
|
html += `<span class="ac" data-i="${idx}">${ch}</span>`;
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_totalChars = idx;
|
||||||
|
logoEl.innerHTML = html;
|
||||||
|
|
||||||
|
// Cache span elements for fast access
|
||||||
|
_charSpans = Array.from(logoEl.querySelectorAll('.ac'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update loading progress.
|
||||||
|
* @param {number} percent 0–100
|
||||||
|
* @param {string} [msg] Optional status line text
|
||||||
|
*/
|
||||||
|
export function setLoadingProgress(percent, msg) {
|
||||||
|
const pct = Math.max(0, Math.min(100, percent));
|
||||||
|
|
||||||
|
// How many chars should be "lit" at this progress level
|
||||||
|
const litCount = Math.round((_totalChars * pct) / 100);
|
||||||
|
|
||||||
|
for (let i = 0; i < _charSpans.length; i++) {
|
||||||
|
if (i < litCount) {
|
||||||
|
_charSpans[i].classList.add('lit');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Progress bar fill
|
||||||
|
const bar = document.getElementById('loading-bar-fill');
|
||||||
|
if (bar) bar.style.width = `${pct}%`;
|
||||||
|
|
||||||
|
// Percentage label
|
||||||
|
const pctEl = document.getElementById('loading-percent');
|
||||||
|
if (pctEl) pctEl.textContent = `${Math.round(pct)}%`;
|
||||||
|
|
||||||
|
// Status message
|
||||||
|
if (msg) {
|
||||||
|
const msgEl = document.getElementById('loading-msg');
|
||||||
|
if (msgEl) msgEl.textContent = msg;
|
||||||
|
}
|
||||||
|
}
|
||||||
22
js/main.js
22
js/main.js
@@ -8,6 +8,7 @@ import { initUI, updateUI } from './ui.js';
|
|||||||
import { initInteraction, updateControls, disposeInteraction } from './interaction.js';
|
import { initInteraction, updateControls, disposeInteraction } from './interaction.js';
|
||||||
import { initWebSocket, getConnectionState, getJobCount } from './websocket.js';
|
import { initWebSocket, getConnectionState, getJobCount } from './websocket.js';
|
||||||
import { initVisitor } from './visitor.js';
|
import { initVisitor } from './visitor.js';
|
||||||
|
import { initLoadingArt, setLoadingProgress } from './loading.js';
|
||||||
|
|
||||||
let running = false;
|
let running = false;
|
||||||
let canvas = null;
|
let canvas = null;
|
||||||
@@ -22,26 +23,40 @@ let canvas = null;
|
|||||||
* Agent state map captured just before teardown; reapplied after initAgents.
|
* Agent state map captured just before teardown; reapplied after initAgents.
|
||||||
*/
|
*/
|
||||||
function buildWorld(firstInit, stateSnapshot) {
|
function buildWorld(firstInit, stateSnapshot) {
|
||||||
|
if (firstInit) setLoadingProgress(10, 'RENDERING ENGINE...');
|
||||||
const { scene, camera, renderer } = initWorld(canvas);
|
const { scene, camera, renderer } = initWorld(canvas);
|
||||||
canvas = renderer.domElement;
|
canvas = renderer.domElement;
|
||||||
|
|
||||||
|
if (firstInit) setLoadingProgress(30, 'EFFECTS SYSTEM...');
|
||||||
initEffects(scene);
|
initEffects(scene);
|
||||||
|
|
||||||
|
if (firstInit) setLoadingProgress(50, 'SPAWNING AGENTS...');
|
||||||
initAgents(scene);
|
initAgents(scene);
|
||||||
|
|
||||||
if (stateSnapshot) {
|
if (stateSnapshot) {
|
||||||
applyAgentStates(stateSnapshot);
|
applyAgentStates(stateSnapshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (firstInit) setLoadingProgress(70, 'ARMING CONTROLS...');
|
||||||
initInteraction(camera, renderer);
|
initInteraction(camera, renderer);
|
||||||
|
|
||||||
if (firstInit) {
|
if (firstInit) {
|
||||||
|
setLoadingProgress(80, 'LOADING INTERFACE...');
|
||||||
initUI();
|
initUI();
|
||||||
|
|
||||||
|
setLoadingProgress(90, 'CONNECTING TO GRID...');
|
||||||
initWebSocket(scene);
|
initWebSocket(scene);
|
||||||
|
|
||||||
|
setLoadingProgress(95, 'INITIALIZING VISITOR...');
|
||||||
initVisitor();
|
initVisitor();
|
||||||
|
|
||||||
// Dismiss loading screen
|
setLoadingProgress(100, 'WELCOME TO TOWER WORLD');
|
||||||
|
|
||||||
|
// Dismiss loading screen after a brief moment so 100% is visible
|
||||||
const loadingScreen = document.getElementById('loading-screen');
|
const loadingScreen = document.getElementById('loading-screen');
|
||||||
if (loadingScreen) loadingScreen.classList.add('hidden');
|
if (loadingScreen) {
|
||||||
|
setTimeout(() => loadingScreen.classList.add('hidden'), 400);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debounce resize to 1 call per frame
|
// Debounce resize to 1 call per frame
|
||||||
@@ -117,6 +132,9 @@ function teardown({ scene, renderer, ac }) {
|
|||||||
function main() {
|
function main() {
|
||||||
const $overlay = document.getElementById('webgl-recovery-overlay');
|
const $overlay = document.getElementById('webgl-recovery-overlay');
|
||||||
|
|
||||||
|
initLoadingArt();
|
||||||
|
setLoadingProgress(0, 'BOOTING MATRIX...');
|
||||||
|
|
||||||
let handle = buildWorld(true, null);
|
let handle = buildWorld(true, null);
|
||||||
|
|
||||||
// WebGL context loss recovery (iPad PWA, GPU driver reset, etc.)
|
// WebGL context loss recovery (iPad PWA, GPU driver reset, etc.)
|
||||||
|
|||||||
Reference in New Issue
Block a user