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:
CyberMind-FR 2026-01-24 03:58:12 +01:00
parent d04c0c6355
commit 814a85754d

View File

@ -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>