summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaze Winther <mazewinther@gmail.com>2026-03-29 14:10:57 +0200
committerMaze Winther <mazewinther@gmail.com>2026-03-29 15:58:46 +0200
commit585e28d49b853155c34a3eba03388722e2049cb7 (patch)
tree92efed21fe58b812de7c7f83dc373d9594e1734c
parent1261e663e1bbb950e4efc8927ca4b068e0946bae (diff)
downloadopencut-585e28d49b853155c34a3eba03388722e2049cb7.tar.gz
feat: add published status to changelog entries
-rw-r--r--apps/web/content-collections.ts6
-rw-r--r--apps/web/content/changelog/0.1.0.md1
-rw-r--r--apps/web/content/changelog/0.2.0.md1
-rw-r--r--apps/web/content/changelog/0.3.0.md1
-rw-r--r--apps/web/src/app/changelog/[version]/page.tsx37
-rw-r--r--apps/web/src/app/changelog/components/copy-markdown-button.tsx2
-rw-r--r--apps/web/src/app/changelog/components/release.tsx2
-rw-r--r--apps/web/src/app/changelog/utils.ts18
8 files changed, 40 insertions, 28 deletions
diff --git a/apps/web/content-collections.ts b/apps/web/content-collections.ts
index b9904bf6..bde2ba94 100644
--- a/apps/web/content-collections.ts
+++ b/apps/web/content-collections.ts
@@ -9,6 +9,7 @@ const changelog = defineCollection({
content: z.string(),
version: z.string(),
date: z.string(),
+ published: z.boolean().default(true),
title: z.string(),
description: z.string().optional(),
changes: z.array(
@@ -20,10 +21,11 @@ const changelog = defineCollection({
}),
transform: async (doc, { collection }) => {
const allDocs = await collection.documents();
- const sorted = [...allDocs].sort((a, b) =>
+ const publishedDocs = allDocs.filter((entry) => entry.published !== false);
+ const sorted = [...publishedDocs].sort((a, b) =>
b.version.localeCompare(a.version, undefined, { numeric: true }),
);
- const isLatest = sorted[0]?.version === doc.version;
+ const isLatest = doc.published !== false && sorted[0]?.version === doc.version;
return { ...doc, isLatest };
},
});
diff --git a/apps/web/content/changelog/0.1.0.md b/apps/web/content/changelog/0.1.0.md
index 9f71ab68..f2000c76 100644
--- a/apps/web/content/changelog/0.1.0.md
+++ b/apps/web/content/changelog/0.1.0.md
@@ -1,6 +1,7 @@
---
version: "0.1.0"
date: "2026-02-23"
+published: true
title: "Editor foundation"
description: "This first release focuses on making editing faster, clearer, and more reliable for day-to-day use."
changes:
diff --git a/apps/web/content/changelog/0.2.0.md b/apps/web/content/changelog/0.2.0.md
index 04c81839..18c8c48c 100644
--- a/apps/web/content/changelog/0.2.0.md
+++ b/apps/web/content/changelog/0.2.0.md
@@ -1,6 +1,7 @@
---
version: "0.2.0"
date: "2026-03-01"
+published: true
title: "Motion & effects"
description: "This release adds the foundation for motion and effects, alongside many improvements & fixes."
changes:
diff --git a/apps/web/content/changelog/0.3.0.md b/apps/web/content/changelog/0.3.0.md
index cbd2037a..42c7c9a7 100644
--- a/apps/web/content/changelog/0.3.0.md
+++ b/apps/web/content/changelog/0.3.0.md
@@ -1,6 +1,7 @@
---
version: "0.3.0"
date: "2026-03-04"
+published: false
title: "Brand & polish"
description: "This release adds a brand page with downloadable assets, alongside a few fixes and improvements."
changes:
diff --git a/apps/web/src/app/changelog/[version]/page.tsx b/apps/web/src/app/changelog/[version]/page.tsx
index a3647fbd..522f454a 100644
--- a/apps/web/src/app/changelog/[version]/page.tsx
+++ b/apps/web/src/app/changelog/[version]/page.tsx
@@ -2,9 +2,8 @@ import type { Metadata } from "next";
import Link from "next/link";
import { notFound } from "next/navigation";
import { BasePage } from "@/app/base-page";
-import { allChangelogs } from "content-collections";
import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
-import { getSortedReleases } from "../utils";
+import { getReleaseByVersion, getSortedReleases } from "../utils";
import {
ReleaseArticle,
ReleaseMeta,
@@ -17,12 +16,12 @@ import { CopyMarkdownButton } from "../components/copy-markdown-button";
type Props = { params: Promise<{ version: string }> };
export async function generateStaticParams() {
- return allChangelogs.map((r) => ({ version: r.version }));
+ return getSortedReleases().map((release) => ({ version: release.version }));
}
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { version } = await params;
- const release = allChangelogs.find((r) => r.version === version);
+ const release = getReleaseByVersion({ version });
if (!release) return {};
return {
title: `${release.title} (${release.version}) - OpenCut Changelog`,
@@ -33,10 +32,8 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
export default async function ReleaseDetailPage({ params }: Props) {
const { version } = await params;
const releases = getSortedReleases();
- const index = releases.findIndex((r) => r.version === version);
-
+ const index = releases.findIndex((entry) => entry.version === version);
if (index === -1) notFound();
-
const release = releases[index];
const newer = index > 0 ? releases[index - 1] : null;
const older = index < releases.length - 1 ? releases[index + 1] : null;
@@ -52,20 +49,20 @@ export default async function ReleaseDetailPage({ params }: Props) {
All releases
</Link>
- <ReleaseArticle variant="detail">
- <div className="flex flex-col gap-4">
- <div className="flex items-center justify-between">
- <ReleaseMeta release={release} />
- <CopyMarkdownButton
- description={release.description}
- changes={release.changes}
- />
+ <ReleaseArticle variant="detail">
+ <div className="flex flex-col gap-4">
+ <div className="flex items-center justify-between">
+ <ReleaseMeta release={release} />
+ <CopyMarkdownButton
+ description={release.description}
+ changes={release.changes}
+ />
+ </div>
+ <ReleaseTitle as="h1">{release.title}</ReleaseTitle>
+ {release.description && (
+ <ReleaseDescription>{release.description}</ReleaseDescription>
+ )}
</div>
- <ReleaseTitle as="h1">{release.title}</ReleaseTitle>
- {release.description && (
- <ReleaseDescription>{release.description}</ReleaseDescription>
- )}
- </div>
<ReleaseChanges release={release} />
</ReleaseArticle>
diff --git a/apps/web/src/app/changelog/components/copy-markdown-button.tsx b/apps/web/src/app/changelog/components/copy-markdown-button.tsx
index edb920f5..7ea18b2a 100644
--- a/apps/web/src/app/changelog/components/copy-markdown-button.tsx
+++ b/apps/web/src/app/changelog/components/copy-markdown-button.tsx
@@ -23,7 +23,7 @@ function buildMarkdown({
const { grouped, orderedTypes } = groupAndOrderChanges({ changes });
for (const type of orderedTypes) {
- lines.push(`## ${getSectionTitle(type)}`);
+ lines.push(`## ${getSectionTitle({ type })}`);
for (const change of grouped[type]) {
lines.push(`- ${change.text}`);
}
diff --git a/apps/web/src/app/changelog/components/release.tsx b/apps/web/src/app/changelog/components/release.tsx
index 2226c321..10dcf561 100644
--- a/apps/web/src/app/changelog/components/release.tsx
+++ b/apps/web/src/app/changelog/components/release.tsx
@@ -87,7 +87,7 @@ export function ReleaseChanges({ release }: { release: Release }) {
{orderedTypes.map((type) => (
<div key={type} className="flex flex-col gap-1.5">
<h3 className="text-base font-semibold text-foreground">
- {getSectionTitle(type)}:
+ {getSectionTitle({ type })}:
</h3>
<ul className="list-disc pl-5 space-y-1.5">
{grouped[type].map((change) => (
diff --git a/apps/web/src/app/changelog/utils.ts b/apps/web/src/app/changelog/utils.ts
index d3405631..3f38ab6b 100644
--- a/apps/web/src/app/changelog/utils.ts
+++ b/apps/web/src/app/changelog/utils.ts
@@ -12,7 +12,7 @@ const knownSectionTitles: Record<string, string> = {
breaking: "Breaking Changes",
};
-export function getSectionTitle(type: string): string {
+export function getSectionTitle({ type }: { type: string }): string {
return (
knownSectionTitles[type] ?? type.charAt(0).toUpperCase() + type.slice(1)
);
@@ -36,8 +36,18 @@ export function groupAndOrderChanges({ changes }: { changes: Change[] }) {
return { grouped, orderedTypes };
}
+function isPublishedRelease({ published }: Release) {
+ return published !== false;
+}
+
export function getSortedReleases() {
- return [...allChangelogs].sort((a, b) =>
- b.version.localeCompare(a.version, undefined, { numeric: true }),
- );
+ return allChangelogs
+ .filter(isPublishedRelease)
+ .sort((a, b) =>
+ b.version.localeCompare(a.version, undefined, { numeric: true }),
+ );
+}
+
+export function getReleaseByVersion({ version }: { version: string }) {
+ return getSortedReleases().find((release) => release.version === version);
}