Your Name
feat: UI improvements and error suppression - Enhanced dashboard and market pages with improved header buttons, logo, and currency symbol display - Stopped animated ticker - Removed pie chart legends - Added error suppressor for external service errors (SSE, Permissions-Policy warnings) - Improved header button prominence and icon appearance - Enhanced logo with glow effects and better design - Fixed currency symbol visibility in market tables
8b7b267
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <!-- Permissions-Policy with only recognized features to avoid browser warnings --> | |
| <meta http-equiv="Permissions-Policy" | |
| content="accelerometer=(), autoplay=(), camera=(), display-capture=(), encrypted-media=(), fullscreen=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), sync-xhr=(), usb=(), web-share=()"> | |
| <!-- Suppress HF Space Permissions-Policy warnings and SSE errors - Load IMMEDIATELY before any other scripts --> | |
| <script src="/static/shared/js/utils/error-suppressor.js"></script> | |
| <script> | |
| // Inline console filter - runs immediately to catch early warnings (backup) | |
| (function () { | |
| if (window._hfWarningsSuppressed) return; | |
| const features = ['ambient-light-sensor', 'battery', 'document-domain', 'layout-animations', 'legacy-image-formats', 'oversized-images', 'vr', 'wake-lock']; | |
| const originalWarn = console.warn; | |
| const originalError = console.error; | |
| const shouldSuppress = (msg) => { | |
| if (!msg) return false; | |
| const m = msg.toString().toLowerCase(); | |
| return m.includes('unrecognized feature:') && features.some(f => m.includes(f)) || | |
| m.includes('sse') && (m.includes('aborted') || m.includes('failed to fetch')); | |
| }; | |
| console.warn = function (...args) { if (!shouldSuppress(args[0])) originalWarn.apply(console, args); }; | |
| console.error = function (...args) { if (!shouldSuppress(args[0])) originalError.apply(console, args); }; | |
| window._hfWarningsSuppressed = true; | |
| })(); | |
| </script> | |
| <title>Crypto Intelligence Hub | Loading...</title> | |
| <!-- Preconnect to external domains --> | |
| <link rel="preconnect" href="https://fonts.googleapis.com" crossorigin> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <!-- Load fonts with font-display swap for non-blocking --> | |
| <link | |
| href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=Inter:wght@300;400;500;600;700&display=swap" | |
| rel="stylesheet" media="print" onload="this.media='all'"> | |
| <noscript> | |
| <link | |
| href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=Inter:wght@300;400;500;600;700&display=swap" | |
| rel="stylesheet"> | |
| </noscript> | |
| <style> | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| :root { | |
| --bg-primary: #0a0e27; | |
| --bg-secondary: #0b1121; | |
| --accent-cyan: #2dd4bf; | |
| --accent-purple: #818cf8; | |
| --accent-pink: #ec4899; | |
| --text-primary: #f8fafc; | |
| --text-secondary: rgba(241, 245, 249, 0.75); | |
| --glass: rgba(6, 12, 27, 0.85); | |
| } | |
| body { | |
| min-height: 100vh; | |
| font-family: 'Inter', sans-serif; | |
| background: linear-gradient(135deg, var(--bg-primary), #020617, var(--bg-secondary)); | |
| color: var(--text-primary); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| overflow: hidden; | |
| } | |
| .container { | |
| max-width: 900px; | |
| width: 90%; | |
| padding: 3rem; | |
| background: var(--glass); | |
| backdrop-filter: blur(20px); | |
| border-radius: 32px; | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| box-shadow: 0 30px 120px rgba(0, 0, 0, 0.5); | |
| text-align: center; | |
| } | |
| .logo { | |
| width: 80px; | |
| height: 80px; | |
| margin: 0 auto 2rem; | |
| background: linear-gradient(135deg, var(--accent-cyan), var(--accent-purple)); | |
| border-radius: 20px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| box-shadow: 0 15px 40px rgba(45, 212, 191, 0.4); | |
| animation: float 3s ease-in-out infinite; | |
| } | |
| @keyframes float { | |
| 0%, | |
| 100% { | |
| transform: translateY(0px); | |
| } | |
| 50% { | |
| transform: translateY(-10px); | |
| } | |
| } | |
| h1 { | |
| font-family: 'Space Grotesk', sans-serif; | |
| font-size: 2.5rem; | |
| font-weight: 700; | |
| margin-bottom: 1rem; | |
| background: linear-gradient(135deg, var(--accent-cyan), var(--accent-purple)); | |
| -webkit-background-clip: text; | |
| background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| } | |
| .subtitle { | |
| color: var(--text-secondary); | |
| font-size: 1.1rem; | |
| margin-bottom: 3rem; | |
| line-height: 1.6; | |
| } | |
| .status { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); | |
| gap: 1rem; | |
| margin: 2rem 0; | |
| } | |
| .status-card { | |
| padding: 1.5rem; | |
| background: rgba(255, 255, 255, 0.03); | |
| border: 1px solid rgba(255, 255, 255, 0.08); | |
| border-radius: 16px; | |
| } | |
| .status-card small { | |
| color: var(--text-secondary); | |
| font-size: 0.85rem; | |
| text-transform: uppercase; | |
| letter-spacing: 0.1em; | |
| } | |
| .status-card strong { | |
| display: block; | |
| font-size: 1.5rem; | |
| margin-top: 0.5rem; | |
| color: var(--accent-cyan); | |
| } | |
| .progress-bar { | |
| width: 100%; | |
| height: 8px; | |
| background: rgba(255, 255, 255, 0.1); | |
| border-radius: 999px; | |
| overflow: hidden; | |
| margin: 2rem 0; | |
| } | |
| .progress-fill { | |
| height: 100%; | |
| width: 0; | |
| background: linear-gradient(90deg, var(--accent-cyan), var(--accent-purple)); | |
| border-radius: 999px; | |
| animation: progress 2s ease-in-out forwards; | |
| } | |
| @keyframes progress { | |
| to { | |
| width: 100%; | |
| } | |
| } | |
| .spinner { | |
| width: 60px; | |
| height: 60px; | |
| margin: 2rem auto; | |
| border: 4px solid rgba(255, 255, 255, 0.1); | |
| border-top-color: var(--accent-cyan); | |
| border-radius: 50%; | |
| animation: spin 1s linear infinite; | |
| } | |
| @keyframes spin { | |
| to { | |
| transform: rotate(360deg); | |
| } | |
| } | |
| .message { | |
| color: var(--text-secondary); | |
| margin-top: 2rem; | |
| font-size: 0.95rem; | |
| } | |
| .skip-link { | |
| margin-top: 2rem; | |
| color: var(--accent-cyan); | |
| text-decoration: none; | |
| font-weight: 500; | |
| transition: color 0.3s; | |
| } | |
| .skip-link:hover { | |
| color: var(--accent-purple); | |
| text-decoration: underline; | |
| } | |
| .error { | |
| background: rgba(239, 68, 68, 0.1); | |
| border: 1px solid rgba(239, 68, 68, 0.3); | |
| color: #fca5a5; | |
| padding: 1.5rem; | |
| border-radius: 12px; | |
| margin: 2rem 0; | |
| } | |
| .error-title { | |
| font-weight: 600; | |
| margin-bottom: 0.5rem; | |
| } | |
| .error-details { | |
| font-size: 0.9rem; | |
| opacity: 0.8; | |
| } | |
| </style> | |
| <!-- Enhanced UI System --> | |
| <link rel="stylesheet" href="/static/css/ui-enhancements.css"> | |
| <script src="/static/js/icons.js"></script> | |
| <script src="/static/js/error-handler.js"></script> | |
| <script src="/static/js/ui-manager.js"></script> | |
| <!-- API Configuration - Smart Fallback System --> | |
| <script src="/static/js/api-config.js"></script> | |
| <!-- Trading Pairs Loader --> | |
| <script src="/static/js/trading-pairs-loader.js"></script> | |
| <script> | |
| // Initialize API client | |
| window.apiReady = new Promise((resolve) => { | |
| if (window.apiClient) { | |
| console.log('✅ API Client ready'); | |
| resolve(window.apiClient); | |
| } else { | |
| console.error('❌ API Client not loaded'); | |
| } | |
| }); | |
| </script> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <div class="logo"> | |
| <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2"> | |
| <path d="M12 2L2 7L12 12L22 7L12 2Z"></path> | |
| <path d="M2 17L12 22L22 17"></path> | |
| <path d="M2 12L12 17L22 12"></path> | |
| </svg> | |
| </div> | |
| <h1>Crypto Intelligence Hub</h1> | |
| <p class="subtitle">Unified data fabric, AI analytics, and real-time market intelligence</p> | |
| <div id="status-section" class="status"> | |
| <div class="status-card"> | |
| <small>Backend</small> | |
| <strong id="backend-status">Checking...</strong> | |
| </div> | |
| <div class="status-card"> | |
| <small>AI Models</small> | |
| <strong id="models-status">Loading...</strong> | |
| </div> | |
| <div class="status-card"> | |
| <small>Data Streams</small> | |
| <strong id="streams-status">Ready</strong> | |
| </div> | |
| </div> | |
| <div class="progress-bar"> | |
| <div class="progress-fill"></div> | |
| </div> | |
| <div class="spinner" id="spinner"></div> | |
| <div id="message" class="message"> | |
| Initializing system components and checking backend health... | |
| </div> | |
| <div id="error-section" style="display: none;" class="error"> | |
| <div class="error-title">⚠️ Connection Issue</div> | |
| <div class="error-details" id="error-message"></div> | |
| </div> | |
| <a href="/static/pages/dashboard/index.html" class="skip-link">Skip to Dashboard →</a> | |
| </div> | |
| <script defer> | |
| const API_BASE = window.location.origin + '/api'; | |
| const REDIRECT_URL = '/static/pages/dashboard/index.html'; | |
| async function checkBackend() { | |
| const backendStatus = document.getElementById('backend-status'); | |
| const modelsStatus = document.getElementById('models-status'); | |
| const messageEl = document.getElementById('message'); | |
| const errorSection = document.getElementById('error-section'); | |
| const errorMessage = document.getElementById('error-message'); | |
| try { | |
| // Check backend health | |
| messageEl.textContent = 'Connecting to backend...'; | |
| const controller = new AbortController(); | |
| const timeoutId = setTimeout(() => controller.abort(), 5000); | |
| const healthRes = await fetch(`${API_BASE}/health`, { signal: controller.signal }); | |
| clearTimeout(timeoutId); | |
| if (!healthRes.ok) { | |
| throw new Error(`Backend returned ${healthRes.status}`); | |
| } | |
| const healthData = await healthRes.json(); | |
| backendStatus.textContent = '✓ Online'; | |
| backendStatus.style.color = '#22c55e'; | |
| // Check models status | |
| messageEl.textContent = 'Loading AI models...'; | |
| try { | |
| const modelsRes = await fetch(`${API_BASE}/models/status`); | |
| if (modelsRes.ok) { | |
| const modelsData = await modelsRes.json(); | |
| const loadedCount = modelsData.models_loaded || 0; | |
| modelsStatus.textContent = `${loadedCount} Loaded`; | |
| modelsStatus.style.color = loadedCount > 0 ? '#22c55e' : '#f59e0b'; | |
| } else { | |
| modelsStatus.textContent = 'Fallback'; | |
| modelsStatus.style.color = '#f59e0b'; | |
| } | |
| } catch (err) { | |
| modelsStatus.textContent = 'Fallback'; | |
| modelsStatus.style.color = '#f59e0b'; | |
| } | |
| // All checks passed | |
| messageEl.textContent = 'System ready! Redirecting...'; | |
| setTimeout(() => { | |
| window.location.href = REDIRECT_URL; | |
| }, 1500); | |
| } catch (error) { | |
| console.error('Backend check failed:', error); | |
| backendStatus.textContent = '✗ Offline'; | |
| backendStatus.style.color = '#ef4444'; | |
| modelsStatus.textContent = 'N/A'; | |
| modelsStatus.style.color = '#64748b'; | |
| errorSection.style.display = 'block'; | |
| errorMessage.textContent = `Failed to connect to backend: ${error.message}. Please ensure the server is running.`; | |
| messageEl.textContent = 'Backend connection failed. You can still access the dashboard, but live data will not be available.'; | |
| document.getElementById('spinner').style.display = 'none'; | |
| // Still allow manual navigation | |
| setTimeout(() => { | |
| const skipText = document.querySelector('.skip-link'); | |
| skipText.textContent = 'Continue to Dashboard (Offline Mode) →'; | |
| skipText.style.fontSize = '1.1rem'; | |
| skipText.style.fontWeight = '600'; | |
| }, 1000); | |
| } | |
| } | |
| // Start checking backend | |
| setTimeout(checkBackend, 500); | |
| </script> | |
| <script> | |
| // Suppress harmless Permissions-Policy warnings from Hugging Face Space container | |
| // These warnings come from the HF Space iframe and cannot be controlled by the application | |
| // Run IMMEDIATELY (not deferred) to catch warnings as early as possible | |
| (function () { | |
| if (window._hfWarningsSuppressed) return; | |
| const originalWarn = console.warn; | |
| const originalError = console.error; | |
| // List of unrecognized features that cause warnings (from HF Space container) | |
| const unrecognizedFeatures = [ | |
| 'ambient-light-sensor', | |
| 'battery', | |
| 'document-domain', | |
| 'layout-animations', | |
| 'legacy-image-formats', | |
| 'oversized-images', | |
| 'vr', | |
| 'wake-lock', | |
| 'screen-wake-lock', | |
| 'virtual-reality', | |
| 'cross-origin-isolated', | |
| 'execution-while-not-rendered', | |
| 'execution-while-out-of-viewport', | |
| 'keyboard-map', | |
| 'navigation-override', | |
| 'publickey-credentials-get', | |
| 'xr-spatial-tracking' | |
| ]; | |
| const shouldSuppress = (message) => { | |
| if (!message) return false; | |
| const msg = message.toString().toLowerCase(); | |
| // Check for "Unrecognized feature:" pattern | |
| if (msg.includes('unrecognized feature:')) { | |
| return unrecognizedFeatures.some(feature => msg.includes(feature)); | |
| } | |
| // Also check for Permissions-Policy warnings | |
| if (msg.includes('permissions-policy') || msg.includes('feature-policy')) { | |
| return unrecognizedFeatures.some(feature => msg.includes(feature)); | |
| } | |
| // Check for HF Space domain in warning | |
| if (msg.includes('datasourceforcryptocurrency') && | |
| unrecognizedFeatures.some(feature => msg.includes(feature))) { | |
| return true; | |
| } | |
| return false; | |
| }; | |
| console.warn = function (...args) { | |
| const message = args[0]?.toString() || ''; | |
| if (shouldSuppress(message)) { | |
| return; // Suppress silently | |
| } | |
| originalWarn.apply(console, args); | |
| }; | |
| console.error = function (...args) { | |
| const message = args[0]?.toString() || ''; | |
| if (shouldSuppress(message)) { | |
| return; // Suppress silently | |
| } | |
| originalError.apply(console, args); | |
| }; | |
| window._hfWarningsSuppressed = true; | |
| })(); | |
| </script> | |
| </body> | |
| </html> |