Crypto Rug Muncher
clean slate: removing all old data, fresh build
6993919
Raw
History Blame Contribute Delete
12.2 kB
<!DOCTYPE html>
<html lang="en" class="dark">
<head>
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RMI News Network — Live Crypto Intelligence</title>
<style>
:root{--bg:#06060f;--fg:#e8e8f0;--accent:#7c6ff7;--green:#2ecc71;--red:#e74c3c;--card:#0f0f1d;--border:#1e1e35;--muted:#888;--yellow:#f1c40f;--orange:#e67e22;--surface:#121224;--glow:rgba(124,111,247,.15);--cyan:#06b6d4}
*{margin:0;padding:0;box-sizing:border-box}
body{background:var(--bg);color:var(--fg);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',system-ui,sans-serif}
header{background:var(--card);border-bottom:1px solid var(--border);padding:10px 24px;display:flex;align-items:center;gap:12px;position:sticky;top:0;z-index:100}
.logo{font-size:20px;font-weight:800}.logo span:first-child{color:var(--accent)}.logo span:last-child{color:var(--fg)}
nav{display:flex;gap:4px}
nav a{color:var(--muted);text-decoration:none;padding:6px 14px;border-radius:6px;font-size:12px;font-weight:500}
nav a:hover,nav a.active{color:var(--fg);background:rgba(255,255,255,.06)}
nav a.active{color:var(--accent)}
.market-ticker{display:flex;gap:16px;margin-left:auto;font-size:12px}
.ticker-item{display:flex;align-items:center;gap:4px}
.ticker-price{font-weight:700;font-family:monospace}
.ticker-change{font-size:11px}
main{max-width:1500px;margin:0 auto;padding:20px;display:grid;grid-template-columns:240px 1fr 340px;gap:20px}
.sidebar{position:sticky;top:70px;height:fit-content;max-height:calc(100vh-90px);overflow-y:auto}
.section-title{font-size:10px;color:var(--muted);text-transform:uppercase;letter-spacing:2px;margin:16px 0 8px;font-weight:700}
.filter-btn{display:block;width:100%;text-align:left;padding:7px 12px;border-radius:6px;color:var(--muted);font-size:12px;cursor:pointer;border:none;background:none;margin:1px 0}
.filter-btn:hover{background:var(--surface);color:var(--fg)}
.filter-btn.active{background:var(--accent);color:#fff;font-weight:600}
.panel{background:var(--card);border:1px solid var(--border);border-radius:12px;padding:14px;margin-bottom:12px}
.panel h4{font-size:11px;color:var(--muted);text-transform:uppercase;letter-spacing:1px;margin-bottom:10px}
.fear-greed-display{text-align:center;padding:12px 0}
.fg-value{font-size:42px;font-weight:800;line-height:1}
.fg-label{font-size:13px;margin-top:4px}
.fg-bar{height:8px;border-radius:4px;margin-top:6px;background:linear-gradient(90deg,#ff0044,#ff8800,#ffd700,#88ff00,#00ff88)}
.fg-marker{width:4px;height:14px;border-radius:2px;background:#fff;position:relative;top:-11px}
.price-row{display:flex;justify-content:space-between;padding:5px 0;font-size:12px;border-bottom:1px solid rgba(255,255,255,.03)}
.price-row:last-child{border:none}
.price-name{color:var(--muted)}
.price-value{font-weight:600;font-family:monospace}
.article-card{background:var(--card);border:1px solid var(--border);border-radius:10px;padding:16px;margin-bottom:8px;transition:.15s;cursor:pointer}
.article-card:hover{border-color:var(--accent);box-shadow:0 4px 20px var(--glow)}
.article-source{font-size:10px;color:var(--accent);font-weight:600;margin-bottom:4px}
.article-title{font-size:15px;font-weight:700;line-height:1.4;margin-bottom:6px}
.article-title a{color:var(--fg);text-decoration:none}
.article-meta{font-size:10px;color:var(--muted)}
.article-card.high-impact{border-left:3px solid var(--red)}
.article-card.medium-impact{border-left:3px solid var(--orange)}
.prediction-item{padding:7px 0;border-bottom:1px solid rgba(255,255,255,.03);font-size:11px}
.prediction-item:last-child{border:none}
.prediction-title{color:var(--fg);margin-bottom:2px}
.prediction-volume{color:var(--muted);font-size:10px}
.loading{text-align:center;padding:40px;color:var(--muted)}
.spinner{display:inline-block;width:20px;height:20px;border:2px solid var(--border);border-top-color:var(--accent);border-radius:50%;animation:spin .6s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
.brief-bar{background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:10px 14px;margin-bottom:16px;font-size:12px;color:var(--muted)}
.brief-bar b{color:var(--fg)}
.source-badge{display:inline-block;padding:2px 6px;border-radius:3px;font-size:9px;background:rgba(6,182,212,.1);color:var(--cyan);margin-right:4px}
</style>
</head>
<body>
<header>
<div class="logo"><span>RMI</span><span> News</span></div>
<nav>
<a href="/rugcharts">RugCharts</a>
<a href="/rugmaps">RugMaps</a>
<a href="/news" class="active">News</a>
</nav>
<div class="market-ticker" id="tickerBar">
<div class="spinner"></div>
</div>
</header>
<main>
<!-- Left Sidebar -->
<aside class="sidebar">
<div class="panel">
<h4>😨 Fear & Greed</h4>
<div id="fearGreedWidget"><div class="spinner"></div></div>
</div>
<div class="panel">
<h4>💰 Live Prices</h4>
<div id="pricesWidget"><div class="spinner"></div></div>
</div>
<div class="panel">
<h4>🔥 Trending</h4>
<div id="trendingWidget"><div class="spinner"></div></div>
</div>
<div class="section-title">Filters</div>
<button class="filter-btn active" onclick="filterNews('all')">All News</button>
<button class="filter-btn" onclick="filterNews('bitcoin')">Bitcoin</button>
<button class="filter-btn" onclick="filterNews('ethereum')">Ethereum</button>
<button class="filter-btn" onclick="filterNews('defi')">DeFi</button>
<button class="filter-btn" onclick="filterNews('regulation')">Regulation</button>
<button class="filter-btn" onclick="filterNews('security')">Security</button>
</aside>
<!-- Main Feed -->
<div>
<div class="brief-bar" id="briefBar"><div class="spinner"></div> Loading market data...</div>
<div style="display:flex;gap:6px;margin-bottom:12px;flex-wrap:wrap" id="tabBar">
<button class="sort-btn active" onclick="switchTab('all')">📰 All News</button>
<button class="sort-btn" onclick="switchTab('ct')">𝕏 CT Rundown</button>
<button class="sort-btn" onclick="switchTab('weekly')">⭐ Weekly Best</button>
<button class="sort-btn" onclick="switchTab('academic')">📚 Academic</button>
<button class="sort-btn" onclick="switchTab('social')">💬 Social</button>
</div>
<div id="articles"><div class="loading"><div class="spinner"></div> Loading news...</div></div>
<div id="ctRundown" style="display:none"></div>
<div id="weeklyContent" style="display:none"></div>
<div id="academicContent" style="display:none"></div>
<div id="socialContent" style="display:none"></div>
</div>
<!-- Right Sidebar -->
<aside class="sidebar">
<div class="panel">
<h4>🎲 Prediction Markets</h4>
<div id="polyWidget"><div class="spinner"></div></div>
</div>
<div class="panel">
<h4>📡 Data Sources</h4>
<div style="font-size:11px;color:var(--muted);line-height:1.6">
<span class="source-badge">CG</span> CoinGecko<br>
<span class="source-badge">FG</span> Fear & Greed<br>
<span class="source-badge">PM</span> Polymarket<br>
<span class="source-badge">RSS</span> 200+ RSS Feeds<br>
<span class="source-badge">DB</span> DataBus (56 chains)
</div>
</div>
</aside>
</main>
<script>
const DB = '/api/v1/databus';
let allArticles = [];
let currentFilter = 'all';
async function loadAll() {
// Load market data and news in parallel
const [prices, fearGreed, trending, polymarket, brief, newsData] = await Promise.all([
fetch(DB+'/market/prices').then(r=>r.json()).catch(()=>null),
fetch(DB+'/market/fear-greed').then(r=>r.json()).catch(()=>null),
fetch(DB+'/market/trending').then(r=>r.json()).catch(()=>null),
fetch(DB+'/market/prediction-markets').then(r=>r.json()).catch(()=>null),
fetch(DB+'/market/brief').then(r=>r.json()).catch(()=>null),
fetch(DB+'/news/full?limit=30').then(r=>r.json()).catch(()=>null),
]);
// Ticker bar
if (prices?.prices) {
let html = '';
for (const [coin, data] of Object.entries(prices.prices)) {
const change = data.usd_24h_change || 0;
const color = change < -3 ? 'var(--red)' : change < 0 ? 'var(--orange)' : 'var(--green)';
html += `<div class="ticker-item">
<span style="color:var(--muted)">${coin.toUpperCase().slice(0,4)}</span>
<span class="ticker-price">$${(data.usd||0).toLocaleString()}</span>
<span class="ticker-change" style="color:${color}">${change>=0?'+':''}${change.toFixed(1)}%</span>
</div>`;
}
document.getElementById('tickerBar').innerHTML = html;
}
// Fear & Greed
if (fearGreed) {
const v = fearGreed.value || 50;
document.getElementById('fearGreedWidget').innerHTML = `
<div class="fear-greed-display">
<div class="fg-value" style="color:${fearGreed.color}">${v}</div>
<div class="fg-label">${fearGreed.classification}</div>
<div class="fg-bar"><div class="fg-marker" style="left:${v}%"></div></div>
</div>`;
}
// Prices
if (prices?.prices) {
let html = '';
for (const [coin, data] of Object.entries(prices.prices)) {
const change = data.usd_24h_change || 0;
const color = change < 0 ? 'var(--red)' : 'var(--green)';
html += `<div class="price-row">
<span class="price-name">${coin.charAt(0).toUpperCase()+coin.slice(1)}</span>
<span class="price-value">$${(data.usd||0).toLocaleString()} <span style="color:${color};font-size:10px">${change>=0?'+':''}${change.toFixed(1)}%</span></span>
</div>`;
}
document.getElementById('pricesWidget').innerHTML = html;
}
// Trending
if (trending?.trending) {
document.getElementById('trendingWidget').innerHTML = trending.trending.slice(0,8).map(t =>
`<div class="price-row">
<span class="price-name">${t.name}</span>
<span style="font-size:10px;color:var(--muted)">${t.symbol} #${t.market_cap_rank||'?'}</span>
</div>`
).join('');
}
// Polymarket
if (polymarket?.markets?.length) {
document.getElementById('polyWidget').innerHTML = polymarket.markets.map(m =>
`<div class="prediction-item">
<div class="prediction-title">${m.title.slice(0,80)}</div>
<div class="prediction-volume">Vol: $${(m.volume||0).toLocaleString()}</div>
</div>`
).join('');
}
// Brief bar
if (brief?.brief || newsData?.market_brief?.brief) {
const b = brief?.brief || newsData?.market_brief?.brief || '';
document.getElementById('briefBar').innerHTML = `<b>Market:</b> ${b}`;
}
// News articles
const newsContent = newsData?.headlines;
if (newsContent?.articles) {
allArticles = newsContent.articles;
} else {
// Fallback to existing news endpoint
try {
const fallback = await fetch('/news/daily-data').then(r=>r.json());
allArticles = fallback?.articles || [];
} catch(e) { allArticles = []; }
}
renderArticles();
}
function filterNews(cat) {
currentFilter = cat;
document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));
event.target.classList.add('active');
renderArticles();
}
function renderArticles() {
let filtered = allArticles;
if (currentFilter !== 'all') {
filtered = allArticles.filter(a => {
const text = (a.title + ' ' + (a.source||'') + ' ' + (a.category||'')).toLowerCase();
return text.includes(currentFilter.toLowerCase());
});
}
if (!filtered.length) {
document.getElementById('articles').innerHTML = '<div class="loading">No articles found. Try a different filter.</div>';
return;
}
document.getElementById('articles').innerHTML = filtered.map(a => {
const impact = a.impact || (a.category === 'security' ? 'high' : 'medium');
return `<div class="article-card ${impact}-impact">
<div class="article-source">${a.source||'Unknown'} · ${a.category||'General'} · ${a.published||''}</div>
<div class="article-title"><a href="${a.url||'#'}" target="_blank">${a.title||'Untitled'}</a></div>
<div class="article-meta">${a.summary||a.description||''.slice(0,150)}</div>
</div>`;
}).join('');
}
loadAll();
// Refresh every 2 minutes
setInterval(loadAll, 120000);
</script>
</body>
</html>