Design: D4RKST3R-Brand statt EcoGame — generisches Devlog-Archiv

- Design-System der d4rkst3r-platform übernommen: Neon-Gelb/Orange auf Schwarz,
  Bebas Neue + Barlow Condensed + Share Tech Mono (selbst gehostet)
- Noise- + Scanline-Overlay, aufsteigende Partikel, Grid-PageHeader mit Ghost-Text
- EcoGame-Branding entfernt — Seite ist jetzt projekt-neutral nutzbar
- Nav mit D4RKST3R-Wortmarke (3R in Orange), Mono-Navigation, Login als Neon-Outline
- Im Browser verifiziert (Devlogs + Commits)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-23 00:45:23 +02:00
co-authored by Claude Opus 4.8
parent d9cc24f4a8
commit 4b7a93ddb1
8 changed files with 458 additions and 386 deletions
+72 -42
View File
@@ -17,60 +17,90 @@ export default function Commits({ me }) {
window.scrollTo(0, 0);
}, [page, me.admin]);
const header = (
<section className="page-header">
<div className="page-bg-text">COMMITS</div>
<div className="page-header-grid" aria-hidden="true" />
<div className="page-header-content">
<div className="page-tag">// Privat</div>
<h1 className="page-title">Commits</h1>
{data && <p className="page-subtitle">{data.total} archivierte Commits über alle Repos.</p>}
</div>
</section>
);
// Zugriffs-Zustände: nicht eingeloggt / eingeloggt aber kein Admin
if (me.loading) return <p className="notice">Lade </p>;
if (me.loading) return <>{header}<section className="content"><p className="notice">Lade </p></section></>;
if (!me.user) {
return (
<div className="notice center">
<p>🔒 Der Commit-Feed ist privat.</p>
<a className="btn btn-discord" href="/auth/login">Login mit Discord</a>
</div>
<>
{header}
<section className="content">
<div className="notice center">
<p>Der Commit-Feed ist privat.</p>
<a className="btn" href="/auth/login">Login mit Discord</a>
</div>
</section>
</>
);
}
if (!me.admin) {
return (
<>
{header}
<section className="content">
<p className="notice center">Kein Zugriff der Commit-Feed ist privat.</p>
</section>
</>
);
}
if (!me.admin) return <p className="notice center">🔒 Kein Zugriff der Commit-Feed ist privat.</p>;
if (error) return <p className="notice">Commits konnten nicht geladen werden ({error}).</p>;
if (!data) return <p className="notice">Lade </p>;
if (data.total === 0) return <p className="notice">Noch keine Commits archiviert.</p>;
const pages = Math.ceil(data.total / data.pageSize);
const pages = data ? Math.ceil(data.total / data.pageSize) : 0;
return (
<>
<section className="hero">
<h1>Commits</h1>
<p>{data.total} archivierte Commits über alle Repos</p>
</section>
{header}
<section className="content">
{error && <p className="notice">Commits konnten nicht geladen werden ({error}).</p>}
{!error && !data && <p className="notice">Lade </p>}
{data?.total === 0 && <p className="notice">Noch keine Commits archiviert.</p>}
<div className="commit-list">
{data.items.map((c) => (
<div className="commit-row" key={c.sha}>
{c.url ? (
<a className="sha" href={c.url} target="_blank" rel="noreferrer">
{c.sha.slice(0, 7)}
</a>
) : (
<span className="sha">{c.sha.slice(0, 7)}</span>
)}
<span className="commit-msg">{c.message.split('\n')[0]}</span>
<span className="chip chip-repo">{c.repo.split('/').pop()}</span>
<span className="chip chip-branch">{c.branch}</span>
<span className="commit-time">{dateFmt.format(new Date(c.committed_at))}</span>
{data?.total > 0 && (
<div className="commit-list">
{data.items.map((c) => (
<div className="commit-row" key={c.sha}>
{c.url ? (
<a className="sha" href={c.url} target="_blank" rel="noreferrer">
{c.sha.slice(0, 7)}
</a>
) : (
<span className="sha">{c.sha.slice(0, 7)}</span>
)}
<span className="commit-msg">{c.message.split('\n')[0]}</span>
<span className="chip chip-repo">{c.repo.split('/').pop()}</span>
<span className="chip chip-branch">{c.branch}</span>
<span className="commit-time">
{dateFmt.format(new Date(c.committed_at))}
</span>
</div>
))}
</div>
))}
</div>
)}
{pages > 1 && (
<div className="pager">
<button className="btn" disabled={page <= 1} onClick={() => setPage(page - 1)}>
Neuere
</button>
<span>Seite {page} / {pages}</span>
<button className="btn" disabled={page >= pages} onClick={() => setPage(page + 1)}>
Ältere
</button>
</div>
)}
{pages > 1 && (
<div className="pager">
<button className="btn" disabled={page <= 1} onClick={() => setPage(page - 1)}>
Neuere
</button>
<span className="pager-info">
{String(page).padStart(2, '0')} / {String(pages).padStart(2, '0')}
</span>
<button className="btn" disabled={page >= pages} onClick={() => setPage(page + 1)}>
Ältere
</button>
</div>
)}
</section>
</>
);
}
+46 -38
View File
@@ -3,7 +3,7 @@ import { apiGet } from '../api.js';
import { Markdown } from '../markdown.jsx';
const dateFmt = new Intl.DateTimeFormat('de-DE', {
weekday: 'long', day: '2-digit', month: 'long', year: 'numeric',
weekday: 'short', day: '2-digit', month: '2-digit', year: 'numeric',
});
/** Relative Angabe wie „vor 3 Tagen" für die Karten-Ecke */
@@ -31,47 +31,55 @@ export default function Devlogs() {
return (
<>
<section className="hero">
<h1>Devlog</h1>
<p>
Die Entwicklung von <strong>EcoGame</strong> einem Voxel-Sandbox-Survival
in Unreal Engine 5. Frisch aus dem Editor, Tag für Tag.
</p>
<section className="page-header">
<div className="page-bg-text">DEVLOG</div>
<div className="page-header-grid" aria-hidden="true" />
<div className="page-header-content">
<div className="page-tag">// Archiv</div>
<h1 className="page-title">Devlog</h1>
<p className="page-subtitle">
Entwicklungs-Updates, frisch aus dem Editor automatisch archiviert.
</p>
</div>
</section>
{error && <p className="notice">Devlogs konnten nicht geladen werden.</p>}
{!error && !data && <p className="notice">Lade </p>}
{data?.total === 0 && <p className="notice">Noch keine Devlogs archiviert.</p>}
<section className="content">
{error && <p className="notice">Devlogs konnten nicht geladen werden.</p>}
{!error && !data && <p className="notice">Lade </p>}
{data?.total === 0 && <p className="notice">Noch keine Devlogs archiviert.</p>}
{data?.total > 0 && (
<div className="timeline">
{data.items.map((d) => (
<article className="card" key={d.message_id}>
<div className="card-meta">
<span className="card-date">
{dateFmt.format(new Date(d.posted_at))}
</span>
<span className="card-rel">{relative(d.posted_at)}</span>
</div>
<div className="card-body">
<Markdown text={d.content} />
</div>
</article>
))}
</div>
)}
{data?.total > 0 && (
<div className="timeline">
{data.items.map((d) => (
<article className="card" key={d.message_id}>
<div className="card-meta">
<span className="card-date">
{dateFmt.format(new Date(d.posted_at))}
</span>
<span className="card-rel">{relative(d.posted_at)}</span>
</div>
<div className="card-body">
<Markdown text={d.content} />
</div>
</article>
))}
</div>
)}
{pages > 1 && (
<div className="pager">
<button className="btn" disabled={page <= 1} onClick={() => setPage(page - 1)}>
Neuere
</button>
<span>Seite {page} / {pages}</span>
<button className="btn" disabled={page >= pages} onClick={() => setPage(page + 1)}>
Ältere
</button>
</div>
)}
{pages > 1 && (
<div className="pager">
<button className="btn" disabled={page <= 1} onClick={() => setPage(page - 1)}>
Neuere
</button>
<span className="pager-info">
{String(page).padStart(2, '0')} / {String(pages).padStart(2, '0')}
</span>
<button className="btn" disabled={page >= pages} onClick={() => setPage(page + 1)}>
Ältere
</button>
</div>
)}
</section>
</>
);
}