Cursor Agent
fix: Replace modal with slide-out drawer panel from right side
70c7696
<!DOCTYPE html>
<html lang="en" dir="ltr" data-theme="light">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Crypto Monitor ULTIMATE - Premium Dashboard with Real-time Market Data, AI Analysis & Sentiment">
<meta name="theme-color" content="#14b8a6">
<title>Dashboard | Crypto Monitor</title>
<!-- Favicon -->
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cdefs%3E%3ClinearGradient id='g' x1='0%25' y1='0%25' x2='100%25' y2='100%25'%3E%3Cstop offset='0%25' stop-color='%232dd4bf'/%3E%3Cstop offset='50%25' stop-color='%2322d3ee'/%3E%3Cstop offset='100%25' stop-color='%233b82f6'/%3E%3C/linearGradient%3E%3C/defs%3E%3Ccircle cx='50' cy='50' r='45' fill='url(%23g)'/%3E%3Cpath d='M50 25 L65 45 L50 40 L35 45 Z M50 75 L35 55 L50 60 L65 55 Z' fill='white'/%3E%3C/svg%3E">
<!-- Preconnect to external domains -->
<link rel="preconnect" href="https://fonts.googleapis.com" crossorigin>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preconnect" href="https://cdnjs.cloudflare.com" crossorigin>
<link rel="dns-prefetch" href="https://fonts.googleapis.com">
<link rel="dns-prefetch" href="https://cdnjs.cloudflare.com">
<!-- Critical CSS - Inline for faster first paint -->
<style>
/* Critical above-the-fold styles */
:root{--teal-dark:#0d7377;--teal:#14b8a6;--teal-light:#2dd4bf;--cyan:#22d3ee;--text-primary:#0f2926;--text-secondary:#2a5f5a;--bg-main:#ffffff;--bg-secondary:#f8fdfc;--sidebar-width:260px}
*,*::before,*::after{margin:0;padding:0;box-sizing:border-box}
html{font-size:14px;-webkit-font-smoothing:antialiased}
body{font-family:system-ui,-apple-system,sans-serif;font-size:14px;line-height:1.5;color:var(--text-secondary);background:var(--bg-main);min-height:100vh}
.app-container{display:flex;min-height:100vh}
.sidebar{position:fixed;left:0;top:0;bottom:0;width:var(--sidebar-width);background:linear-gradient(180deg,#fff 0%,#f8fdfc 100%);border-right:1px solid rgba(20,184,166,0.12);z-index:100}
.main-content{flex:1;margin-left:var(--sidebar-width);min-height:100vh}
.page-content{padding:1.5rem;max-width:1400px;margin:0 auto}
@media(max-width:768px){.sidebar{transform:translateX(-100%)}.main-content{margin-left:0}}
</style>
<!-- Non-critical CSS - Load with media trick for async loading -->
<link rel="stylesheet" href="/static/shared/css/design-system.css?v=3.0" media="print" onload="this.media='all'">
<noscript><link rel="stylesheet" href="/static/shared/css/design-system.css?v=3.0"></noscript>
<link rel="stylesheet" href="/static/shared/css/global.css?v=3.0" media="print" onload="this.media='all'">
<noscript><link rel="stylesheet" href="/static/shared/css/global.css?v=3.0"></noscript>
<link rel="stylesheet" href="/static/shared/css/components.css" media="print" onload="this.media='all'">
<noscript><link rel="stylesheet" href="/static/shared/css/components.css"></noscript>
<link rel="stylesheet" href="/static/shared/css/layout.css" media="print" onload="this.media='all'">
<noscript><link rel="stylesheet" href="/static/shared/css/layout.css"></noscript>
<link rel="stylesheet" href="/static/pages/dashboard/dashboard.css?v=3.0" media="print" onload="this.media='all'">
<noscript><link rel="stylesheet" href="/static/pages/dashboard/dashboard.css?v=3.0"></noscript>
<link rel="stylesheet" href="/static/shared/css/status-drawer.css" media="print" onload="this.media='all'">
<noscript><link rel="stylesheet" href="/static/shared/css/status-drawer.css"></noscript>
<!-- Error Suppressor - Suppress external service errors (load first) -->
<script src="/static/shared/js/utils/error-suppressor.js"></script>
<!-- Status Drawer Component -->
<script src="/static/shared/js/components/status-drawer.js"></script>
<!-- Crypto Icons Library -->
<script src="/static/assets/icons/crypto-icons.js"></script>
<!-- API Configuration - Smart Fallback System -->
<script src="/static/js/api-config.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="app-container">
<!-- Sidebar -->
<aside id="sidebar-container"></aside>
<!-- Main Content -->
<main class="main-content">
<!-- Header -->
<header id="header-container"></header>
<!-- Dashboard Content -->
<div class="page-content">
<!-- Page Header -->
<div class="page-header">
<div class="page-title">
<h1>
<span class="page-icon">
<!-- High-quality dashboard icon -->
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="url(#iconGradient)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<defs>
<linearGradient id="iconGradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#2dd4bf"/>
<stop offset="50%" stop-color="#22d3ee"/>
<stop offset="100%" stop-color="#3b82f6"/>
</linearGradient>
</defs>
<rect x="3" y="3" width="7" height="7" rx="1"/>
<rect x="14" y="3" width="7" height="7" rx="1"/>
<rect x="14" y="14" width="7" height="7" rx="1"/>
<rect x="3" y="14" width="7" height="7" rx="1"/>
</svg>
</span>
Dashboard
</h1>
<p class="page-subtitle">Real-time Market Data & AI Analysis</p>
</div>
<div class="page-actions">
<button id="refresh-btn" class="btn-icon" title="Refresh" aria-label="Refresh data">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8"/>
<path d="M21 3v5h-5"/>
</svg>
</button>
<span id="last-update" class="last-update">Loading...</span>
</div>
</div>
<!-- Dynamic content injected by JS -->
</div>
</main>
</div>
<!-- Toast Container -->
<div id="toast-container" aria-live="polite"></div>
<script type="module">
// Defer non-critical initialization
(async function() {
const initApp = async () => {
try {
const { LayoutManager } = await import('/static/shared/js/core/layout-manager.js?v=3.0');
await LayoutManager.init('dashboard');
// Load dashboard module after layout is ready
const { default: dashboardPage } = await import('/static/pages/dashboard/dashboard.js?v=3.0');
window.dashboardPage = dashboardPage;
window.addEventListener('beforeunload', () => {
if (window.dashboardPage) window.dashboardPage.destroy();
});
} catch (error) {
console.error('Failed to initialize dashboard:', error);
}
};
// Use requestIdleCallback if available, otherwise setTimeout
if (window.requestIdleCallback) {
window.requestIdleCallback(initApp, { timeout: 2000 });
} else {
setTimeout(initApp, 100);
}
})();
</script>
</body>
</html>