style(cyberfeed): Amber & green CRT color scheme for timeline
- Amber (#ffb000) for titles, borders, hover effects - Green (#33ff33) for dates, sources, navigation - Gradient timeline line (amber → green → amber) - Glow effects on text and borders - Audio items highlighted in green - Status bar with item count and sync time - Emojified content from AWK parser preserved Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
d04c0c6355
commit
814a85754d
@ -231,7 +231,7 @@ parse_rss() {
|
||||
'
|
||||
}
|
||||
|
||||
# === TIMELINE HTML GENERATOR (1/3 CRT + 2/3 Standard) ===
|
||||
# === TIMELINE HTML GENERATOR (Amber & Green CRT Colors) ===
|
||||
generate_timeline() {
|
||||
local json_file="$1"
|
||||
local output_file="${OUTPUT_DIR}/timeline.html"
|
||||
@ -244,386 +244,254 @@ generate_timeline() {
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>⚡ CYBERFEED TIMELINE ⚡</title>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=Share+Tech+Mono&family=VT323&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=Share+Tech+Mono&display=swap');
|
||||
:root {
|
||||
--neon-cyan: #0ff;
|
||||
--neon-magenta: #f0f;
|
||||
--neon-green: #0f0;
|
||||
--dark-bg: #0a0a0f;
|
||||
--text-primary: #e0e0e0;
|
||||
--text-dim: #606080;
|
||||
--crt-glow: #33ff33;
|
||||
--amber: #ffb000;
|
||||
--amber-dim: #cc8800;
|
||||
--amber-glow: rgba(255,176,0,0.4);
|
||||
--green: #33ff33;
|
||||
--green-dim: #22aa22;
|
||||
--green-glow: rgba(51,255,51,0.4);
|
||||
--dark-bg: #0a0a0a;
|
||||
--darker-bg: #050505;
|
||||
--text-dim: #555;
|
||||
}
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
font-family: 'Share Tech Mono', monospace;
|
||||
background: var(--dark-bg);
|
||||
color: var(--text-primary);
|
||||
color: var(--amber);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* === SPLIT LAYOUT === */
|
||||
.split-container {
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* === CRT MONITOR (1/3) === */
|
||||
.crt-section {
|
||||
width: 33.33%;
|
||||
padding: 20px;
|
||||
background: #111;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-right: 2px solid #333;
|
||||
}
|
||||
.crt-monitor {
|
||||
flex: 1;
|
||||
background: #1a1a1a;
|
||||
border-radius: 20px;
|
||||
padding: 15px;
|
||||
position: relative;
|
||||
box-shadow:
|
||||
inset 0 0 50px rgba(0,0,0,0.5),
|
||||
0 0 20px rgba(0,255,0,0.1);
|
||||
}
|
||||
/* CRT Bezel */
|
||||
.crt-monitor::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: 20px;
|
||||
border: 8px solid #2a2a2a;
|
||||
background: linear-gradient(135deg, #3a3a3a 0%, #1a1a1a 50%, #2a2a2a 100%);
|
||||
z-index: -1;
|
||||
}
|
||||
/* Corner screws */
|
||||
.crt-monitor::after {
|
||||
content: '⊕';
|
||||
position: absolute;
|
||||
top: 8px; left: 12px;
|
||||
color: #555;
|
||||
font-size: 10px;
|
||||
}
|
||||
.crt-screen {
|
||||
background: #001a00;
|
||||
border-radius: 10px;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
border: 3px solid #333;
|
||||
}
|
||||
/* Scanlines */
|
||||
.crt-screen::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: repeating-linear-gradient(
|
||||
0deg,
|
||||
rgba(0,0,0,0.15) 0px,
|
||||
rgba(0,0,0,0.15) 1px,
|
||||
transparent 1px,
|
||||
transparent 2px
|
||||
);
|
||||
pointer-events: none;
|
||||
z-index: 10;
|
||||
}
|
||||
/* Screen curvature */
|
||||
.crt-screen::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: radial-gradient(ellipse at center, transparent 60%, rgba(0,0,0,0.4) 100%);
|
||||
pointer-events: none;
|
||||
z-index: 11;
|
||||
}
|
||||
/* Phosphor glow effect */
|
||||
.crt-content {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
padding: 15px;
|
||||
font-family: 'VT323', monospace;
|
||||
font-size: 14px;
|
||||
color: var(--crt-glow);
|
||||
text-shadow: 0 0 5px var(--crt-glow), 0 0 10px rgba(0,255,0,0.5);
|
||||
animation: flicker 0.15s infinite;
|
||||
}
|
||||
@keyframes flicker {
|
||||
0%, 100% { opacity: 0.98; }
|
||||
50% { opacity: 1; }
|
||||
}
|
||||
.crt-content::-webkit-scrollbar { width: 6px; }
|
||||
.crt-content::-webkit-scrollbar-track { background: #001a00; }
|
||||
.crt-content::-webkit-scrollbar-thumb { background: #0f0; border-radius: 3px; }
|
||||
|
||||
.crt-item {
|
||||
margin-bottom: 12px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px dashed rgba(0,255,0,0.3);
|
||||
}
|
||||
.crt-item .time {
|
||||
color: #0a0;
|
||||
font-size: 11px;
|
||||
}
|
||||
.crt-item .title {
|
||||
color: var(--crt-glow);
|
||||
font-size: 13px;
|
||||
margin: 4px 0;
|
||||
}
|
||||
.crt-item .title a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
.crt-item .title a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.crt-item .source {
|
||||
color: #0a0;
|
||||
font-size: 10px;
|
||||
}
|
||||
/* Power LED */
|
||||
.crt-power {
|
||||
position: absolute;
|
||||
bottom: 15px;
|
||||
right: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.crt-led {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background: #0f0;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 8px #0f0, 0 0 15px #0f0;
|
||||
animation: led-pulse 2s ease-in-out infinite;
|
||||
}
|
||||
@keyframes led-pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.6; }
|
||||
}
|
||||
.crt-label {
|
||||
font-family: 'Orbitron', sans-serif;
|
||||
font-size: 8px;
|
||||
color: #666;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
/* === STANDARD TIMELINE (2/3) === */
|
||||
.timeline-section {
|
||||
width: 66.66%;
|
||||
padding: 20px;
|
||||
overflow-y: auto;
|
||||
max-height: 100vh;
|
||||
}
|
||||
.header {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
border-bottom: 1px solid var(--neon-cyan);
|
||||
margin-bottom: 30px;
|
||||
border-bottom: 2px solid var(--amber);
|
||||
box-shadow: 0 2px 20px var(--amber-glow);
|
||||
}
|
||||
.header h1 {
|
||||
font-family: 'Orbitron', sans-serif;
|
||||
font-size: 1.8rem;
|
||||
color: var(--neon-cyan);
|
||||
text-shadow: 0 0 10px var(--neon-cyan);
|
||||
font-size: 2rem;
|
||||
color: var(--amber);
|
||||
text-shadow: 0 0 10px var(--amber), 0 0 20px var(--amber-glow);
|
||||
letter-spacing: 0.1em;
|
||||
}
|
||||
.header .subtitle {
|
||||
color: var(--green);
|
||||
margin-top: 8px;
|
||||
font-size: 0.85rem;
|
||||
text-shadow: 0 0 8px var(--green-glow);
|
||||
}
|
||||
.nav-links {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
.nav-links a {
|
||||
color: var(--neon-cyan);
|
||||
color: var(--green);
|
||||
margin: 0 15px;
|
||||
text-decoration: none;
|
||||
font-size: 0.9rem;
|
||||
padding: 8px 16px;
|
||||
border: 1px solid var(--green-dim);
|
||||
transition: all 0.3s;
|
||||
}
|
||||
.nav-links a:hover {
|
||||
text-shadow: 0 0 10px var(--neon-cyan);
|
||||
background: var(--green);
|
||||
color: var(--dark-bg);
|
||||
box-shadow: 0 0 15px var(--green-glow);
|
||||
}
|
||||
.timeline {
|
||||
max-width: 700px;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
padding-left: 35px;
|
||||
padding-left: 40px;
|
||||
}
|
||||
.timeline::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 12px;
|
||||
left: 15px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 2px;
|
||||
background: linear-gradient(180deg, var(--neon-cyan), var(--neon-magenta));
|
||||
box-shadow: 0 0 10px var(--neon-cyan);
|
||||
width: 3px;
|
||||
background: linear-gradient(180deg, var(--amber), var(--green), var(--amber));
|
||||
box-shadow: 0 0 15px var(--amber-glow);
|
||||
}
|
||||
.timeline-date {
|
||||
font-family: 'Orbitron', sans-serif;
|
||||
font-size: 1rem;
|
||||
color: var(--neon-magenta);
|
||||
margin: 25px 0 12px -35px;
|
||||
padding-left: 35px;
|
||||
text-shadow: 0 0 5px var(--neon-magenta);
|
||||
font-size: 1.1rem;
|
||||
color: var(--green);
|
||||
margin: 30px 0 15px -40px;
|
||||
padding-left: 40px;
|
||||
text-shadow: 0 0 8px var(--green-glow);
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
.timeline-item {
|
||||
position: relative;
|
||||
margin-bottom: 15px;
|
||||
padding: 12px;
|
||||
background: rgba(0,255,255,0.05);
|
||||
border: 1px solid rgba(0,255,255,0.2);
|
||||
border-radius: 6px;
|
||||
margin-bottom: 20px;
|
||||
padding: 15px;
|
||||
background: rgba(255,176,0,0.03);
|
||||
border: 1px solid rgba(255,176,0,0.2);
|
||||
border-left: 3px solid var(--amber);
|
||||
border-radius: 4px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
.timeline-item:hover {
|
||||
background: rgba(255,176,0,0.08);
|
||||
border-color: var(--amber);
|
||||
box-shadow: 0 0 20px var(--amber-glow);
|
||||
}
|
||||
.timeline-item::before {
|
||||
content: '◆';
|
||||
content: '●';
|
||||
position: absolute;
|
||||
left: -28px;
|
||||
top: 14px;
|
||||
color: var(--neon-cyan);
|
||||
font-size: 10px;
|
||||
text-shadow: 0 0 10px var(--neon-cyan);
|
||||
left: -32px;
|
||||
top: 18px;
|
||||
color: var(--amber);
|
||||
font-size: 12px;
|
||||
text-shadow: 0 0 10px var(--amber);
|
||||
}
|
||||
.timeline-item.has-audio {
|
||||
border-left-color: var(--green);
|
||||
}
|
||||
.timeline-item.has-audio::before {
|
||||
content: '♪';
|
||||
color: var(--green);
|
||||
text-shadow: 0 0 10px var(--green);
|
||||
}
|
||||
.timeline-item.has-video::before {
|
||||
content: '▶';
|
||||
color: var(--green);
|
||||
text-shadow: 0 0 10px var(--green);
|
||||
}
|
||||
.timeline-item.has-audio::before { content: '🎧'; font-size: 12px; }
|
||||
.timeline-item.has-video::before { content: '📺'; font-size: 12px; }
|
||||
.item-time {
|
||||
font-size: 0.7rem;
|
||||
color: var(--neon-magenta);
|
||||
margin-bottom: 4px;
|
||||
font-size: 0.75rem;
|
||||
color: var(--green-dim);
|
||||
margin-bottom: 6px;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
.item-title {
|
||||
font-family: 'Orbitron', sans-serif;
|
||||
font-size: 0.9rem;
|
||||
color: var(--neon-cyan);
|
||||
margin-bottom: 6px;
|
||||
font-size: 1rem;
|
||||
color: var(--amber);
|
||||
margin-bottom: 8px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.item-title a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
transition: text-shadow 0.3s;
|
||||
}
|
||||
.item-title a:hover {
|
||||
text-shadow: 0 0 10px var(--neon-cyan);
|
||||
text-shadow: 0 0 15px var(--amber);
|
||||
}
|
||||
.item-desc {
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-primary);
|
||||
opacity: 0.8;
|
||||
margin-bottom: 8px;
|
||||
line-height: 1.4;
|
||||
font-size: 0.85rem;
|
||||
color: var(--amber-dim);
|
||||
opacity: 0.85;
|
||||
margin-bottom: 10px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.item-source {
|
||||
display: inline-block;
|
||||
background: rgba(255,0,255,0.2);
|
||||
border: 1px solid var(--neon-magenta);
|
||||
padding: 2px 6px;
|
||||
font-size: 0.6rem;
|
||||
background: rgba(51,255,51,0.1);
|
||||
border: 1px solid var(--green-dim);
|
||||
color: var(--green);
|
||||
padding: 3px 10px;
|
||||
font-size: 0.65rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
}
|
||||
.item-source.duration {
|
||||
background: rgba(255,176,0,0.1);
|
||||
border-color: var(--amber-dim);
|
||||
color: var(--amber);
|
||||
}
|
||||
.audio-player {
|
||||
margin-top: 12px;
|
||||
}
|
||||
.audio-player { margin-top: 8px; }
|
||||
.audio-player audio {
|
||||
width: 100%;
|
||||
height: 35px;
|
||||
border-radius: 18px;
|
||||
height: 40px;
|
||||
border-radius: 20px;
|
||||
filter: sepia(100%) hue-rotate(-10deg) saturate(200%);
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 900px) {
|
||||
.split-container { flex-direction: column; }
|
||||
.crt-section { width: 100%; height: 40vh; border-right: none; border-bottom: 2px solid #333; }
|
||||
.timeline-section { width: 100%; max-height: none; }
|
||||
.status-bar {
|
||||
text-align: center;
|
||||
padding: 15px;
|
||||
margin-top: 30px;
|
||||
border-top: 1px solid var(--green-dim);
|
||||
font-size: 0.7rem;
|
||||
color: var(--text-dim);
|
||||
}
|
||||
.status-bar span {
|
||||
color: var(--green);
|
||||
margin: 0 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="split-container">
|
||||
<!-- CRT Monitor Section (1/3) -->
|
||||
<div class="crt-section">
|
||||
<div class="crt-monitor">
|
||||
<div class="crt-screen">
|
||||
<div class="crt-content" id="crt-feed">
|
||||
<p>> INITIALIZING FEED MATRIX...</p>
|
||||
<p>> AWAITING DATA STREAM...</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="crt-power">
|
||||
<div class="crt-led"></div>
|
||||
<span class="crt-label">POWER</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Standard Timeline Section (2/3) -->
|
||||
<div class="timeline-section">
|
||||
<header class="header">
|
||||
<h1>⚡ TIMELINE ⚡</h1>
|
||||
<p style="color: var(--text-dim); margin-top: 8px; font-size: 0.8rem;">Chronological Feed History</p>
|
||||
</header>
|
||||
<nav class="nav-links">
|
||||
<a href="/cyberfeed/">← Dashboard</a>
|
||||
<a href="/cyberfeed/timeline.html">🔄 Refresh</a>
|
||||
</nav>
|
||||
<div class="timeline" id="timeline">
|
||||
<p style="text-align:center; color: var(--text-dim);">Loading timeline...</p>
|
||||
</div>
|
||||
</div>
|
||||
<header class="header">
|
||||
<h1>⚡ TIMELINE ⚡</h1>
|
||||
<p class="subtitle">[ NEURAL FEED MATRIX ]</p>
|
||||
</header>
|
||||
<nav class="nav-links">
|
||||
<a href="/cyberfeed/">← DASHBOARD</a>
|
||||
<a href="/cyberfeed/timeline.html">↻ REFRESH</a>
|
||||
</nav>
|
||||
<div class="timeline" id="timeline">
|
||||
<p style="text-align:center; color: var(--amber-dim);">LOADING DATA STREAM...</p>
|
||||
</div>
|
||||
<div class="status-bar">
|
||||
<span id="item-count">-- ITEMS</span> |
|
||||
<span id="last-update">SYNCING...</span> |
|
||||
CYBERFEED v0.2
|
||||
</div>
|
||||
|
||||
<script>
|
||||
async function loadFeed() {
|
||||
async function loadTimeline() {
|
||||
try {
|
||||
const resp = await fetch('/cyberfeed/feeds.json?' + Date.now());
|
||||
const items = await resp.json();
|
||||
|
||||
// Sort by date
|
||||
items.sort((a, b) => new Date(b.date || 0) - new Date(a.date || 0));
|
||||
document.getElementById('item-count').textContent = items.length + ' ITEMS';
|
||||
document.getElementById('last-update').textContent = new Date().toLocaleTimeString('fr-FR', {hour12: false});
|
||||
|
||||
// CRT Feed (scrolling latest)
|
||||
let crtHtml = '> CYBERFEED NEURAL MATRIX v0.2\\n> ' + items.length + ' TRANSMISSIONS RECEIVED\\n> ════════════════════════════\\n\\n';
|
||||
items.slice(0, 15).forEach(item => {
|
||||
crtHtml += '<div class="crt-item">';
|
||||
crtHtml += '<div class="time">> ' + (item.date || 'Unknown') + '</div>';
|
||||
crtHtml += '<div class="title"><a href="' + item.link + '" target="_blank">' + (item.title || 'Untitled') + '</a></div>';
|
||||
crtHtml += '<div class="source">[' + (item.source || 'RSS') + ']</div>';
|
||||
crtHtml += '</div>';
|
||||
});
|
||||
document.getElementById('crt-feed').innerHTML = crtHtml;
|
||||
|
||||
// Standard Timeline
|
||||
// Group by date
|
||||
const grouped = {};
|
||||
items.forEach(item => {
|
||||
const dateStr = item.date ? new Date(item.date).toLocaleDateString('fr-FR', {
|
||||
weekday: 'long', year: 'numeric', month: 'long', day: 'numeric'
|
||||
}) : 'Date inconnue';
|
||||
}).toUpperCase() : 'DATE INCONNUE';
|
||||
if (!grouped[dateStr]) grouped[dateStr] = [];
|
||||
grouped[dateStr].push(item);
|
||||
});
|
||||
|
||||
let html = '';
|
||||
Object.keys(grouped).forEach(date => {
|
||||
html += '<div class="timeline-date">' + date + '</div>';
|
||||
html += '<div class="timeline-date">► ' + date + '</div>';
|
||||
grouped[date].forEach(item => {
|
||||
const hasMedia = item.media_type ? ' has-' + item.media_type : '';
|
||||
html += '<div class="timeline-item' + hasMedia + '">';
|
||||
html += '<div class="item-time">⏰ ' + (item.date || '') + '</div>';
|
||||
html += '<div class="item-time">◷ ' + (item.date || '') + '</div>';
|
||||
html += '<div class="item-title"><a href="' + item.link + '" target="_blank">' + item.title + '</a></div>';
|
||||
if (item.desc) html += '<div class="item-desc">' + item.desc + '</div>';
|
||||
if (item.enclosure && item.media_type === 'audio') {
|
||||
html += '<div class="audio-player"><audio controls preload="none"><source src="' + item.enclosure + '" type="audio/mpeg"></audio></div>';
|
||||
}
|
||||
html += '<span class="item-source">' + (item.source || 'RSS') + '</span>';
|
||||
if (item.duration) html += ' <span class="item-source">⏱ ' + item.duration + '</span>';
|
||||
if (item.duration) html += ' <span class="item-source duration">◷ ' + item.duration + '</span>';
|
||||
html += '</div>';
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById('timeline').innerHTML = html || '<p style="text-align:center;">Aucun élément</p>';
|
||||
document.getElementById('timeline').innerHTML = html || '<p style="text-align:center; color: var(--amber);">NO DATA</p>';
|
||||
} catch(e) {
|
||||
document.getElementById('crt-feed').innerHTML = '> ERROR: FEED CONNECTION LOST\\n> RETRYING...';
|
||||
document.getElementById('timeline').innerHTML = '<p style="text-align:center; color: #f00;">Erreur de chargement</p>';
|
||||
document.getElementById('timeline').innerHTML = '<p style="text-align:center; color: #ff3333;">ERROR: CONNECTION LOST</p>';
|
||||
}
|
||||
}
|
||||
|
||||
loadFeed();
|
||||
setInterval(loadFeed, 180000);
|
||||
loadTimeline();
|
||||
setInterval(loadTimeline, 180000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user