changelog timeline page (0.52.0)

- new #/changelog route: a vivid vertical timeline of releases with version
  badges, titles, dates and highlights (14 entries, 0.30 → 0.51)
- communicates the product's continuous shipping cadence — trust + transparency
- scroll-reveal on each entry; linked from the footer Resources column
  (replaced the placeholder Roadmap link)
- verified live: 14 entries render, latest first
This commit is contained in:
artheru 2026-06-10 07:41:21 +08:00
parent c9ad6407c4
commit d9a62b4c3c
4 changed files with 60 additions and 5 deletions

View File

@ -4,7 +4,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="theme-color" content="#0e0a14" /> <meta name="theme-color" content="#0e0a14" />
<meta name="description" content="amerc is the agent mercenary tavern — hire, run, publish, and remotely deliver AI agents across software boundaries. No infrastructure, just skills." /> <meta name="description" content="amerc is the agent mercenary tavern — hire, run, publish, and remotely deliver AI agents across software boundaries. No infrastructure, just skills." />
<meta name="version" content="0.51.0-skel" /> <meta name="version" content="0.52.0-changelog" />
<link rel="icon" href="/favicon.svg" type="image/svg+xml" /> <link rel="icon" href="/favicon.svg" type="image/svg+xml" />
<link rel="icon" href="/app-192.png" type="image/png" sizes="192x192" /> <link rel="icon" href="/app-192.png" type="image/png" sizes="192x192" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" /> <link rel="apple-touch-icon" href="/apple-touch-icon.png" />

View File

@ -1,6 +1,6 @@
{ {
"name": "amerc-site", "name": "amerc-site",
"version": "0.51.0-skel", "version": "0.52.0-changelog",
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {

View File

@ -7,7 +7,7 @@ import Account from './Account.jsx';
import TavernGame from './TavernGame.jsx'; import TavernGame from './TavernGame.jsx';
import { api } from './api.js'; import { api } from './api.js';
const RELEASE = '0.51.0-skel'; const RELEASE = '0.52.0-changelog';
const NAV = [ const NAV = [
{ id: 'home', label: 'Tavern' }, { id: 'home', label: 'Tavern' },
@ -15,7 +15,7 @@ const NAV = [
{ id: 'mansion', label: 'Mansion' }, { id: 'mansion', label: 'Mansion' },
{ id: 'booth', label: 'My Booth' }, { id: 'booth', label: 'My Booth' },
]; ];
const VALID_ROUTES = ['home', 'agents', 'mansion', 'booth', 'account', 'admin', 'signin', 'status']; const VALID_ROUTES = ['home', 'agents', 'mansion', 'booth', 'account', 'admin', 'signin', 'status', 'changelog'];
function useHashRoute() { function useHashRoute() {
const normalize = () => { const normalize = () => {
@ -273,7 +273,7 @@ function Footer({ setRoute }) {
</div> </div>
<div className="ft-col"><h5>Resources</h5> <div className="ft-col"><h5>Resources</h5>
<a href="https://docs.amerc.ai/">Docs</a> <a href="https://docs.amerc.ai/">Docs</a>
<a href="https://docs.amerc.ai/">Roadmap</a> <button onClick={() => setRoute('changelog')}>Changelog</button>
</div> </div>
<div className="ft-col"><h5>Platform</h5> <div className="ft-col"><h5>Platform</h5>
<a href="https://git.amerc.ai/">Git</a> <a href="https://git.amerc.ai/">Git</a>
@ -383,6 +383,42 @@ function StatusPage() {
); );
} }
const CHANGELOG = [
{ v: '0.51', date: 'Jun 2026', title: 'Loading skeletons', notes: ['Browse shows shimmer skeletons while the roster loads', 'Fixed an empty-state flash on cold load'] },
{ v: '0.50', date: 'Jun 2026', title: 'Sharper hero', notes: ['Clear value proposition and Browse / Publish calls-to-action on the home hero'] },
{ v: '0.49', date: 'Jun 2026', title: 'Live session preview', notes: ['An animated chatbox on the home page shows what a hired agent does'] },
{ v: '0.48', date: 'Jun 2026', title: 'Findable', notes: ['robots.txt, sitemap.xml and Organization / WebSite structured data'] },
{ v: '0.47', date: 'Jun 2026', title: 'Architecture flow', notes: ['Animated You → amerc edge → your users diagram on the home page'] },
{ v: '0.46', date: 'Jun 2026', title: 'System status', notes: ['A live status page probing the tavern, API, docs, git and webagent'] },
{ v: '0.45', date: 'Jun 2026', title: 'Motion', notes: ['Scroll-reveal feature cards and count-up statistics'] },
{ v: '0.44', date: 'Jun 2026', title: 'Roster storefront', notes: ['Browse became a storefront with category chips and a publish-your-own card'] },
{ v: '0.43', date: 'Jun 2026', title: 'Lean + onboarding', notes: ['Dropped 68 MB of dead assets and moved to atomic deploys', 'First-run onboarding in My Booth'] },
{ v: '0.42', date: 'Jun 2026', title: 'The Gatehouse', notes: ['A split join page that leads with the value proposition'] },
{ v: '0.41', date: 'Jun 2026', title: 'The Mansion', notes: ['A vivid services hall with the Showcase reverse proxy front and centre'] },
{ v: '0.40', date: 'Jun 2026', title: 'Installable', notes: ['amerc is a PWA — branded app icons, offline shell, add to home screen'] },
{ v: '0.39', date: 'Jun 2026', title: 'Public docs', notes: ['docs.amerc.ai opened to everyone with quickstart and reference guides'] },
{ v: '0.30', date: 'Jun 2026', title: 'The tavern opens', notes: ['The 2D RPG tavern, the agent platform, the Showcase reverse proxy, and single sign-on across every *.amerc.ai'] },
];
function ChangelogPage() {
return (
<div className="cl">
<h2 className="cl-title">Changelog</h2>
<p className="cl-lead">amerc ships continuously one focused improvement at a time. Here's the trail.</p>
<div className="cl-timeline">
{CHANGELOG.map((r, i) => (
<Reveal key={r.v} className="cl-item" delay={Math.min(i, 7) * 55}>
<span className="cl-dot" aria-hidden="true" />
<div className="cl-card">
<div className="cl-card-head"><span className="cl-ver">v{r.v}</span><strong>{r.title}</strong><span className="cl-date">{r.date}</span></div>
<ul>{r.notes.map((n, j) => <li key={j}>{n}</li>)}</ul>
</div>
</Reveal>
))}
</div>
</div>
);
}
function Page({ children, setRoute }) { return <div className="td-page"><div className="td-page-inner">{children}</div><Footer setRoute={setRoute} /></div>; } function Page({ children, setRoute }) { return <div className="td-page"><div className="td-page-inner">{children}</div><Footer setRoute={setRoute} /></div>; }
function Scene({ route, setRoute, auth }) { function Scene({ route, setRoute, auth }) {
@ -392,6 +428,7 @@ function Scene({ route, setRoute, auth }) {
if (route === 'booth') return <Page setRoute={setRoute}><BoothDashboard auth={auth} setRoute={setRoute} /></Page>; if (route === 'booth') return <Page setRoute={setRoute}><BoothDashboard auth={auth} setRoute={setRoute} /></Page>;
if (route === 'account') return <Page setRoute={setRoute}><Account auth={auth} setRoute={setRoute} /></Page>; if (route === 'account') return <Page setRoute={setRoute}><Account auth={auth} setRoute={setRoute} /></Page>;
if (route === 'status') return <Page setRoute={setRoute}><StatusPage /></Page>; if (route === 'status') return <Page setRoute={setRoute}><StatusPage /></Page>;
if (route === 'changelog') return <Page setRoute={setRoute}><ChangelogPage /></Page>;
if (route === 'signin') return <Page setRoute={setRoute}><Gatehouse auth={auth} setRoute={setRoute} /></Page>; if (route === 'signin') return <Page setRoute={setRoute}><Gatehouse auth={auth} setRoute={setRoute} /></Page>;
if (route === 'admin') return <Page setRoute={setRoute}><div className="px-board" style={{ position: 'static', transform: 'none', width: 'min(720px,94vw)', margin: '0 auto' }}> if (route === 'admin') return <Page setRoute={setRoute}><div className="px-board" style={{ position: 'static', transform: 'none', width: 'min(720px,94vw)', margin: '0 auto' }}>
<header className="px-board-head"><span className="px-board-tag">BACKDOOR</span><h2>Quartermaster</h2><p>Tenant, key, session and risk controls.</p></header> <header className="px-board-head"><span className="px-board-tag">BACKDOOR</span><h2>Quartermaster</h2><p>Tenant, key, session and risk controls.</p></header>

View File

@ -1369,3 +1369,21 @@ button {
.ap-skel-foot i { height: 9px; width: 30%; } .ap-skel-foot i { height: 9px; width: 30%; }
@keyframes sk-shimmer { 0% { background-position: 100% 0; } 100% { background-position: 0 0; } } @keyframes sk-shimmer { 0% { background-position: 100% 0; } 100% { background-position: 0 0; } }
@media (prefers-reduced-motion: reduce) { .sk-av, .sk-tt i, .sk-p, .ap-skel-foot i { animation: none; } } @media (prefers-reduced-motion: reduce) { .sk-av, .sk-tt i, .sk-p, .ap-skel-foot i { animation: none; } }
/* changelog timeline (0.52) */
.cl { max-width: 720px; margin: 0 auto; }
.cl-title { font-family: Georgia, "Times New Roman", serif; font-size: 26px; color: #fff; margin: 0 0 6px; }
.cl-lead { color: #b6a7d4; font-size: 14px; margin: 0 0 26px; line-height: 1.6; }
.cl-timeline { position: relative; padding-left: 26px; }
.cl-timeline::before { content: ''; position: absolute; left: 5px; top: 8px; bottom: 8px; width: 2px; background: linear-gradient(180deg, #46c8e0, #46c8e0 30%, #2e2440); border-radius: 2px; }
.cl-item { position: relative; margin-bottom: 16px; }
.cl-dot { position: absolute; left: -26px; top: 17px; width: 12px; height: 12px; border-radius: 50%; background: #14101e; border: 2px solid #46c8e0; box-shadow: 0 0 8px rgba(70,200,224,0.5); }
.cl-item:first-child .cl-dot { background: #46c8e0; }
.cl-card { background: linear-gradient(180deg, #160f24, #120c1d); border: 1px solid #2a1f3e; border-radius: 12px; padding: 14px 16px; transition: border-color .15s, transform .15s; }
.cl-card:hover { border-color: #46c8e0; transform: translateX(2px); }
.cl-card-head { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; flex-wrap: wrap; }
.cl-ver { font-family: ui-monospace, monospace; font-size: 11px; font-weight: 700; color: #06222f; background: linear-gradient(180deg, #5fe6ff, #27d8ff); padding: 2px 8px; border-radius: 999px; }
.cl-card-head strong { color: #fff; font-size: 15px; }
.cl-date { margin-left: auto; color: #7a6a98; font-size: 12px; }
.cl-card ul { margin: 0; padding-left: 18px; display: flex; flex-direction: column; gap: 5px; }
.cl-card li { color: #b6a7d4; font-size: 13px; line-height: 1.5; }