mirror of
https://github.com/tale/headplane.git
synced 2026-10-27 06:57:53 +00:00
111 lines
3.9 KiB
TypeScript
111 lines
3.9 KiB
TypeScript
import { TOCProvider, TOCScrollArea } from "fumadocs-ui/components/toc";
|
|
import { TOCItem, TOCItems } from "fumadocs-ui/components/toc/default";
|
|
import { CircleDot, GitPullRequest, History } from "lucide-react";
|
|
import type { ReactNode } from "react";
|
|
|
|
import { releaseId, releaseLabel } from "@/lib/changelog";
|
|
|
|
// Rendered by remark-changelog from CHANGELOG.md.
|
|
|
|
// Clears the sticky header (and the beta banner, when shown).
|
|
const stickyTop = "top-[calc(var(--fd-banner-height,0px)+5rem)]";
|
|
|
|
export function ReleaseList({ versions, children }: { versions: string; children: ReactNode }) {
|
|
const toc = (JSON.parse(versions) as string[]).map((version) => ({
|
|
title: releaseLabel(version),
|
|
url: `#${releaseId(version)}`,
|
|
depth: 2,
|
|
}));
|
|
|
|
return (
|
|
<TOCProvider toc={toc}>
|
|
<div className="xl:grid xl:grid-cols-[minmax(0,1fr)_10rem] xl:gap-x-12">
|
|
<div className="min-w-0">{children}</div>
|
|
<aside
|
|
className={`sticky ${stickyTop} hidden max-h-[calc(100vh-7rem)] flex-col gap-y-3 self-start xl:flex`}
|
|
>
|
|
<p className="text-muted inline-flex items-center gap-1.5 text-sm">
|
|
<History className="size-4" />
|
|
Versions
|
|
</p>
|
|
<TOCScrollArea className="ms-px">
|
|
<TOCItems>
|
|
{toc.map((item) => (
|
|
<TOCItem item={item} key={item.url} />
|
|
))}
|
|
</TOCItems>
|
|
</TOCScrollArea>
|
|
</aside>
|
|
</div>
|
|
</TOCProvider>
|
|
);
|
|
}
|
|
|
|
interface ReleaseProps {
|
|
version: string;
|
|
date?: string;
|
|
children: ReactNode;
|
|
}
|
|
|
|
export function Release({ version, date, children }: ReleaseProps) {
|
|
const isPrerelease = version.includes("-");
|
|
const id = releaseId(version);
|
|
|
|
return (
|
|
<section
|
|
className="border-line grid scroll-mt-[calc(var(--fd-banner-height,0px)+5rem)] gap-x-10 gap-y-4 border-t py-10 first:border-t-0 first:pt-0 md:grid-cols-[10rem_minmax(0,1fr)]"
|
|
id={id}
|
|
>
|
|
<header className="flex flex-wrap items-baseline gap-x-2 gap-y-1 self-start md:sticky md:top-[calc(var(--fd-banner-height,0px)+5rem)] md:flex-col">
|
|
<a className="text-fg text-lg font-semibold tracking-tight" href={`#${id}`}>
|
|
{releaseLabel(version)}
|
|
</a>
|
|
{date && <time className="text-muted text-sm">{date}</time>}
|
|
{isPrerelease && (
|
|
<span className="bg-subtle text-muted border-line inline-flex h-[18px] items-center rounded-sm border px-1.5 text-[11px] font-medium">
|
|
Beta
|
|
</span>
|
|
)}
|
|
</header>
|
|
<div className="prose release-notes min-w-0">{children}</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
const sectionStyles: Record<string, string> = {
|
|
Upgrading: "bg-warn-subtle text-warn border-warn/30",
|
|
Changes: "bg-accent-subtle text-accent border-accent/30",
|
|
Fixes: "bg-ok-subtle text-ok border-ok/30",
|
|
};
|
|
|
|
export function ReleaseSection({ title, children }: { title: string; children: ReactNode }) {
|
|
return (
|
|
<div className="release-section">
|
|
<div className="not-prose">
|
|
<h3
|
|
className={`inline-flex h-6 items-center rounded-md border px-2 text-xs font-semibold ${sectionStyles[title] ?? "bg-subtle text-muted border-line"}`}
|
|
>
|
|
{title}
|
|
</h3>
|
|
</div>
|
|
{children}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function Reference({ kind, number }: { kind: "issues" | "pull"; number: string }) {
|
|
const Icon = kind === "pull" ? GitPullRequest : CircleDot;
|
|
|
|
return (
|
|
<span className="not-prose">
|
|
<a
|
|
className="bg-accent-subtle text-accent border-accent/25 hover:border-accent/60 ms-1.5 inline-flex h-5 -translate-y-px items-center gap-1 rounded-md border px-1.5 align-middle text-xs font-medium tabular-nums transition-colors [&_svg]:size-3"
|
|
href={`https://github.com/tale/headplane/${kind}/${number}`}
|
|
title={`${kind === "pull" ? "Pull request" : "Issue"} #${number}`}
|
|
>
|
|
<Icon />#{number}
|
|
</a>
|
|
</span>
|
|
);
|
|
}
|