/* ===================================================
JAESWIFT BLOG — Individual Post Loader
Markdown rendering, operator stats, navigation
=================================================== */
(function () {
'use strict';
const API = window.location.hostname === 'localhost'
? 'http://localhost:5000'
: '/api';
// ─── Simple Markdown Parser ───
function parseMarkdown(md) {
let html = md;
// Code blocks (```lang ... ```)
html = html.replace(/```(\w+)?\n([\s\S]*?)```/g, (_, lang, code) => {
const cls = lang ? ` class="language-${lang}"` : '';
return `
.*<\/li>\n?)+/g, (match) => ``);
// Horizontal rule
html = html.replace(/^---$/gm, '
');
// Paragraphs (double newline)
html = html.replace(/\n\n(?!<)/g, '');
html = '
' + html + '
';
// Clean up empty paragraphs and fix nesting
html = html.replace(/<(h[1-4]|pre|ul|hr|blockquote)/g, '<$1');
html = html.replace(/<\/(h[1-4]|pre|ul|hr|blockquote)><\/p>/g, '$1>');
html = html.replace(/
<\/p>/g, '');
html = html.replace(/
\s*<\/p>/g, '');
return html;
}
function escapeHtml(str) {
return str.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"');
}
// ─── Stat Pips ───
function buildPips(val, max) {
max = max || 5;
let html = '
';
for (let i = 0; i < max; i++) {
let cls = 'pip';
if (i < val) {
cls += ' filled';
if (val <= 2) cls += ' danger';
else if (val <= 3) cls += ' warn';
}
html += '
';
}
html += '
';
return html;
}
// ─── Clock ───
function initClock() {
const el = document.getElementById('navClock');
if (!el) return;
const tick = () => {
const d = new Date();
el.textContent = d.toLocaleTimeString('en-GB', { hour12: false }) + ' UTC' + (d.getTimezoneOffset() <= 0 ? '+' : '') + (-d.getTimezoneOffset() / 60);
};
tick();
setInterval(tick, 1000);
}
// ─── Navbar ───
function initNavbar() {
const toggle = document.getElementById('navToggle');
const menu = document.getElementById('navMenu');
if (toggle && menu) {
toggle.addEventListener('click', () => menu.classList.toggle('active'));
}
window.addEventListener('scroll', () => {
const nb = document.getElementById('navbar');
if (nb) nb.classList.toggle('scrolled', window.scrollY > 50);
}, { passive: true });
}
// ─── Mood Icons Map ───
const moodIcons = {
'focused': '◎', 'creative': '✦', 'productive': '⚡',
'tired': '◡', 'wired': '⚡', 'chaotic': '✸',
'locked-in': '◉', 'zen': '☯'
};
const moodColors = {
'focused': '#00ffc8', 'creative': '#a855f7', 'productive': '#22d3ee',
'tired': '#6b7280', 'wired': '#f59e0b', 'chaotic': '#ff4757',
'locked-in': '#00ff88', 'zen': '#818cf8'
};
// ─── Render Operator Stats ───
function renderStats(post) {
const el = document.getElementById('postStats');
if (!el) return;
const moodStr = (post.mood || 'focused').toLowerCase();
const moodIcon = moodIcons[moodStr] || '◈';
const moodColor = moodColors[moodStr] || '#00ffc8';
const hr = post.heart_rate || post.bpm || 72;
const coffee = '☕'.repeat(Math.min(post.coffee || 0, 10)) +
'' + '☕'.repeat(Math.max(0, 5 - (post.coffee || 0))) + '';
el.innerHTML = `
MOOD
${moodIcon} ${moodStr.toUpperCase()}
ENERGY
${buildPips(post.energy)}
MOTIVATION
${buildPips(post.motivation)}
FOCUS
${buildPips(post.focus)}
DIFFICULTY
${buildPips(post.difficulty)}
CAFFEINE
${coffee}
`;
// Animate heartbeat
animateHeartbeat(hr);
}
// ─── Heartbeat Animation ───
function animateHeartbeat(bpm) {
const el = document.getElementById('bpmValue');
if (!el) return;
const interval = 60000 / bpm;
setInterval(() => {
el.classList.add('pulse');
setTimeout(() => el.classList.remove('pulse'), 200);
// Flicker the value slightly
const jitter = Math.floor(Math.random() * 5) - 2;
el.textContent = bpm + jitter;
}, interval);
}
// ─── Render Metadata Panel ───
function renderMetaPanel(post) {
const el = document.getElementById('postMetaPanel');
if (!el) return;
el.innerHTML = `
DATE
${post.date}
TIME
${post.time_written || 'UNKNOWN'}
WORDS
${post.word_count || '---'}
THREAT
${post.threat_level || 'LOW'}
READ TIME
~${Math.max(1, Math.ceil((post.word_count || 300) / 250))} MIN
SLUG
${post.slug}
`;
}
// ─── Render Navigation ───
function renderNav(allPosts, currentSlug) {
const el = document.getElementById('postNav');
if (!el) return;
// Sort by date descending
const sorted = [...allPosts].sort((a, b) => new Date(b.date) - new Date(a.date));
const idx = sorted.findIndex(p => p.slug === currentSlug);
let navHtml = '';
if (idx > 0) {
const prev = sorted[idx - 1];
navHtml += `
← NEWER
${prev.title}
`;
}
if (idx < sorted.length - 1) {
const next = sorted[idx + 1];
navHtml += `
OLDER →
${next.title}
`;
}
navHtml += `
⟐ ALL TRANSMISSIONS
`;
el.innerHTML = navHtml;
}
// ─── Load Post ───
async function loadPost() {
// Support both /blog/post/slug and ?slug=x formats
let slug = null;
const pathMatch = window.location.pathname.match(/\/blog\/post\/([^\/]+)/);
if (pathMatch) {
slug = decodeURIComponent(pathMatch[1]);
} else {
const params = new URLSearchParams(window.location.search);
slug = params.get('slug');
}
if (!slug) {
document.getElementById('postContent').innerHTML =
'NO TRANSMISSION ID SPECIFIED
';
return;
}
try {
// Fetch the specific post
const res = await fetch(API + '/posts/' + encodeURIComponent(slug));
if (!res.ok) throw new Error('Post not found');
const post = await res.json();
// Update page title
document.title = 'JAESWIFT // ' + post.title.toUpperCase();
// Update header
document.getElementById('postTitle').textContent = post.title;
document.getElementById('postDate').textContent = post.date;
document.getElementById('postTime').textContent = post.time_written || '';
const threatEl = document.getElementById('postThreat');
threatEl.textContent = post.threat_level || 'LOW';
threatEl.className = 'post-threat threat-' + (post.threat_level || 'LOW').toLowerCase();
// Tags
const tagsEl = document.getElementById('postTags');
tagsEl.innerHTML = (post.tags || []).map(t =>
'' + t + ''
).join('');
// Render content
const contentEl = document.getElementById('postContent');
contentEl.innerHTML = '' + parseMarkdown(post.content || '') + '
';
// Animate content in
contentEl.style.opacity = '0';
contentEl.style.transform = 'translateY(20px)';
contentEl.style.transition = 'all 0.6s ease';
requestAnimationFrame(() => {
contentEl.style.opacity = '1';
contentEl.style.transform = 'translateY(0)';
});
// Sidebar panels
renderStats(post);
renderMetaPanel(post);
// Load all posts for navigation
try {
const allRes = await fetch(API + '/posts');
const allPosts = await allRes.json();
renderNav(allPosts, slug);
} catch (e) {
console.warn('Could not load post navigation');
}
} catch (err) {
// Fallback: try static JSON
try {
const res2 = await fetch('api/data/posts.json');
const posts = await res2.json();
const post = posts.find(p => p.slug === slug);
if (!post) throw new Error('Not found');
document.title = 'JAESWIFT // ' + post.title.toUpperCase();
document.getElementById('postTitle').textContent = post.title;
document.getElementById('postDate').textContent = post.date;
document.getElementById('postTime').textContent = post.time_written || '';
document.getElementById('postContent').innerHTML =
'' + parseMarkdown(post.content || '') + '
';
renderStats(post);
renderMetaPanel(post);
renderNav(posts, slug);
} catch (e2) {
document.getElementById('postContent').innerHTML =
'TRANSMISSION NOT FOUND — SIGNAL LOST
';
}
}
}
// ─── Init ───
document.addEventListener('DOMContentLoaded', () => {
initClock();
initNavbar();
loadPost();
});
})();