/** * ASCII Art Banners for Tinker Tickets - Terminal Edition * * This file contains ASCII art banners and rendering functions * for the retro terminal aesthetic redesign. */ // ASCII Art Banner Definitions const ASCII_BANNERS = { // Main large banner for desktop main: ` ╔══════════════════════════════════════════════════════════════════════════╗ ║ ║ ║ ████████╗██╗███╗ ██╗██╗ ██╗███████╗██████╗ ║ ║ ╚══██╔══╝██║████╗ ██║██║ ██╔╝██╔════╝██╔══██╗ ║ ║ ██║ ██║██╔██╗ ██║█████╔╝ █████╗ ██████╔╝ ║ ║ ██║ ██║██║╚██╗██║██╔═██╗ ██╔══╝ ██╔══██╗ ║ ║ ██║ ██║██║ ╚████║██║ ██╗███████╗██║ ██║ ║ ║ ╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ║ ║ ║ ║ ████████╗██╗ ██████╗██╗ ██╗███████╗████████╗███████╗ ║ ║ ╚══██╔══╝██║██╔════╝██║ ██╔╝██╔════╝╚══██╔══╝██╔════╝ ║ ║ ██║ ██║██║ █████╔╝ █████╗ ██║ ███████╗ ║ ║ ██║ ██║██║ ██╔═██╗ ██╔══╝ ██║ ╚════██║ ║ ║ ██║ ██║╚██████╗██║ ██╗███████╗ ██║ ███████║ ║ ║ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝╚══════╝ ╚═╝ ╚══════╝ ║ ║ ║ ║ >> RETRO TERMINAL TICKETING SYSTEM v1.0 << ║ ║ ║ ╚══════════════════════════════════════════════════════════════════════════╝ `, // Compact version for smaller screens compact: ` ┌──────────────────────────────────────────────────────────┐ │ ▀█▀ █ █▄ █ █▄▀ █▀▀ █▀█ ▀█▀ █ █▀▀ █▄▀ █▀▀ ▀█▀ █▀ │ │ █ █ █ ▀█ █ █ ██▄ █▀▄ █ █ █▄▄ █ █ ██▄ █ ▄█ │ │ Terminal Ticketing System v1.0 │ └──────────────────────────────────────────────────────────┘ `, // Minimal version for mobile minimal: ` ╔════════════════════════════╗ ║ TINKER TICKETS v1.0 ║ ╚════════════════════════════╝ ` }; /** * Renders ASCII banner with optional typewriter effect * * @param {string} bannerId - ID of banner to render ('main', 'compact', or 'minimal') * @param {string} containerSelector - CSS selector for container element * @param {number} speed - Speed of typewriter effect in milliseconds (0 = instant) * @param {boolean} addGlow - Whether to add text glow effect */ function renderASCIIBanner(bannerId, containerSelector, speed = 5, addGlow = true) { const banner = ASCII_BANNERS[bannerId]; const container = document.querySelector(containerSelector); if (!container || !banner) { console.error('ASCII Banner: Container or banner not found', { bannerId, containerSelector }); return; } // Create pre element for ASCII art const pre = document.createElement('pre'); pre.className = 'ascii-banner'; pre.style.margin = '0'; pre.style.fontFamily = 'var(--font-mono)'; pre.style.color = 'var(--terminal-green)'; if (addGlow) { pre.style.textShadow = 'var(--glow-green)'; } pre.style.fontSize = getBannerFontSize(bannerId); pre.style.lineHeight = '1.2'; pre.style.whiteSpace = 'pre'; pre.style.overflow = 'visible'; pre.style.textAlign = 'center'; container.appendChild(pre); // Instant render or typewriter effect if (speed === 0) { pre.textContent = banner; } else { renderWithTypewriter(pre, banner, speed); } } /** * Get appropriate font size for banner type * * @param {string} bannerId - Banner ID * @returns {string} - CSS font size */ function getBannerFontSize(bannerId) { const width = window.innerWidth; if (bannerId === 'main') { if (width < 768) return '0.4rem'; if (width < 1024) return '0.6rem'; return '0.8rem'; } else if (bannerId === 'compact') { if (width < 768) return '0.6rem'; return '0.8rem'; } else { return '0.8rem'; } } /** * Renders text with typewriter effect * * @param {HTMLElement} element - Element to render into * @param {string} text - Text to render * @param {number} speed - Speed in milliseconds per character */ function renderWithTypewriter(element, text, speed) { let index = 0; const typeInterval = setInterval(() => { element.textContent = text.substring(0, index); index++; if (index > text.length) { clearInterval(typeInterval); // Trigger completion event const event = new CustomEvent('bannerComplete'); element.dispatchEvent(event); } }, speed); } /** * Renders responsive banner based on screen size * * @param {string} containerSelector - CSS selector for container * @param {number} speed - Typewriter speed (0 = instant) */ function renderResponsiveBanner(containerSelector, speed = 5) { const width = window.innerWidth; let bannerId; if (width < 480) { bannerId = 'minimal'; } else if (width < 1024) { bannerId = 'compact'; } else { bannerId = 'main'; } renderASCIIBanner(bannerId, containerSelector, speed, true); } /** * Animated welcome sequence * Shows banner followed by a blinking cursor effect * * @param {string} containerSelector - CSS selector for container */ function animatedWelcome(containerSelector) { const container = document.querySelector(containerSelector); if (!container) return; // Clear container container.innerHTML = ''; // Render banner renderResponsiveBanner(containerSelector, 3); // Add blinking cursor after banner const banner = container.querySelector('.ascii-banner'); if (banner) { banner.addEventListener('bannerComplete', () => { const cursor = document.createElement('span'); cursor.textContent = '█'; cursor.style.animation = 'blink-caret 0.75s step-end infinite'; cursor.style.marginLeft = '5px'; banner.appendChild(cursor); }); } } // Export functions for use in other scripts if (typeof module !== 'undefined' && module.exports) { module.exports = { ASCII_BANNERS, renderASCIIBanner, renderResponsiveBanner, animatedWelcome }; }