custom-ocean.xyz-dashboard/templates/boot.html
DJObleezy 41883f3f9c Enhance Bitcoin logo styling in DeepSea theme
Added fixed height and flexbox properties to center the Bitcoin logo. Adjusted positioning of the DeepSea ASCII art for perfect centering within the logo area.
2025-04-23 07:40:16 -07:00

992 lines
44 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ocean.xyz Pool Miner - Initializing...</title>
<link href="https://fonts.googleapis.com/css2?family=VT323&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/static/css/boot.css">
<link rel="stylesheet" href="/static/css/theme-toggle.css">
<!-- Add Theme JS -->
<script src="/static/js/theme.js"></script>
<style>
/* Config form styling - fixed width and hidden by default */
#config-form {
display: none;
width: 500px;
max-width: 90%;
margin: 30px auto;
padding: 20px;
background-color: #0d0d0d;
border: 1px solid #f7931a;
box-shadow: 0 0 10px rgba(247, 147, 26, 0.5);
border-radius: 4px;
}
/* Boot text color - updated with theme toggling */
body:not(.deepsea-theme) #terminal,
body:not(.deepsea-theme) #output,
body:not(.deepsea-theme) #prompt-container,
body:not(.deepsea-theme) #prompt-text,
body:not(.deepsea-theme) #user-input,
body:not(.deepsea-theme) #loading-message {
color: #f7931a;
text-shadow: 0 0 5px rgba(247, 147, 26, 0.4);
}
/* DeepSea theme text color */
body.deepsea-theme #terminal,
body.deepsea-theme #output,
body.deepsea-theme #prompt-container,
body.deepsea-theme #prompt-text,
body.deepsea-theme #user-input,
body.deepsea-theme #loading-message {
color: #0088cc;
text-shadow: 0 0 5px rgba(0, 136, 204, 0.4);
}
/* DeepSea cursor color */
body.deepsea-theme .cursor,
body.deepsea-theme .prompt-cursor {
background-color: #0088cc;
box-shadow: 0 0 5px rgba(0, 136, 204, 0.8);
}
/* Boot-specific DeepSea theme adjustments */
body.deepsea-theme #bitcoin-logo {
color: #0088cc;
border-color: #0088cc;
text-shadow: 0 0 10px rgba(0, 136, 204, 0.5);
box-shadow: 0 0 15px rgba(0, 136, 204, 0.5);
}
body.deepsea-theme #config-form {
border: 1px solid #0088cc;
box-shadow: 0 0 10px rgba(0, 136, 204, 0.5);
}
body.deepsea-theme .config-title {
color: #0088cc;
text-shadow: 0 0 8px rgba(0, 136, 204, 0.8);
}
body.deepsea-theme .form-group label {
color: #0088cc;
}
body.deepsea-theme .form-group input,
body.deepsea-theme .form-group select {
border: 1px solid #0088cc;
}
body.deepsea-theme .form-group input:focus,
body.deepsea-theme .form-group select:focus {
box-shadow: 0 0 5px #0088cc;
}
body.deepsea-theme .btn {
background-color: #0088cc;
}
body.deepsea-theme .btn:hover {
background-color: #00b3ff;
}
body.deepsea-theme .btn-secondary {
background-color: #333;
color: #0088cc;
}
body.deepsea-theme .tooltip .tooltip-text {
border: 1px solid #0088cc;
}
body.deepsea-theme .form-group select {
background-image: linear-gradient(45deg, transparent 50%, #0088cc 50%), linear-gradient(135deg, #0088cc 50%, transparent 50%);
}
/* DeepSea skip button */
body.deepsea-theme #skip-button {
background-color: #0088cc;
box-shadow: 0 0 8px rgba(0, 136, 204, 0.5);
}
body.deepsea-theme #skip-button:hover {
background-color: #00b3ff;
box-shadow: 0 0 12px rgba(0, 136, 204, 0.7);
}
/* Original Bitcoin styling preserved by default */
.config-title {
font-size: 24px;
text-align: center;
margin-bottom: 20px;
color: #f7931a;
text-shadow: 0 0 8px rgba(247, 147, 26, 0.8);
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
color: #f7931a;
}
.form-group input,
.form-group select {
width: 100%;
padding: 8px;
background-color: #0d0d0d;
border: 1px solid #f7931a;
color: #fff;
font-family: 'VT323', monospace;
font-size: 18px;
}
.form-group input:focus,
.form-group select:focus {
outline: none;
box-shadow: 0 0 5px #f7931a;
}
.form-actions {
display: flex;
justify-content: space-between;
margin-top: 20px;
}
.btn {
padding: 8px 16px;
background-color: #f7931a;
color: #000;
border: none;
cursor: pointer;
font-family: 'VT323', monospace;
font-size: 18px;
}
.btn:hover {
background-color: #ffa32e;
}
.btn-secondary {
background-color: #333;
color: #f7931a;
}
#form-message {
margin-top: 15px;
padding: 10px;
border-radius: 3px;
display: none;
}
.message-success {
background-color: rgba(50, 205, 50, 0.2);
border: 1px solid #32CD32;
color: #32CD32;
}
.message-error {
background-color: rgba(255, 0, 0, 0.2);
border: 1px solid #ff0000;
color: #ff0000;
}
.tooltip {
position: relative;
display: inline-block;
margin-left: 5px;
width: 14px;
height: 14px;
background-color: #333;
color: #fff;
border-radius: 50%;
text-align: center;
line-height: 14px;
font-size: 10px;
cursor: help;
}
.tooltip .tooltip-text {
visibility: hidden;
width: 200px;
background-color: #000;
color: #fff;
text-align: center;
border-radius: 3px;
padding: 5px;
position: absolute;
z-index: 1;
bottom: 125%;
left: 50%;
margin-left: -100px;
opacity: 0;
transition: opacity 0.3s;
font-size: 14px;
border: 1px solid #f7931a;
}
.tooltip:hover .tooltip-text {
visibility: visible;
opacity: 1;
}
/* Style the select dropdown with custom arrow */
.form-group select {
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
background-image: linear-gradient(45deg, transparent 50%, #f7931a 50%), linear-gradient(135deg, #f7931a 50%, transparent 50%);
background-position: calc(100% - 15px) calc(1em + 0px), calc(100% - 10px) calc(1em + 0px);
background-size: 5px 5px, 5px 5px;
background-repeat: no-repeat;
padding-right: 30px;
}
/* Base styling for the Bitcoin logo */
#bitcoin-logo {
position: relative;
white-space: pre;
font-family: monospace;
height: 130px; /* Set fixed height to match original logo */
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
/* Update the DeepSea theme logo styling */
body.deepsea-theme #bitcoin-logo {
color: transparent; /* Hide original logo */
position: relative;
text-shadow: none;
min-height: 120px; /* Ensure enough height for the new logo */
}
/* Add the new DeepSea ASCII art */
body.deepsea-theme #bitcoin-logo::after {
content: " ____ ____ \A| _ \\ ___ ___ _ __/ ___| ___ __ _ \A| | | |/ _ \\/ _ \\ '_ \\___ \\ / _ \\/ _` |\A| |_| | __/ __/ |_) |__) | __/ (_| |\A|____/ \\___|\\___|_.__/____/ \\___|\\__,_|\A|_| ";
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%); /* Center perfectly */
font-size: 100%; /* Full size */
font-weight: bold;
line-height: 1.2;
color: #0088cc;
white-space: pre;
display: block;
text-shadow: 0 0 10px rgba(0, 136, 204, 0.5);
font-family: monospace;
z-index: 1;
padding: 10px 0;
}
/* Add "DeepSea" version info */
body.deepsea-theme #bitcoin-logo::before {
content: "v.21";
position: absolute;
bottom: 0;
right: 10px;
color: #0088cc;
font-size: 16px;
text-shadow: 0 0 5px rgba(0, 136, 204, 0.5);
font-family: 'VT323', monospace;
z-index: 2; /* Ensure version displays on top */
}
</style>
</head>
<body>
<!-- Theme toggle button (new) -->
<button id="themeToggle" class="theme-toggle-btn">
<span>Toggle Theme</span>
</button>
<button id="skip-button">SKIP</button>
<div id="debug-info"></div>
<div id="loading-message">Loading mining data...</div>
<div id="bitcoin-logo">
██████╗ ████████╗ ██████╗ ██████╗ ███████╗
██╔══██╗╚══██╔══╝██╔════╝ ██╔═══██╗██╔════╝
██████╔╝ ██║ ██║ ██║ ██║███████╗
██╔══██╗ ██║ ██║ ██║ ██║╚════██║
██████╔╝ ██║ ╚██████╗ ╚██████╔╝███████║
╚═════╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝
v.21
</div>
<div id="terminal">
<div id="terminal-content">
<span id="output"></span><span class="cursor"></span>
<span id="prompt-container">
<span id="prompt-text">
Initialize mining dashboard? [Y/N]:
<span class="prompt-cursor"></span>
<input type="text" id="user-input" maxlength="1" autocomplete="off" spellcheck="false" autofocus style="font-size: 16px; font-weight: bold;">
</span>
</span>
</div>
</div>
<!-- Configuration Form -->
<div id="config-form">
<div class="config-title">MINING CONFIGURATION</div>
<div class="form-group">
<label for="wallet-address">
Bitcoin Wallet Address
<span class="tooltip">
?
<span class="tooltip-text">Your Ocean.xyz pool mining address</span>
</span>
</label>
<input type="text" id="wallet-address" placeholder="bc1..." value="">
</div>
<div class="form-group">
<label for="power-cost">
Power Cost ($/kWh)
<span class="tooltip">
?
<span class="tooltip-text">Your electricity cost per kilowatt-hour</span>
</span>
</label>
<input type="number" id="power-cost" step="0.01" min="0" placeholder="0.12" value="">
</div>
<div class="form-group">
<label for="power-usage">
Power Usage (Watts)
<span class="tooltip">
?
<span class="tooltip-text">Total power consumption of your mining equipment</span>
</span>
</label>
<input type="number" id="power-usage" step="50" min="0" placeholder="13450" value="">
</div>
<div class="form-group">
<label for="network-fee">
Network Fee (%)
<span class="tooltip">
?
<span class="tooltip-text">Additional fees beyond pool fee, like Firmware fees</span>
</span>
</label>
<input type="number" id="network-fee" step="0.1" min="0" max="10" placeholder="0.0" value="">
</div>
<div class="form-group">
<label for="timezone">
Timezone
<span class="tooltip">
?
<span class="tooltip-text">Your local timezone for displaying time information</span>
</span>
</label>
<select id="timezone" class="form-control">
<optgroup label="Common Timezones">
<option value="America/Los_Angeles">Los Angeles (Pacific Time)</option>
<option value="America/Denver">Denver (Mountain Time)</option>
<option value="America/Chicago">Chicago (Central Time)</option>
<option value="America/New_York">New York (Eastern Time)</option>
<option value="Europe/London">London (GMT/BST)</option>
<option value="Europe/Paris">Paris (Central European Time)</option>
<option value="Asia/Tokyo">Tokyo (Japan Standard Time)</option>
<option value="Australia/Sydney">Sydney (Australian Eastern Time)</option>
</optgroup>
<optgroup label="Other Timezones" id="other-timezones">
<!-- Will be populated by JavaScript -->
</optgroup>
</select>
</div>
<div id="form-message"></div>
<div class="form-actions">
<button class="btn btn-secondary" id="use-defaults">Use Defaults</button>
<button class="btn" id="save-config">Save & Continue</button>
</div>
</div>
<script>
// Theme toggle initialization
document.addEventListener('DOMContentLoaded', function () {
// Initialize theme toggle button based on current theme
const themeToggle = document.getElementById('themeToggle');
if (themeToggle) {
// Check current theme
const isDeepSea = localStorage.getItem('useDeepSeaTheme') === 'true';
// Update button style based on theme
if (isDeepSea) {
document.body.classList.add('deepsea-theme');
themeToggle.style.borderColor = '#0088cc';
themeToggle.style.color = '#0088cc';
} else {
document.body.classList.remove('deepsea-theme');
themeToggle.style.borderColor = '#f2a900';
themeToggle.style.color = '#f2a900';
}
// Add click event listener
themeToggle.addEventListener('click', function () {
toggleTheme(); // This will now trigger a page refresh
});
}
// Update terminal colors based on theme (boot.html specific)
function updateTerminalColors() {
const isDeepSeaTheme = localStorage.getItem('useDeepSeaTheme') === 'true';
if (isDeepSeaTheme) {
document.body.classList.add('deepsea-theme');
} else {
document.body.classList.remove('deepsea-theme');
}
}
// Initialize terminal colors
updateTerminalColors();
});
// Add a function to populate all available timezones
function populateTimezones() {
const otherTimezones = document.getElementById('other-timezones');
// Common timezone areas to include
const commonAreas = [
'Africa', 'America', 'Antarctica', 'Asia', 'Atlantic',
'Australia', 'Europe', 'Indian', 'Pacific'
];
// Fetch the list of available timezones
fetch('/api/available_timezones')
.then(response => response.json())
.then(data => {
if (!data.timezones || !Array.isArray(data.timezones)) {
console.error('Invalid timezone data received');
return;
}
// Sort timezones and filter to include only common areas
const sortedTimezones = data.timezones
.filter(tz => commonAreas.some(area => tz.startsWith(area + '/')))
.sort();
// Add options for each timezone (excluding those already in common list)
const commonOptions = Array.from(document.querySelectorAll('#timezone optgroup:first-child option'))
.map(opt => opt.value);
sortedTimezones.forEach(tz => {
if (!commonOptions.includes(tz)) {
const option = document.createElement('option');
option.value = tz;
option.textContent = tz.replace('_', ' ');
otherTimezones.appendChild(option);
}
});
})
.catch(error => console.error('Error fetching timezones:', error));
}
// Call this when the page loads
document.addEventListener('DOMContentLoaded', populateTimezones);
// Load the current timezone from configuration
function loadTimezoneFromConfig() {
if (currentConfig && currentConfig.timezone) {
const timezoneSelect = document.getElementById('timezone');
// First, check if the option exists
let optionExists = false;
for (let i = 0; i < timezoneSelect.options.length; i++) {
if (timezoneSelect.options[i].value === currentConfig.timezone) {
timezoneSelect.selectedIndex = i;
optionExists = true;
break;
}
}
// If the option doesn't exist yet (might be in the 'other' group being loaded)
// set a data attribute to select it when options are loaded
if (!optionExists) {
timezoneSelect.setAttribute('data-select-value', currentConfig.timezone);
}
}
}
// Call this after loading config
loadConfig().then(() => {
loadTimezoneFromConfig();
});
// Update saveConfig to include network fee
function saveConfig() {
const wallet = document.getElementById('wallet-address').value.trim();
const powerCost = parseFloat(document.getElementById('power-cost').value) || 0;
const powerUsage = parseFloat(document.getElementById('power-usage').value) || 0;
const timezone = document.getElementById('timezone').value;
const networkFee = parseFloat(document.getElementById('network-fee').value) || 0;
const updatedConfig = {
wallet: wallet || (currentConfig ? currentConfig.wallet : ""),
power_cost: powerCost,
power_usage: powerUsage,
timezone: timezone,
network_fee: networkFee
};
return fetch('/api/config', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(updatedConfig)
})
.then(response => {
if (!response.ok) {
throw new Error('Failed to save configuration');
}
return response.json();
});
}
// Debug logging
function updateDebug(message) {
document.getElementById('debug-info').textContent = message;
console.log(message);
}
// Format numbers with commas
function numberWithCommas(x) {
if (x == null) return "N/A";
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
// Global variables
let bootMessages = [];
let dashboardData = null;
let outputElement = document.getElementById('output');
const bitcoinLogo = document.getElementById('bitcoin-logo');
const skipButton = document.getElementById('skip-button');
const loadingMessage = document.getElementById('loading-message');
const promptContainer = document.getElementById('prompt-container');
const userInput = document.getElementById('user-input');
const configForm = document.getElementById('config-form');
let messageIndex = 0;
let timeoutId = null;
let waitingForUserInput = false;
let bootComplete = false;
let configLoaded = false;
let currentConfig = {
wallet: "yourwallethere",
power_cost: 0.0,
power_usage: 0.0
};
// Update loadConfig function to include network fee
function loadConfig() {
return new Promise((resolve, reject) => {
fetch('/api/config?nocache=' + new Date().getTime())
.then(response => {
if (!response.ok) {
throw new Error('Failed to load configuration: ' + response.statusText);
}
return response.json();
})
.then(data => {
console.log("Loaded configuration:", data);
currentConfig = data;
// Update form fields with latest values
document.getElementById('wallet-address').value = currentConfig.wallet || "";
document.getElementById('power-cost').value = currentConfig.power_cost || "";
document.getElementById('power-usage').value = currentConfig.power_usage || "";
document.getElementById('network-fee').value = currentConfig.network_fee || "";
configLoaded = true;
resolve(currentConfig);
})
.catch(err => {
console.error("Error loading config:", err);
// Use default values if loading fails
currentConfig = {
wallet: "yourwallethere",
power_cost: 0.0,
power_usage: 0.0,
network_fee: 0.0
};
document.getElementById('wallet-address').value = currentConfig.wallet || "";
document.getElementById('power-cost').value = currentConfig.power_cost || "";
document.getElementById('power-usage').value = currentConfig.power_usage || "";
document.getElementById('network-fee').value = currentConfig.network_fee || "";
resolve(currentConfig);
});
});
}
// Also update the save button event handler to reload the config after saving
document.getElementById('save-config').addEventListener('click', function () {
const messageElement = document.getElementById('form-message');
messageElement.style.display = 'block';
saveConfig()
.then(data => {
console.log("Configuration saved:", data);
messageElement.textContent = "Configuration saved successfully!";
messageElement.className = "message-success";
// Update currentConfig with the saved values
currentConfig = data.config || data;
setTimeout(redirectToDashboard, 1000);
})
.catch(error => {
console.error("Error saving configuration:", error);
messageElement.textContent = "Error saving configuration. Please try again.";
messageElement.className = "message-error";
});
});
// Safety timeout: redirect after 120 seconds if boot not complete
window.addEventListener('load', function () {
setTimeout(function () {
if (!bootComplete && !waitingForUserInput) {
console.warn("Safety timeout reached - redirecting to dashboard");
redirectToDashboard();
}
}, 120000);
});
// Configuration form event listeners
document.getElementById('save-config').addEventListener('click', function () {
const messageElement = document.getElementById('form-message');
messageElement.style.display = 'block';
saveConfig()
.then(data => {
console.log("Configuration saved:", data);
messageElement.textContent = "Configuration saved successfully!";
messageElement.className = "message-success";
setTimeout(redirectToDashboard, 1000);
})
.catch(error => {
console.error("Error saving configuration:", error);
messageElement.textContent = "Error saving configuration. Please try again.";
messageElement.className = "message-error";
});
});
// Update Use Defaults button handler
document.getElementById('use-defaults').addEventListener('click', function () {
// Set default values including network fee
document.getElementById('wallet-address').value = "bc1py5zmrtssheq3shd8cptpl5l5m3txxr5afynyg2gyvam6w78s4dlqqnt4v9";
document.getElementById('power-cost').value = 0.0;
document.getElementById('power-usage').value = 0.0;
document.getElementById('network-fee').value = 0.0;
// Visual feedback
const btn = document.getElementById('use-defaults');
const originalText = btn.textContent;
btn.textContent = "Defaults Applied";
btn.style.backgroundColor = "#32CD32";
setTimeout(function () {
btn.textContent = originalText;
btn.style.backgroundColor = "";
}, 1500);
});
// Redirect to dashboard
function redirectToDashboard() {
updateDebug("Boot sequence complete, redirecting...");
const baseUrl = window.location.origin;
window.location.href = baseUrl + "/dashboard";
}
// Fade in Bitcoin logo
document.addEventListener('DOMContentLoaded', function () {
setTimeout(function () {
bitcoinLogo.style.visibility = 'visible';
setTimeout(function () {
bitcoinLogo.style.opacity = '1';
}, 100);
}, 500);
// Load configuration
loadConfig();
});
// Post-confirmation messages with retro typing effect
function showPostConfirmationMessages(response) {
try {
outputElement = document.getElementById('output');
if (!outputElement) {
setTimeout(redirectToDashboard, 1000);
return;
}
// Configuration form will be shown after boot sequence
if (response.toUpperCase() === 'Y') {
const yesMessages = [
{ text: "INITIALIZING DASHBOARD...\n", html: true, delay: 400 },
{ text: "Connecting to real-time data feeds...", speed: 20, delay: 300 },
{ text: "<span class='green'>CONNECTED</span>\n", html: true, delay: 400 },
{ text: "Loading blockchain validators...", speed: 15, delay: 300 },
{ text: "<span class='green'>COMPLETE</span>\n", html: true, delay: 400 },
{ text: "Starting TX fee calculation module...", speed: 15, delay: 400 },
{ text: "<span class='green'>ACTIVE</span>\n", html: true, delay: 400 },
{ text: "Verifying BTC-USD exchange rates...", speed: 15, delay: 200 },
{ text: "<span class='green'>CURRENT RATE CONFIRMED</span>\n", html: true, delay: 300 },
{ text: "Calibrating hashrate telemetry...", speed: 15, delay: 200 },
{ text: "<span class='green'>CALIBRATED</span>\n", html: true, delay: 200 },
{ text: "Loading mining configuration...", speed: 15, delay: 200 },
{ text: "<span class='green'>LOADED</span>\n", html: true, delay: 300 },
{ text: "Preparing configuration interface...", speed: 15, delay: 800 },
{ text: "<span class='green'>READY</span>\n", html: true, delay: 500 },
{ text: "\nPlease configure your mining setup or use the default values:\n", html: true, delay: 800, showConfigForm: true }
];
let msgIndex = 0;
function processNextMessage() {
if (msgIndex >= yesMessages.length) {
return;
}
const currentMessage = yesMessages[msgIndex];
if (currentMessage.showConfigForm) {
msgIndex++;
// Show configuration form
document.getElementById('config-form').style.display = 'block';
return;
}
if (currentMessage.html) {
outputElement.innerHTML += currentMessage.text;
msgIndex++;
setTimeout(processNextMessage, currentMessage.delay || 300);
} else {
let charIndex = 0;
function typeCharacter() {
if (charIndex < currentMessage.text.length) {
outputElement.innerHTML += currentMessage.text.charAt(charIndex);
charIndex++;
setTimeout(typeCharacter, currentMessage.speed || 20);
} else {
msgIndex++;
setTimeout(processNextMessage, currentMessage.delay || 300);
}
}
typeCharacter();
}
}
setTimeout(processNextMessage, 500);
} else {
// If user selects 'N', just redirect to dashboard
outputElement.innerHTML += "N\n\nDASHBOARD INITIALIZATION ABORTED.\n";
outputElement.innerHTML += "\nUsing default configuration values.\n";
setTimeout(redirectToDashboard, 2000);
}
} catch (err) {
setTimeout(redirectToDashboard, 1000);
}
}
// Handle Y/N prompt input
userInput.addEventListener('keydown', function (e) {
if (waitingForUserInput && e.key === 'Enter') {
e.preventDefault();
const response = userInput.value.toUpperCase();
if (response === 'Y' || response === 'N') {
promptContainer.style.display = 'none';
waitingForUserInput = false;
outputElement.innerHTML += response + "\n";
userInput.value = '';
showPostConfirmationMessages(response);
}
}
});
// Show the prompt
function showUserPrompt() {
promptContainer.style.display = 'inline';
waitingForUserInput = true;
document.querySelector('.cursor').style.display = 'none';
userInput.focus();
}
// Disable truncation so all text is visible
function manageTerminalContent() { }
// Retro typing effect for boot messages
function typeBootMessages() {
try {
if (!outputElement) {
outputElement = document.getElementById('output');
if (!outputElement) {
skipButton.click();
return;
}
}
if (messageIndex >= bootMessages.length) { return; }
const currentMessage = bootMessages[messageIndex];
if (currentMessage.showPrompt) {
messageIndex++;
showUserPrompt();
return;
}
if (currentMessage.html) {
outputElement.innerHTML += currentMessage.text;
messageIndex++;
timeoutId = setTimeout(typeBootMessages, currentMessage.delay || 300);
return;
}
if (!currentMessage.typingIndex) { currentMessage.typingIndex = 0; }
if (currentMessage.typingIndex < currentMessage.text.length) {
outputElement.innerHTML += currentMessage.text.charAt(currentMessage.typingIndex);
currentMessage.typingIndex++;
timeoutId = setTimeout(typeBootMessages, currentMessage.speed || 15);
} else {
messageIndex++;
timeoutId = setTimeout(typeBootMessages, currentMessage.delay || 300);
}
} catch (err) {
messageIndex++;
timeoutId = setTimeout(typeBootMessages, 500);
}
}
// Skip button: reveal configuration form only
skipButton.addEventListener('click', function () {
clearTimeout(timeoutId);
// Optionally, clear boot messages or hide elements related to boot sequence
outputElement.innerHTML = "";
// Hide any loading or prompt messages
loadingMessage.style.display = 'none';
promptContainer.style.display = 'none';
// Show the configuration form
configForm.style.display = 'block';
});
// Start the typing animation (hides loading message)
function startTyping() {
loadingMessage.style.display = 'none';
setTimeout(typeBootMessages, 150);
}
// Fallback messages (used immediately)
function setupFallbackMessages() {
bootMessages = [
{ text: "BITCOIN OS - MINING SYSTEM - v21.000.000\n", speed: 25, delay: 300 },
{ text: "Copyright (c) 2009-2025 Satoshi Nakamoto\n", speed: 20, delay: 250 },
{ text: "All rights reserved.\n\n", speed: 25, delay: 300 },
{ text: "INITIALIZING SYSTEM...\n", speed: 25, delay: 300 },
{ text: "HARDWARE: ", speed: 25, delay: 100 },
{ text: "<span class='green'>OK</span>\n", html: true, delay: 300 },
{ text: "NETWORK: ", speed: 25, delay: 100 },
{ text: "<span class='green'>OK</span>\n", html: true, delay: 300 },
{ text: "BLOCKCHAIN: ", speed: 25, delay: 100 },
{ text: "<span class='green'>SYNCHRONIZED</span>\n", html: true, delay: 300 },
{ text: "MINING RIG: ", speed: 25, delay: 100 },
{ text: "<span class='green'>ONLINE</span>\n", html: true, delay: 300 },
{ text: "\nSystem ready. ", speed: 25, delay: 400 },
{ showPrompt: true, delay: 0 }
];
startTyping();
}
// Initialize with fallback, then try live data
setupFallbackMessages();
updateDebug("Fetching dashboard data...");
fetch('/api/metrics')
.then(response => {
if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); }
return response.json();
})
.then(data => {
dashboardData = data;
clearTimeout(timeoutId);
messageIndex = 0;
outputElement = document.getElementById('output');
outputElement.innerHTML = "";
bootMessages = [
{ text: "BITCOIN OS - MINING CONTROL SYSTEM - v21.000.000\n", speed: 25, delay: 300 },
{ text: "Copyright (c) 2009-2025 Satoshi Nakamoto & The Bitcoin Core Developers\n", speed: 20, delay: 250 },
{ text: "All rights reserved.\n\n", speed: 25, delay: 300 },
{ text: "INITIALIZING SHA-256 MINING SUBSYSTEMS...\n", speed: 25, delay: 400 },
{ text: "ASIC CLUSTER STATUS: ", speed: 15, delay: 100 },
{ text: "<span class='green'>ONLINE</span>\n", html: true, delay: 300 },
{ text: "CHIP TEMPERATURE: ", speed: 15, delay: 100 },
{ text: "<span class='green'>62°C - WITHIN OPTIMAL RANGE</span>\n", html: true, delay: 300 },
{ text: "COOLING SYSTEMS: ", speed: 15, delay: 100 },
{ text: "<span class='green'>OPERATIONAL</span>\n", html: true, delay: 300 },
{ text: "POWER SUPPLY HEALTH: ", speed: 15, delay: 100 },
{ text: "<span class='green'>98.7% - NOMINAL</span>\n", html: true, delay: 300 },
{ text: "\nCONNECTING TO BITCOIN NETWORK...\n", speed: 20, delay: 400 },
{ text: "BLOCKCHAIN SYNC STATUS: ", speed: 15, delay: 100 },
{ text: "<span class='green'>SYNCHRONIZED</span>\n", html: true, delay: 300 },
{ text: "DIFFICULTY ADJUSTMENT: ", speed: 15, delay: 100 },
{ text: "<span class='yellow'>CALCULATED</span>\n", html: true, delay: 300 },
{ text: "MEMPOOL MONITORING: ", speed: 15, delay: 100 },
{ text: "<span class='green'>ACTIVE</span>\n", html: true, delay: 300 },
{ text: "\nESTABLISHING POOL CONNECTION...\n", speed: 20, delay: 300 },
{ text: "CONNECTING TO OCEAN.XYZ...\n", speed: 20, delay: 300 },
{ text: "STRATUM PROTOCOL v2: ", speed: 15, delay: 100 },
{ text: "<span class='green'>INITIALIZED</span>\n", html: true, delay: 300 },
{ text: "POOL HASHRATE: ", speed: 15, delay: 100 },
{ text: "<span class='green'>VERIFIED</span>\n", html: true, delay: 300 },
{ text: "WORKER AUTHENTICATION: ", speed: 15, delay: 100 },
{ text: "<span class='green'>SUCCESSFUL</span>\n", html: true, delay: 300 },
{ text: "\nINITIALIZING METRICS COLLECTORS...\n", speed: 20, delay: 300 },
{ text: "HASHRATE MONITOR: ", speed: 15, delay: 100 },
{ text: "<span class='green'>ACTIVE</span>\n", html: true, delay: 300 },
{ text: "EARNINGS CALCULATOR: ", speed: 15, delay: 100 },
{ text: "<span class='green'>CALIBRATED</span>\n", html: true, delay: 300 },
{ text: "POWER USAGE TRACKING: ", speed: 15, delay: 100 },
{ text: "<span class='green'>ENABLED</span>\n", html: true, delay: 300 },
{ text: "PAYOUT THRESHOLD MONITOR: ", speed: 15, delay: 100 },
{ text: "<span class='green'>ACTIVE</span>\n", html: true, delay: 300 },
{ text: "\nCURRENT NETWORK METRICS DETECTED\n", speed: 20, delay: 300 },
{ text: "BTC PRICE: ", speed: 20, delay: 100 },
{ text: "<span class='yellow'>$" + numberWithCommas((data.btc_price || 0).toFixed(2)) + "</span>\n", html: true, delay: 300 },
{ text: "NETWORK DIFFICULTY: ", speed: 20, delay: 100 },
{ text: "<span class='white'>" + numberWithCommas(Math.round(data.difficulty || 0)) + "</span>\n", html: true, delay: 300 },
{ text: "NETWORK HASHRATE: ", speed: 20, delay: 100 },
{ text: "<span class='white'>" + (data.network_hashrate ? numberWithCommas(Math.round(data.network_hashrate)) : "N/A") + " EH/s</span>\n", html: true, delay: 300 },
{ text: "BLOCK HEIGHT: ", speed: 20, delay: 100 },
{ text: "<span class='white'>" + numberWithCommas(data.block_number || "N/A") + "</span>\n", html: true, delay: 300 },
{ text: "\nMINER PERFORMANCE DATA\n", speed: 20, delay: 300 },
{ text: "CURRENT HASHRATE: ", speed: 20, delay: 100 },
{ text: "<span class='yellow'>" + (data.hashrate_60sec || "N/A") + " " + (data.hashrate_60sec_unit || "TH/s") + "</span>\n", html: true, delay: 300 },
{ text: "24HR AVG HASHRATE: ", speed: 20, delay: 100 },
{ text: "<span class='yellow'>" + (data.hashrate_24hr || "N/A") + " " + (data.hashrate_24hr_unit || "TH/s") + "</span>\n", html: true, delay: 300 },
{ text: "ACTIVE WORKERS: ", speed: 20, delay: 100 },
{ text: "<span class='yellow'>" + (data.workers_hashing || "0") + "</span>\n", html: true, delay: 300 },
{ text: "\nFINANCIAL CALCULATIONS\n", speed: 20, delay: 300 },
{ text: "DAILY MINING REVENUE: ", speed: 20, delay: 100 },
{ text: "<span class='green'>$" + numberWithCommas((data.daily_revenue || 0).toFixed(2)) + "</span>\n", html: true, delay: 300 },
{ text: "DAILY POWER COST: ", speed: 20, delay: 100 },
{ text: "<span class='red'>$" + numberWithCommas((data.daily_power_cost || 0).toFixed(2)) + "</span>\n", html: true, delay: 300 },
{ text: "DAILY PROFIT: ", speed: 20, delay: 100 },
{ text: "<span class='green'>$" + numberWithCommas((data.daily_profit_usd || 0).toFixed(2)) + "</span>\n", html: true, delay: 300 },
{ text: "PROJECTED MONTHLY PROFIT: ", speed: 20, delay: 100 },
{ text: "<span class='green'>$" + numberWithCommas((data.monthly_profit_usd || 0).toFixed(2)) + "</span>\n", html: true, delay: 300 },
{ text: "DAILY SATOSHI YIELD: ", speed: 20, delay: 100 },
{ text: "<span class='yellow'>" + numberWithCommas(data.daily_mined_sats || 0) + " sats</span>\n", html: true, delay: 300 },
{ text: "UNPAID EARNINGS: ", speed: 20, delay: 100 },
{ text: "<span class='green'>" + (data.unpaid_earnings || "0") + " BTC</span>\n", html: true, delay: 300 },
{ text: "ESTIMATED TIME TO PAYOUT: ", speed: 20, delay: 100 },
{ text: "<span class='yellow'>" + (data.est_time_to_payout || "Unknown") + "</span>\n", html: true, delay: 300 },
{ text: "\n", speed: 25, delay: 100 },
{ text: "<span class='green'>ALL MINING PROCESSES OPERATIONAL</span>\n", html: true, delay: 400 },
{ text: "\nInitialize mining dashboard? ", speed: 25, delay: 400 },
{ showPrompt: true, delay: 0 }
];
startTyping();
})
.catch(error => {
updateDebug(`Error fetching dashboard data: ${error.message}`);
});
</script>
</body>
</html>