import type { Metadata } from "next"; import { BasePage } from "@/app/base-page"; import { Separator } from "@/components/ui/separator"; import { cn } from "@/utils/ui"; import { allChangelogs } from "content-collections"; export const metadata: Metadata = { title: "Changelog - OpenCut", description: "Every update, improvement, and fix to OpenCut — documented.", openGraph: { title: "Changelog - OpenCut", description: "Every update, improvement, and fix to OpenCut — documented.", type: "website", }, }; const knownSectionOrder = ["new", "improved", "fixed", "breaking"]; const knownSectionTitles: Record = { new: "Features", improved: "Improvements", fixed: "Fixes", breaking: "Breaking Changes", }; function getSectionTitle(type: string): string { return ( knownSectionTitles[type] ?? type.charAt(0).toUpperCase() + type.slice(1) ); } export default function ChangelogPage() { const releases = [...allChangelogs].sort((a, b) => b.version.localeCompare(a.version, undefined, { numeric: true }), ); return (
{releases.map((release, releaseIndex) => (
{releaseIndex < releases.length - 1 && ( // ml-1.5 aligns with the center of the 11px timeline dot )}
))}
); } type Change = { type: string; text: string }; type Release = (typeof allChangelogs)[number]; function groupAndOrderChanges({ changes }: { changes: Change[] }) { const grouped = changes.reduce>((acc, change) => { if (!acc[change.type]) { acc[change.type] = []; } acc[change.type].push(change); return acc; }, {}); const customTypes = Object.keys(grouped).filter( (type) => !knownSectionOrder.includes(type), ); const orderedTypes = [ ...knownSectionOrder.filter((type) => grouped[type]?.length > 0), ...customTypes, ]; return { grouped, orderedTypes }; } function ReleaseEntry({ release }: { release: Release }) { const { grouped: groupedChanges, orderedTypes } = groupAndOrderChanges({ changes: release.changes, }); return (
{release.version} - {release.date}

{release.title}

{release.description && (

{release.description}

)}
{orderedTypes.map((type) => (

{getSectionTitle(type)}:

    {groupedChanges[type].map((change) => (
  • {change.text}
  • ))}
))}
); }