diff --git a/api/app.py b/api/app.py index 398a151..607bce7 100644 --- a/api/app.py +++ b/api/app.py @@ -896,5 +896,16 @@ def contraband_search(): # ─── Run ───────────────────────────────────────────── + + +# ─── Changelog ──────────────────────────────────────── +@app.route('/api/changelog') +def get_changelog(): + try: + with open(os.path.join(DATA_DIR, 'changelog.json'), 'r') as f: + return json.load(f) + except Exception as e: + return {"error": str(e)}, 500 + if __name__ == '__main__': app.run(host='0.0.0.0', port=5000, debug=False) diff --git a/api/data/changelog.json b/api/data/changelog.json new file mode 100644 index 0000000..5a07721 --- /dev/null +++ b/api/data/changelog.json @@ -0,0 +1,134 @@ +{ + "site": "jaeswift.xyz", + "entries": [ + { + "version": "1.8.0", + "date": "2026-04-03", + "title": "CONTRABAND Auto-Sync & Grid Overhaul", + "category": "feature", + "changes": [ + "Subcategories now display as 2-column card grid with expandable detail panels", + "Added weekly auto-sync — resource database updates every Sunday at 03:00", + "Click any subcategory card to expand/collapse its entries below", + "Active card highlighting with amber glow", + "Responsive grid: 2-col desktop, 1-col mobile" + ] + }, + { + "version": "1.7.0", + "date": "2026-04-03", + "title": "Sitewide Visual Overhaul", + "category": "fix", + "changes": [ + "Bumped 64 font sizes sitewide — no more microscopic text", + "Brightened all text colours: primary #c0c0c0→#d8d8d8, secondary #707070→#999999, muted #3a3a3a→#666666", + "CONTRABAND page: 4-column category grid with responsive breakpoints", + "Purged all third-party attribution references from entire codebase" + ] + }, + { + "version": "1.6.0", + "date": "2026-04-03", + "title": "CONTRABAND — Classified Resource Index", + "category": "feature", + "changes": [ + "Launched CONTRABAND page at /depot/contraband with 15,800+ indexed assets", + "24 categories with military codenames (CRT-001 through CRT-024)", + "Full-text search across all entries via API", + "Starred/top-pick filter system with ⭐ indicators", + "Collapsible subcategories with item counts", + "Flask API endpoints: /api/contraband, /api/contraband/, /api/contraband/search" + ] + }, + { + "version": "1.5.1", + "date": "2026-04-03", + "title": "Navbar Dropdown Fix", + "category": "fix", + "changes": [ + "Fixed dropdown menus disappearing on all subpages", + "Root cause: 25 subpages used class 'navbar' instead of 'nav-main'", + "All pages now use correct nav class with proper positioning and z-index" + ] + }, + { + "version": "1.5.0", + "date": "2026-04-02", + "title": "Globe & Chat AI Admin Panels", + "category": "feature", + "changes": [ + "Admin panel: Globe management section — server location, rotation speed, arc cities, colours", + "Admin panel: Chat AI configuration — model selection, system prompt, greeting toggle", + "New API endpoints: /api/globe, /api/chat-config with auth-protected GET/POST", + "Interactive colour picker and slider controls for globe parameters", + "Arc cities table with add/remove functionality" + ] + }, + { + "version": "1.4.0", + "date": "2026-04-01", + "title": "Interactive 3D Globe", + "category": "feature", + "changes": [ + "Added interactive 3D globe to homepage using globe.gl", + "Animated arcs connecting server location to cities worldwide", + "Hex polygon layer with customisable opacity and colour", + "Atmosphere glow effect with configurable altitude", + "Auto-rotation with adjustable speed" + ] + }, + { + "version": "1.3.0", + "date": "2026-03-31", + "title": "Blog & Transmissions System", + "category": "feature", + "changes": [ + "Built blog system with markdown-to-HTML rendering", + "Blog index page with post cards, dates, and categories", + "Individual post pages with full content rendering", + "Transmissions section: SITREP, RADAR, DISPATCHES pages", + "Admin panel: blog post management with create/edit/delete" + ] + }, + { + "version": "1.2.0", + "date": "2026-03-30", + "title": "Admin Panel & Authentication", + "category": "feature", + "changes": [ + "Built admin panel at /admin with session-based authentication", + "Dashboard with real-time server stats and process monitoring", + "Navigation management: add, edit, reorder, delete menu items", + "Blog post editor with live preview", + "Password-protected API endpoints with cookie auth" + ] + }, + { + "version": "1.1.0", + "date": "2026-03-29", + "title": "HQ & Section Pages", + "category": "feature", + "changes": [ + "Created full site navigation structure with dropdown menus", + "HQ section: Telemetry, Logs, Profile, Briefing pages", + "DEPOT section: index, Propaganda, Recon, Exfil pages", + "ARMOURY section: Lab, Field Manuals, Deployments, Debrief pages", + "COMMS section: Open Channels, Encrypted Line, Backup Relay pages" + ] + }, + { + "version": "1.0.0", + "date": "2026-03-28", + "title": "Initial Launch", + "category": "release", + "changes": [ + "Launched jaeswift.xyz with sci-fi military dashboard theme", + "Homepage with system stats, process monitor, clock, and typing effect", + "Dark theme with scanline overlay and monospace typography", + "Flask API backend for real-time server data", + "Responsive design across all screen sizes", + "Gitea repository setup at git.jaeswift.xyz" + ] + } + ] +} \ No newline at end of file diff --git a/css/changelog.css b/css/changelog.css new file mode 100644 index 0000000..5f7bee4 --- /dev/null +++ b/css/changelog.css @@ -0,0 +1,229 @@ +/* ═══════════════════════════════════════════════════════ + CHANGELOG — Mission Update Log + ═══════════════════════════════════════════════════════ */ + +.changelog-container { + max-width: 900px; + margin: 0 auto; + padding: 2rem 1.5rem; +} + +.changelog-header { + text-align: center; + margin-bottom: 2.5rem; + padding-bottom: 1.5rem; + border-bottom: 1px solid var(--border); +} + +.changelog-title { + font-family: var(--font-display); + font-size: 1.8rem; + color: var(--text-primary); + letter-spacing: 3px; + margin-bottom: 0.5rem; +} + +.changelog-subtitle { + font-family: var(--font-mono); + font-size: 0.85rem; + color: var(--text-secondary); + letter-spacing: 1px; +} + +.changelog-stats { + display: flex; + justify-content: center; + gap: 2rem; + margin-top: 1rem; +} + +.changelog-stat { + font-family: var(--font-mono); + font-size: 0.8rem; + color: var(--text-muted); + letter-spacing: 1px; +} + +.changelog-stat-value { + color: var(--status-green); + font-weight: 600; +} + +/* ─── Timeline ───────────────────────────────────────── */ +.changelog-timeline { + position: relative; + padding-left: 2rem; +} + +.changelog-timeline::before { + content: ''; + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: 2px; + background: linear-gradient(to bottom, var(--status-green), var(--border), transparent); +} + +/* ─── Entry ──────────────────────────────────────────── */ +.changelog-entry { + position: relative; + margin-bottom: 2rem; + transition: all 0.3s ease; +} + +.changelog-entry::before { + content: ''; + position: absolute; + left: -2rem; + top: 0.9rem; + width: 10px; + height: 10px; + border-radius: 50%; + background: var(--bg-primary); + border: 2px solid var(--status-green); + z-index: 1; + transition: all 0.3s ease; +} + +.changelog-entry:hover::before { + background: var(--status-green); + box-shadow: 0 0 8px var(--status-green-glow); +} + +.changelog-entry-card { + background: var(--bg-panel); + border: 1px solid var(--border); + padding: 1.2rem 1.5rem; + transition: all 0.3s ease; +} + +.changelog-entry:hover .changelog-entry-card { + border-color: var(--status-green-dim); + background: var(--bg-panel-hover); +} + +/* ─── Entry Header ───────────────────────────────────── */ +.changelog-entry-header { + display: flex; + align-items: center; + gap: 1rem; + margin-bottom: 0.75rem; + flex-wrap: wrap; +} + +.changelog-version { + font-family: var(--font-display); + font-size: 1rem; + color: var(--status-green); + letter-spacing: 1px; + font-weight: 700; +} + +.changelog-badge { + font-family: var(--font-mono); + font-size: 0.7rem; + padding: 0.15rem 0.5rem; + letter-spacing: 1px; + text-transform: uppercase; + border: 1px solid; +} + +.changelog-badge.feature { + color: var(--status-green); + border-color: var(--status-green-dim); + background: rgba(0, 204, 51, 0.08); +} + +.changelog-badge.fix { + color: var(--warning); + border-color: var(--mil-red-dim); + background: rgba(201, 162, 39, 0.08); +} + +.changelog-badge.release { + color: var(--accent); + border-color: var(--accent-dim); + background: rgba(208, 208, 208, 0.08); +} + +.changelog-badge.security { + color: var(--danger); + border-color: rgba(255, 45, 45, 0.3); + background: rgba(255, 45, 45, 0.08); +} + +.changelog-date { + font-family: var(--font-mono); + font-size: 0.8rem; + color: var(--text-muted); + letter-spacing: 1px; + margin-left: auto; +} + +.changelog-entry-title { + font-family: var(--font-display); + font-size: 1.1rem; + color: var(--text-primary); + letter-spacing: 0.5px; + margin-bottom: 0.75rem; +} + +/* ─── Change List ────────────────────────────────────── */ +.changelog-changes { + list-style: none; + padding: 0; + margin: 0; +} + +.changelog-changes li { + position: relative; + padding: 0.35rem 0 0.35rem 1.5rem; + font-family: var(--font-mono); + font-size: 0.85rem; + color: var(--text-secondary); + line-height: 1.5; +} + +.changelog-changes li::before { + content: '▸'; + position: absolute; + left: 0; + color: var(--status-green); + font-size: 0.9rem; +} + +/* ─── Loading ────────────────────────────────────────── */ +.changelog-loading { + text-align: center; + padding: 4rem 0; + font-family: var(--font-mono); + font-size: 0.9rem; + color: var(--text-muted); + letter-spacing: 2px; +} + +/* ─── Responsive ─────────────────────────────────────── */ +@media (max-width: 768px) { + .changelog-container { + padding: 1.5rem 1rem; + } + .changelog-timeline { + padding-left: 1.5rem; + } + .changelog-entry::before { + left: -1.5rem; + } + .changelog-entry-header { + flex-direction: column; + align-items: flex-start; + gap: 0.5rem; + } + .changelog-date { + margin-left: 0; + } + .changelog-stats { + flex-direction: column; + gap: 0.5rem; + } +} diff --git a/hq/changelog.html b/hq/changelog.html new file mode 100644 index 0000000..98c9ccd --- /dev/null +++ b/hq/changelog.html @@ -0,0 +1,56 @@ + + + + + + JAESWIFT // CHANGELOG + + + + + + + +
+
+ + + + + +
+
HQ // HEADQUARTERS
+

CHANGELOG

+

> Complete mission update log. Every deployment, fix, and feature recorded.

+
+ +
+
+
LOADING UPDATE LOG...
+
+
+ + + + + + diff --git a/js/changelog.js b/js/changelog.js new file mode 100644 index 0000000..4ee0e67 --- /dev/null +++ b/js/changelog.js @@ -0,0 +1,86 @@ +/* ═══════════════════════════════════════════════════════ + CHANGELOG — Mission Update Log Controller + ═══════════════════════════════════════════════════════ */ +(function () { + 'use strict'; + + const root = document.getElementById('changelogRoot'); + if (!root) return; + + function esc(s) { + const d = document.createElement('div'); + d.textContent = s || ''; + return d.innerHTML; + } + + function formatDate(dateStr) { + const d = new Date(dateStr + 'T00:00:00'); + const months = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC']; + return `${d.getDate().toString().padStart(2, '0')} ${months[d.getMonth()]} ${d.getFullYear()}`; + } + + function render(data) { + const entries = data.entries || []; + const totalChanges = entries.reduce((sum, e) => sum + (e.changes ? e.changes.length : 0), 0); + const latestVersion = entries.length > 0 ? entries[0].version : '0.0.0'; + + let html = ''; + + // Header + html += `
`; + html += `
MISSION UPDATE LOG
`; + html += `
// SYSTEM CHANGELOG — ALL DEPLOYMENTS
`; + html += `
`; + html += `CURRENT BUILD: v${esc(latestVersion)}`; + html += `UPDATES: ${entries.length}`; + html += `TOTAL CHANGES: ${totalChanges}`; + html += `
`; + + // Timeline + html += `
`; + + for (const entry of entries) { + const badge = entry.category || 'update'; + html += `
`; + html += `
`; + + // Header row + html += `
`; + html += `v${esc(entry.version)}`; + html += `${esc(badge)}`; + html += `${formatDate(entry.date)}`; + html += `
`; + + // Title + html += `
${esc(entry.title)}
`; + + // Changes + if (entry.changes && entry.changes.length > 0) { + html += `
    `; + for (const change of entry.changes) { + html += `
  • ${esc(change)}
  • `; + } + html += `
`; + } + + html += `
`; + } + + html += `
`; + root.innerHTML = html; + } + + // Load data + root.innerHTML = '
LOADING UPDATE LOG...
'; + + fetch('/api/changelog') + .then(res => { + if (!res.ok) throw new Error('API ' + res.status); + return res.json(); + }) + .then(data => render(data)) + .catch(err => { + root.innerHTML = '
⚠ FAILED TO LOAD UPDATE LOG
'; + console.error('Changelog load error:', err); + }); +})();