Polish release notes and in-app help (#2070)

* feat(ui): improve changelog and help navigation

Make release notes easier to scan and reach from the app. Also keep the homepage marquee within the layout viewport so it cannot create horizontal scrolling.

* fix(website): reject duplicate release anchors
This commit is contained in:
Mohamed Boudra
2026-07-14 21:06:25 +02:00
committed by GitHub
parent cd6c608c9e
commit d781b68711
14 changed files with 310 additions and 110 deletions
+83 -3
View File
@@ -4,6 +4,14 @@ import changelogMarkdown from "../../../../CHANGELOG.md?raw";
import { SiteShell } from "~/components/site-shell";
import { pageMeta } from "~/meta";
interface ChangelogRelease {
version: string;
date: string;
markdown: string;
}
const releaseHeadingPattern = /^## (.+?) - (\d{4}-\d{2}-\d{2})$/;
export const Route = createFileRoute("/changelog")({
head: () =>
pageMeta(
@@ -14,12 +22,84 @@ export const Route = createFileRoute("/changelog")({
component: Changelog,
});
function formatDate(date: string): string {
const [year, month, day] = date.split("-").map(Number);
return new Intl.DateTimeFormat("en", {
month: "long",
day: "numeric",
year: "numeric",
timeZone: "UTC",
}).format(new Date(Date.UTC(year, month - 1, day)));
}
function parseChangelog(markdown: string): ChangelogRelease[] {
const releases: ChangelogRelease[] = [];
const versions = new Set<string>();
let currentRelease: ChangelogRelease | null = null;
for (const line of markdown.split("\n")) {
const heading = line.match(releaseHeadingPattern);
if (heading) {
const version = heading[1];
if (versions.has(version)) {
throw new Error(`Duplicate changelog version: ${version}`);
}
versions.add(version);
if (currentRelease) releases.push(currentRelease);
currentRelease = {
version,
date: heading[2],
markdown: "",
};
continue;
}
if (currentRelease) currentRelease.markdown += `${line}\n`;
}
if (currentRelease) releases.push(currentRelease);
return releases;
}
const changelogReleases = parseChangelog(changelogMarkdown);
function Release({ release }: { release: ChangelogRelease }) {
const anchor = `release-${release.version}`;
return (
<article className="changelog-release">
<time dateTime={release.date} className="changelog-release-date">
{formatDate(release.date)}
</time>
<div id={anchor} className="changelog-release-heading">
<a
href={`#${anchor}`}
className="changelog-heading-anchor"
aria-label={`Link to Paseo ${release.version}`}
>
<span aria-hidden="true">#</span>
</a>
<h2 className="changelog-release-title">
Paseo <span>{release.version}</span>
</h2>
</div>
<div className="changelog-release-notes">
<ReactMarkdown>{release.markdown}</ReactMarkdown>
</div>
</article>
);
}
function Changelog() {
return (
<SiteShell width="default">
<article className="changelog-markdown rounded-xl border border-border bg-card/40 p-6 md:p-8">
<ReactMarkdown>{changelogMarkdown}</ReactMarkdown>
</article>
<div className="max-w-2xl">
<h1 className="mb-12 text-3xl font-medium tracking-tight">Changelog</h1>
{changelogReleases.map((release) => (
<Release key={release.version} release={release} />
))}
</div>
</SiteShell>
);
}