import { useEffect } from "react";
import {
commitUrl,
compareUrl,
KIND_LABEL,
specUrl,
type Entry,
type Note,
} from "../../shared/changelog.ts";
import { asOfLabel } from "../../shared/routines.ts";
import { REPO_URL } from "./Colophon.tsx";
const link =
"underline decoration-hairline underline-offset-2 transition-colors hover:text-ink";
/**
* The fuller record (PRD F18): release notes, each a few paragraphs from the
* history and the specs, with the commit range and the specs it draws on, and
* the short entries it covers listed under it. Entries that belong to no note
* follow, by month.
*
* The detail sheet's shape — the same scrim, the same Escape — because it is
* the same kind of thing: something opened to read, then closed.
*/
export function ChangelogSheet({
notes,
entries,
onClose,
}: {
notes: readonly Note[];
entries: readonly Entry[];
onClose: () => void;
}) {
useEffect(() => {
const onKey = (e: KeyboardEvent) => {
if (e.key === "Escape") onClose();
};
window.addEventListener("keydown", onKey);
return () => window.removeEventListener("keydown", onKey);
}, [onClose]);
return (
);
}
export function ChangelogContent({ notes, entries }: { notes: readonly Note[]; entries: readonly Entry[] }) {
const loose = entries.filter((e) => e.note === undefined);
const months = [...new Set(loose.map((e) => e.date.slice(0, 7)))];
return (<>
Changelog
What changed, summarised from the project's history and design notes. Each links to the
commits it covers.
{notes.map((note) => {
const covered = entries.filter((e) => e.note === note.id);
return (
{asOfLabel(note.date)}
{note.title}
{note.summary.map((paragraph, i) => (
{paragraph}
))}
{covered.length > 0 && (
{covered.map((entry) => (
))}
)}
commits {note.from}…{note.to}
{(note.specs ?? []).map((path) => (
{path.split("/").at(-1)}
))}
);
})}
{months.length > 0 && (
Other changes
{months.map((month) => (
{asOfLabel(`${month}-01`).replace(/^1 /, "")}
{loose
.filter((e) => e.date.startsWith(month))
.map((entry) => (
))}
))}
)}
>);
}
function EntryLine({ entry }: { entry: Entry }) {
return (
{KIND_LABEL[entry.kind]}
{entry.title}
{entry.ref}
);
}