mirror of
https://github.com/Retropex/custom-ocean.xyz-dashboard.git
synced 2025-05-12 19:20:45 +02:00

Updated `console.css` to enhance the layout and responsiveness of the console interface. Adjusted the console container to utilize the full viewport height while maintaining a controlled height for the console wrapper. Changed console output positioning to relative for better spacing and positioned the stats bar at the bottom for consistency. Added a new JavaScript function `adjustConsoleLayout` to dynamically calculate and set the height of the console wrapper based on viewport size, improving user experience across different screen sizes.
507 lines
21 KiB
JavaScript
507 lines
21 KiB
JavaScript
/**
|
|
* Bitcoin Mining Console Log Simulation
|
|
* A retro-styled terminal/console log display showing real-time mining metrics
|
|
*/
|
|
|
|
// Message type constants
|
|
const MSG_TYPE = {
|
|
SYSTEM: 'system',
|
|
INFO: 'info',
|
|
WARNING: 'warning',
|
|
ERROR: 'error',
|
|
SUCCESS: 'success',
|
|
HASH: 'hash',
|
|
SHARE: 'share',
|
|
BLOCK: 'block',
|
|
NETWORK: 'network'
|
|
};
|
|
|
|
// Global settings and state
|
|
const consoleSettings = {
|
|
maxLines: 500, // Maximum number of lines to keep in the console
|
|
autoScroll: true, // Auto-scroll to bottom on new messages
|
|
logFrequency: { // Frequency of different message types (ms)
|
|
system: 5000, // System messages every 5 seconds
|
|
hash: 2000, // Hash updates every 2 seconds
|
|
share: 10000, // Share submissions every 10 seconds
|
|
network: 30000, // Network updates every 30 seconds
|
|
block: 600000 // Block updates roughly every 10 minutes (on average)
|
|
},
|
|
glitchProbability: 0.05 // 5% chance of text glitch effect on any message
|
|
};
|
|
|
|
// Cache for metrics data
|
|
let cachedMetrics = null;
|
|
const hashRateFluctuation = 0.01; // 1% fluctuation for realistic variance
|
|
|
|
// Initialize console
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
console.log('Console log initialized');
|
|
|
|
// Update clock
|
|
updateClock();
|
|
setInterval(updateClock, 1000);
|
|
|
|
// Set up metrics fetch
|
|
fetchMetrics();
|
|
setupEventSource();
|
|
|
|
// Start log generators with slight delays to avoid all happening at once
|
|
setTimeout(() => startLogGenerator(MSG_TYPE.HASH), 1000);
|
|
setTimeout(() => startLogGenerator(MSG_TYPE.SHARE), 3000);
|
|
setTimeout(() => startLogGenerator(MSG_TYPE.SYSTEM), 5000);
|
|
setTimeout(() => startLogGenerator(MSG_TYPE.NETWORK), 7000);
|
|
setTimeout(() => startLogGenerator(MSG_TYPE.BLOCK), 9000);
|
|
|
|
// Add random errors/warnings occasionally
|
|
setInterval(generateRandomEvent, 60000);
|
|
});
|
|
|
|
/**
|
|
* Format date for console display
|
|
*/
|
|
function formatDate(date) {
|
|
const hours = String(date.getHours()).padStart(2, '0');
|
|
const minutes = String(date.getMinutes()).padStart(2, '0');
|
|
const seconds = String(date.getSeconds()).padStart(2, '0');
|
|
const day = String(date.getDate()).padStart(2, '0');
|
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
const year = date.getFullYear();
|
|
|
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
|
}
|
|
|
|
/**
|
|
* Update the clock in the console header
|
|
*/
|
|
function updateClock() {
|
|
const now = new Date();
|
|
document.getElementById('current-time').textContent = formatDate(now);
|
|
}
|
|
|
|
/**
|
|
* Set up Server-Sent Events for real-time updates
|
|
*/
|
|
function setupEventSource() {
|
|
const eventSource = new EventSource('/stream');
|
|
|
|
eventSource.onmessage = function (event) {
|
|
try {
|
|
const data = JSON.parse(event.data);
|
|
if (data.type === "ping" || data.type === "timeout_warning" || data.type === "timeout") {
|
|
return; // Ignore ping and timeout messages
|
|
}
|
|
|
|
// Update cached metrics
|
|
cachedMetrics = data;
|
|
|
|
// Update dashboard stats
|
|
updateDashboardStats(data);
|
|
|
|
} catch (error) {
|
|
console.error('Error processing SSE data:', error);
|
|
}
|
|
};
|
|
|
|
eventSource.onerror = function () {
|
|
console.error('SSE connection error');
|
|
// Reconnect after 5 seconds
|
|
setTimeout(setupEventSource, 5000);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Fetch metrics via API
|
|
*/
|
|
function fetchMetrics() {
|
|
fetch('/api/metrics')
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
cachedMetrics = data;
|
|
updateDashboardStats(data);
|
|
})
|
|
.catch(error => {
|
|
console.error('Error fetching metrics:', error);
|
|
addConsoleMessage('ERROR FETCHING METRICS DATA. RETRYING...', MSG_TYPE.ERROR);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Update the dashboard stats display
|
|
*/
|
|
function updateDashboardStats(data) {
|
|
if (!data) return;
|
|
|
|
// Update hashrate
|
|
const hashrate = data.hashrate_60sec || data.hashrate_10min || data.hashrate_3hr || 0;
|
|
const hashrateUnit = (data.hashrate_60sec_unit || data.hashrate_10min_unit || data.hashrate_3hr_unit || 'TH/s').toUpperCase();
|
|
document.getElementById('current-hashrate').textContent = `${hashrate} ${hashrateUnit}`;
|
|
|
|
// Update block height
|
|
document.getElementById('block-height').textContent = numberWithCommas(data.block_number || 0);
|
|
|
|
// Update workers online
|
|
document.getElementById('workers-online').textContent = data.workers_hashing || 0;
|
|
|
|
// Update BTC price
|
|
document.getElementById('btc-price').textContent = `$${numberWithCommas(parseFloat(data.btc_price || 0).toFixed(2))}`;
|
|
}
|
|
|
|
/**
|
|
* Format number with commas
|
|
*/
|
|
function numberWithCommas(x) {
|
|
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
}
|
|
|
|
/**
|
|
* Start a specific log generator
|
|
*/
|
|
function startLogGenerator(type) {
|
|
const interval = consoleSettings.logFrequency[type];
|
|
if (!interval) return;
|
|
|
|
setInterval(() => {
|
|
generateLog(type);
|
|
}, interval);
|
|
}
|
|
|
|
/**
|
|
* Generate a log message based on type
|
|
*/
|
|
function generateLog(type) {
|
|
if (!cachedMetrics) return;
|
|
|
|
let message = '';
|
|
let messageType = type;
|
|
|
|
switch (type) {
|
|
case MSG_TYPE.SYSTEM:
|
|
message = generateSystemMessage();
|
|
break;
|
|
|
|
case MSG_TYPE.HASH:
|
|
message = generateHashrateMessage();
|
|
break;
|
|
|
|
case MSG_TYPE.SHARE:
|
|
message = generateShareMessage();
|
|
break;
|
|
|
|
case MSG_TYPE.NETWORK:
|
|
message = generateNetworkMessage();
|
|
break;
|
|
|
|
case MSG_TYPE.BLOCK:
|
|
// Only generate block messages occasionally (simulating real mining)
|
|
if (Math.random() < 0.1) { // 10% chance each time this is called
|
|
message = generateBlockMessage();
|
|
} else {
|
|
return; // Skip this cycle
|
|
}
|
|
break;
|
|
}
|
|
|
|
// If we have a message, add it to console
|
|
if (message) {
|
|
addConsoleMessage(message, messageType);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate a system status message based on actual metrics
|
|
*/
|
|
function generateSystemMessage() {
|
|
if (!cachedMetrics) return 'SYSTEM STATUS: AWAITING METRICS DATA...';
|
|
|
|
// Create an array of possible messages that use real metrics where available
|
|
const systemMessages = [
|
|
// Power consumption using actual configured power usage when available
|
|
`POWER CONSUMPTION: ${cachedMetrics.power_usage ? `${cachedMetrics.power_usage}W` : `${randomInt(800, 1500)}W`} - EFFICIENCY: ${cachedMetrics.power_cost ? `$${cachedMetrics.power_cost}/kWh` : 'CALCULATING...'}`,
|
|
|
|
// Use real worker count for system health reports
|
|
`SYSTEM HEALTH CHECK: ${cachedMetrics.workers_hashing || 0}/${cachedMetrics.workers_total || 0} WORKERS OPERATIONAL - ${Math.round(((cachedMetrics.workers_hashing || 0) / (cachedMetrics.workers_total || 1)) * 100)}% ONLINE`,
|
|
|
|
// Use actual hashrate
|
|
`PROCESSING CAPACITY: ${cachedMetrics.hashrate_60sec || cachedMetrics.hashrate_10min || cachedMetrics.hashrate_3hr || 0} ${cachedMetrics.hashrate_60sec_unit || cachedMetrics.hashrate_10min_unit || cachedMetrics.hashrate_3hr_unit || 'TH/s'} - ${cachedMetrics.workers_hashing ? 'OPTIMAL' : 'SUBOPTIMAL'} PERFORMANCE`,
|
|
|
|
// Network synchronization based on real block height
|
|
`BLOCKCHAIN SYNC STATUS: HEIGHT ${numberWithCommas(cachedMetrics.block_number || 0)} - 100% SYNCHRONIZED`,
|
|
|
|
// Earnings projection based on actual data
|
|
`REVENUE PROJECTION: ${cachedMetrics.daily_revenue ? `$${cachedMetrics.daily_revenue.toFixed(2)}/DAY` : 'CALCULATING...'} - ${cachedMetrics.daily_profit_usd ? `$${cachedMetrics.daily_profit_usd.toFixed(2)} PROFIT` : 'CALCULATING PROFIT...'}`,
|
|
|
|
// Time to payout/rewards
|
|
`PAYOUT ESTIMATION: ${cachedMetrics.est_time_to_payout || 'CALCULATING...'} - ${numberWithCommas(cachedMetrics.unpaid_earnings || 0)} SATS PENDING`,
|
|
|
|
// Real Bitcoin price
|
|
`MARKET CONDITIONS: BTC $${numberWithCommas(parseFloat(cachedMetrics.btc_price || 0).toFixed(2))} - MINING PROFITABILITY ${cachedMetrics.daily_profit_usd > 0 ? 'POSITIVE' : 'NEGATIVE'}`,
|
|
|
|
// Monthly projections
|
|
`MONTHLY PROJECTION: ${cachedMetrics.monthly_mined_sats ? `${numberWithCommas(cachedMetrics.monthly_mined_sats)} SATS` : 'CALCULATING...'} - ${cachedMetrics.monthly_profit_usd ? `$${numberWithCommas(cachedMetrics.monthly_profit_usd.toFixed(2))}` : 'CALCULATING...'}`,
|
|
|
|
// Network difficulty and mining pool statistics
|
|
`MINING DIFFICULTY: ${numberWithCommas(Math.round(cachedMetrics.difficulty || 0))} - POOL HASHRATE: ${cachedMetrics.pool_total_hashrate || 0} ${cachedMetrics.pool_total_hashrate_unit || 'TH/s'}`,
|
|
|
|
// System uptime (still random as we don't track this)
|
|
`SYSTEM UPTIME: ${randomInt(1, 24)}h ${randomInt(0, 59)}m ${randomInt(0, 59)}s - STABILITY: ${cachedMetrics.workers_hashing > 0 ? 'HIGH' : 'DEGRADED'}`
|
|
];
|
|
|
|
// Add some random but realistic messages to maintain variety
|
|
if (Math.random() < 0.3) {
|
|
systemMessages.push(`SYSTEM TEMPERATURE: ${randomInt(50, 75)}°C - ${randomInt(50, 70) > 65 ? 'WARNING: HIGH' : 'WITHIN NORMAL PARAMETERS'}`);
|
|
systemMessages.push(`MEMORY USAGE: ${randomInt(30, 70)}% - ${randomInt(2048, 8192)}MB ALLOCATED`);
|
|
systemMessages.push(`CPU UTILIZATION: ${randomInt(20, 95)}% - PROCESSING MINING ALGORITHMS`);
|
|
systemMessages.push(`NETWORK LATENCY: ${randomInt(5, 200)}ms TO MINING POOL - ${randomInt(5, 200) > 100 ? 'HIGH LATENCY' : 'OPTIMAL'}`);
|
|
}
|
|
|
|
// Special alerts based on metrics state
|
|
if (cachedMetrics.workers_total && cachedMetrics.workers_hashing < cachedMetrics.workers_total) {
|
|
systemMessages.push(`ALERT: ${cachedMetrics.workers_total - cachedMetrics.workers_hashing} WORKERS OFFLINE - MAINTENANCE REQUIRED`);
|
|
}
|
|
|
|
if (cachedMetrics.daily_profit_usd && cachedMetrics.daily_profit_usd < 0) {
|
|
systemMessages.push(`WARNING: NEGATIVE MINING PROFITABILITY - $${Math.abs(cachedMetrics.daily_profit_usd).toFixed(2)}/DAY LOSS`);
|
|
}
|
|
|
|
// Return a random message from the array
|
|
return systemMessages[Math.floor(Math.random() * systemMessages.length)];
|
|
}
|
|
|
|
/**
|
|
* Generate a hashrate update message
|
|
*/
|
|
function generateHashrateMessage() {
|
|
if (!cachedMetrics) return '';
|
|
|
|
// Get base hashrate from cached metrics
|
|
const baseHashrate = cachedMetrics.hashrate_60sec || cachedMetrics.hashrate_10min || cachedMetrics.hashrate_3hr || 1;
|
|
const hashrateUnit = (cachedMetrics.hashrate_60sec_unit || cachedMetrics.hashrate_10min_unit || cachedMetrics.hashrate_3hr_unit || 'TH/s').toUpperCase();
|
|
|
|
// Add some fluctuation for realism
|
|
const fluctuation = (Math.random() * 2 - 1) * hashRateFluctuation;
|
|
const currentHashrate = (baseHashrate * (1 + fluctuation)).toFixed(2);
|
|
|
|
const hashMessages = [
|
|
`HASHRATE UPDATE: ${currentHashrate} ${hashrateUnit} - ${fluctuation >= 0 ? 'INCREASE' : 'DECREASE'} OF ${Math.abs(fluctuation * 100).toFixed(2)}%`,
|
|
`MINING PERFORMANCE: ${currentHashrate} ${hashrateUnit} - ${randomInt(97, 100)}% EFFICIENCY`,
|
|
`HASH COMPUTATION RATE: ${currentHashrate} ${hashrateUnit} - ${fluctuation >= 0 ? 'OPTIMAL' : 'SUBOPTIMAL'} PERFORMANCE`,
|
|
`PROCESSING HASHES AT ${currentHashrate} ${hashrateUnit} - ${cachedMetrics.workers_hashing || 1} WORKERS ACTIVE`,
|
|
`CURRENT HASHING POWER: ${currentHashrate} ${hashrateUnit} - NETWORK CONTRIBUTION: ${(baseHashrate / (cachedMetrics.network_hashrate * 1000 || 1) * 100).toFixed(8)}%`
|
|
];
|
|
|
|
return hashMessages[Math.floor(Math.random() * hashMessages.length)];
|
|
}
|
|
|
|
/**
|
|
* Generate a share submission message
|
|
*/
|
|
function generateShareMessage() {
|
|
if (!cachedMetrics) return '';
|
|
|
|
const difficulty = randomInt(8000, 12000) / 100;
|
|
const timeToSolve = randomInt(10, 990) / 10;
|
|
const accepted = Math.random() < 0.98; // 98% acceptance rate
|
|
|
|
if (accepted) {
|
|
const shareMessages = [
|
|
`SHARE ACCEPTED [${generateRandomHex(8)}] - DIFFICULTY ${difficulty} - SOLVED IN ${timeToSolve}s`,
|
|
`VALID SHARE SUBMITTED [${generateRandomHex(8)}] - POOL ACCEPTED - DIFFICULTY ${difficulty}`,
|
|
`SHARE SOLUTION FOUND [${generateRandomHex(8)}] - VERIFICATION SUCCESSFUL - EFFORT: ${randomInt(1, 200)}%`,
|
|
`MINING SHARE ACCEPTED BY POOL [${generateRandomHex(8)}] - ${timeToSolve}s SOLUTION TIME`,
|
|
`SHARE SUBMISSION SUCCESSFUL [${generateRandomHex(8)}] - YAY!!! ⚡`
|
|
];
|
|
return shareMessages[Math.floor(Math.random() * shareMessages.length)];
|
|
} else {
|
|
const rejectReasons = ['LOW DIFFICULTY', 'STALE', 'DUPLICATE', 'INVALID NONCE', 'BAD TRANSACTION'];
|
|
const reason = rejectReasons[Math.floor(Math.random() * rejectReasons.length)];
|
|
|
|
return `SHARE REJECTED [${generateRandomHex(8)}] - REASON: ${reason} - DIFFICULTY ${difficulty}`;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate a network update message
|
|
*/
|
|
function generateNetworkMessage() {
|
|
if (!cachedMetrics) return '';
|
|
|
|
const networkHashrate = cachedMetrics.network_hashrate || 350;
|
|
const difficulty = cachedMetrics.difficulty || 60000000000000;
|
|
const btcPrice = cachedMetrics.btc_price || 75000;
|
|
|
|
const networkMessages = [
|
|
`NETWORK HASHRATE: ${networkHashrate} EH/s - GLOBAL MINING POWER`,
|
|
`NETWORK DIFFICULTY: ${numberWithCommas(Math.round(difficulty))} - NEXT ADJUSTMENT IN ${randomInt(1, 14)} DAYS`,
|
|
`BTC MARKET UPDATE: $${numberWithCommas(btcPrice)} - ${Math.random() < 0.7 ? 'UP' : 'DOWN'} ${randomInt(1, 500) / 100}% IN 24HR`,
|
|
`BLOCKCHAIN HEIGHT: ${numberWithCommas(cachedMetrics.block_number || 0)} - FULLY SYNCHRONIZED`,
|
|
`MEMPOOL STATUS: ${randomInt(5000, 50000)} TRANSACTIONS PENDING - ${randomInt(10, 100)} SAT/VBYTE`,
|
|
`ESTIMATED EARNINGS: ${cachedMetrics.daily_mined_sats || 0} SATS PER DAY AT CURRENT RATE`,
|
|
`TRANSACTION FEES: AVERAGE ${randomInt(1000, 10000)} SATS PER TRANSACTION`,
|
|
`POOL FEE: ${randomInt(0, 3)}% + ${randomInt(0, 2)}% MINING FEE = ${randomInt(1, 5)}% TOTAL DEDUCTION`
|
|
];
|
|
|
|
return networkMessages[Math.floor(Math.random() * networkMessages.length)];
|
|
}
|
|
|
|
/**
|
|
* Generate a block-related message
|
|
*/
|
|
function generateBlockMessage() {
|
|
if (!cachedMetrics) return '';
|
|
|
|
const blockHeight = cachedMetrics.block_number || 0;
|
|
const nextBlockHeight = blockHeight + 1;
|
|
|
|
// Generate random transaction count for the block
|
|
const txCount = randomInt(1000, 4000);
|
|
const blockSize = randomInt(1, 4) + "." + randomInt(0, 9) + " MB";
|
|
const blockReward = "3.125 BTC + " + randomInt(0, 3) + "." + randomInt(0, 999) + " BTC FEES";
|
|
|
|
// Determine if this is a block found by the pool (rare event)
|
|
const isPoolBlock = Math.random() < 0.01; // 1% chance
|
|
|
|
if (isPoolBlock) {
|
|
// This is a special case - our pool found a block!
|
|
const blockMessages = [
|
|
`🎉 BLOCK FOUND BY POOL! 🎉 HEIGHT: ${blockHeight} - REWARD: ${blockReward}`,
|
|
`⚡⚡⚡ POOL SUCCESSFULLY MINED BLOCK ${blockHeight}! REWARD: ${blockReward} ⚡⚡⚡`,
|
|
`!!!CONGRATULATIONS!!! BLOCK ${blockHeight} MINED BY OUR POOL! ${txCount} TRANSACTIONS CONFIRMED`,
|
|
`$$$ BLOCK REWARD INCOMING $$$ - POOL MINED BLOCK ${blockHeight} - ${blockReward}`,
|
|
`NEW BLOCK ${blockHeight} MINED BY OUR POOL! SIZE: ${blockSize}, TXs: ${txCount}, REWARD: ${blockReward}`
|
|
];
|
|
return blockMessages[Math.floor(Math.random() * blockMessages.length)];
|
|
} else {
|
|
// Regular block found by someone else
|
|
const blockMessages = [
|
|
`NEW BLOCK DETECTED: HEIGHT ${blockHeight} - ${txCount} TRANSACTIONS - SIZE: ${blockSize}`,
|
|
`BLOCKCHAIN UPDATE: BLOCK ${blockHeight} CONFIRMED - MINED BY EXTERNAL POOL`,
|
|
`BLOCK ${blockHeight} ADDED TO BLOCKCHAIN - DIFFICULTY TARGET: ${generateRandomHex(8)}...`,
|
|
`NETWORK: NEW BLOCK ${blockHeight} - REWARD: ${blockReward}`,
|
|
`BLOCK HEIGHT ${blockHeight} CONFIRMED - WORKING ON NEXT BLOCK: ${nextBlockHeight}`
|
|
];
|
|
return blockMessages[Math.floor(Math.random() * blockMessages.length)];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate random technical or error events
|
|
*/
|
|
function generateRandomEvent() {
|
|
// Only 20% chance to generate a random event
|
|
if (Math.random() > 0.2) return;
|
|
|
|
const events = [
|
|
{ message: "WARNING: EXCESSIVE TEMPERATURE DETECTED ON WORKER #" + randomInt(1, 5), type: MSG_TYPE.WARNING },
|
|
{ message: "ERROR: INVALID MINING PARAMETER DETECTED - RECONFIGURING ALGORITHM", type: MSG_TYPE.ERROR },
|
|
{ message: "NOTICE: OPTIMIZING MEMORY ALLOCATION FOR IMPROVED PERFORMANCE", type: MSG_TYPE.INFO },
|
|
{ message: "ALERT: NETWORK DIFFICULTY INCREASED BY " + randomInt(1, 10) + "% - ADJUSTING PARAMETERS", type: MSG_TYPE.WARNING },
|
|
{ message: "SYSTEM: DETECTED " + randomInt(2, 10) + " REJECTED SHARES IN LAST MINUTE - ANALYZING CAUSE", type: MSG_TYPE.WARNING },
|
|
{ message: "ERROR: CONNECTION TO MINING POOL TIMED OUT - ATTEMPTING RECONNECTION...", type: MSG_TYPE.ERROR },
|
|
{ message: "SUCCESS: CONNECTION RESTORED - RESUMING MINING OPERATIONS", type: MSG_TYPE.SUCCESS },
|
|
{ message: "NOTICE: FIRMWARE UPDATE AVAILABLE FOR MINING HARDWARE", type: MSG_TYPE.INFO },
|
|
{ message: "WARNING: POWER SUPPLY FLUCTUATION DETECTED - MONITORING VOLTAGE", type: MSG_TYPE.WARNING },
|
|
{ message: "SYSTEM: RECALIBRATING HASH VERIFICATION ALGORITHM", type: MSG_TYPE.SYSTEM }
|
|
];
|
|
|
|
const event = events[Math.floor(Math.random() * events.length)];
|
|
addConsoleMessage(event.message, event.type);
|
|
}
|
|
|
|
/**
|
|
* Add a message to the console output
|
|
*/
|
|
function addConsoleMessage(message, type = MSG_TYPE.SYSTEM) {
|
|
const consoleOutput = document.getElementById('console-output');
|
|
const now = new Date();
|
|
const timestamp = formatDate(now);
|
|
|
|
// Create the message element
|
|
const lineElement = document.createElement('div');
|
|
lineElement.className = 'console-line';
|
|
|
|
// Add timestamp
|
|
const timestampSpan = document.createElement('span');
|
|
timestampSpan.className = 'timestamp';
|
|
timestampSpan.textContent = `[${timestamp}] `;
|
|
lineElement.appendChild(timestampSpan);
|
|
|
|
// Add message with appropriate class based on type
|
|
const messageSpan = document.createElement('span');
|
|
messageSpan.className = type;
|
|
messageSpan.textContent = message;
|
|
|
|
// Apply glitch effect occasionally for retro aesthetic
|
|
if (Math.random() < consoleSettings.glitchProbability) {
|
|
messageSpan.classList.add('glitch');
|
|
messageSpan.setAttribute('data-text', message);
|
|
}
|
|
|
|
lineElement.appendChild(messageSpan);
|
|
|
|
// Add to console
|
|
consoleOutput.appendChild(lineElement);
|
|
|
|
// Limit the number of lines in the console
|
|
while (consoleOutput.children.length > consoleSettings.maxLines) {
|
|
consoleOutput.removeChild(consoleOutput.firstChild);
|
|
}
|
|
|
|
// Auto-scroll to bottom
|
|
if (consoleSettings.autoScroll) {
|
|
const consoleWrapper = document.querySelector('.console-wrapper');
|
|
consoleWrapper.scrollTop = consoleWrapper.scrollHeight;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate a random integer between min and max (inclusive)
|
|
*/
|
|
function randomInt(min, max) {
|
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
}
|
|
|
|
/**
|
|
* Generate a random hexadecimal string of specified length
|
|
*/
|
|
function generateRandomHex(length) {
|
|
const characters = '0123456789ABCDEF';
|
|
let result = '';
|
|
for (let i = 0; i < length; i++) {
|
|
result += characters.charAt(Math.floor(Math.random() * 16));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// Add this function to your console.js file - this will fix the layout issue
|
|
function adjustConsoleLayout() {
|
|
// Get the elements
|
|
const container = document.querySelector('.console-container');
|
|
const header = document.querySelector('.console-header');
|
|
const stats = document.querySelector('.console-stats');
|
|
const wrapper = document.querySelector('.console-wrapper');
|
|
const output = document.querySelector('.console-output');
|
|
|
|
if (container && header && stats && wrapper && output) {
|
|
// Calculate available height for the wrapper
|
|
const viewportHeight = window.innerHeight;
|
|
const headerHeight = header.offsetHeight;
|
|
const statsHeight = stats.offsetHeight;
|
|
const wrapperHeight = viewportHeight - headerHeight - statsHeight - 2; // 2px for borders
|
|
|
|
// Apply the calculated height to the wrapper
|
|
wrapper.style.height = `${wrapperHeight}px`;
|
|
wrapper.style.maxHeight = `${wrapperHeight}px`;
|
|
wrapper.style.position = 'relative';
|
|
|
|
// Make the output relative instead of absolute
|
|
output.style.position = 'relative';
|
|
output.style.bottom = 'auto';
|
|
}
|
|
}
|
|
|
|
// Call the function on load and whenever the window is resized
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
adjustConsoleLayout();
|
|
window.addEventListener('resize', adjustConsoleLayout);
|
|
}); |