Skip to content

Commit 370944f

Browse files
committed
feat: new-release notice and colocate changelog under domain
1 parent c839db9 commit 370944f

14 files changed

Lines changed: 111 additions & 11 deletions

File tree

apps/web/content-collections.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { z } from "zod";
33

44
const changelog = defineCollection({
55
name: "changelog",
6-
directory: "content/changelog",
6+
directory: "src/lib/changelog/entries",
77
include: "*.md",
88
schema: z.object({
99
content: z.string(),
@@ -12,6 +12,7 @@ const changelog = defineCollection({
1212
published: z.boolean().default(true),
1313
title: z.string(),
1414
description: z.string().optional(),
15+
summary: z.string().optional(),
1516
changes: z.array(
1617
z.object({
1718
type: z.string(),
@@ -25,7 +26,8 @@ const changelog = defineCollection({
2526
const sorted = [...publishedDocs].sort((a, b) =>
2627
b.version.localeCompare(a.version, undefined, { numeric: true }),
2728
);
28-
const isLatest = doc.published !== false && sorted[0]?.version === doc.version;
29+
const isLatest =
30+
doc.published !== false && sorted[0]?.version === doc.version;
2931
return { ...doc, isLatest };
3032
},
3133
});

apps/web/src/app/changelog/[version]/page.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ import Link from "next/link";
33
import { notFound } from "next/navigation";
44
import { BasePage } from "@/app/base-page";
55
import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
6-
import { getReleaseByVersion, getSortedReleases } from "../utils";
6+
import { getReleaseByVersion, getSortedReleases } from "@/lib/changelog/utils";
77
import {
88
ReleaseArticle,
99
ReleaseMeta,
1010
ReleaseTitle,
1111
ReleaseDescription,
1212
ReleaseChanges,
13-
} from "../components/release";
14-
import { CopyMarkdownButton } from "../components/copy-markdown-button";
13+
} from "@/lib/changelog/components/release";
14+
import { CopyMarkdownButton } from "@/lib/changelog/components/copy-markdown-button";
1515

1616
type Props = { params: Promise<{ version: string }> };
1717

apps/web/src/app/changelog/page.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
import type { Metadata } from "next";
22
import { BasePage } from "@/app/base-page";
33
import { Separator } from "@/components/ui/separator";
4-
import { type Release as ReleaseType, getSortedReleases } from "./utils";
4+
import {
5+
type Release as ReleaseType,
6+
getSortedReleases,
7+
} from "@/lib/changelog/utils";
58
import {
69
ReleaseArticle,
710
ReleaseMeta,
811
ReleaseTitle,
912
ReleaseDescription,
1013
ReleaseChanges,
11-
} from "./components/release";
14+
} from "@/lib/changelog/components/release";
1215

1316
export const metadata: Metadata = {
1417
title: "Changelog - OpenCut",

apps/web/src/app/editor/[project_id]/page.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { useEditor } from "@/hooks/use-editor";
2222
import { Cancel01Icon } from "@hugeicons/core-free-icons";
2323
import { HugeiconsIcon } from "@hugeicons/react";
2424
import { Button } from "@/components/ui/button";
25+
import { ChangelogNotification } from "@/lib/changelog/components/changelog-notification";
2526

2627
export default function Editor() {
2728
const params = useParams();
@@ -38,6 +39,7 @@ export default function Editor() {
3839
</div>
3940
<Onboarding />
4041
<MigrationDialog />
42+
<ChangelogNotification />
4143
</div>
4244
</EditorProvider>
4345
</MobileGate>
@@ -51,9 +53,7 @@ function DegradedRendererBanner() {
5153

5254
return (
5355
<div className="bg-accent border-b h-9 flex items-center justify-center gap-2 text-xs text-muted-foreground">
54-
<span>
55-
For the best experience, open OpenCut in Chrome.
56-
</span>
56+
<span>For the best experience, open OpenCut in Chrome.</span>
5757
<Button
5858
variant="text"
5959
size="icon"

apps/web/src/app/layout.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { ThemeProvider } from "next-themes";
22
import Script from "next/script";
33
import "./globals.css";
44
import { Toaster } from "../components/ui/sonner";
5+
import { ChangelogNotification } from "@/lib/changelog/components/changelog-notification";
56
import { TooltipProvider } from "../components/ui/tooltip";
67
import { baseMetaData } from "./metadata";
78
import { BotIdClient } from "botid/client";

apps/web/src/app/projects/page.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ import { DeleteProjectDialog } from "@/components/editor/dialogs/delete-project-
6666
import { ProjectInfoDialog } from "@/components/editor/dialogs/project-info-dialog";
6767
import { RenameProjectDialog } from "@/components/editor/dialogs/rename-project-dialog";
6868
import { cn } from "@/utils/ui";
69-
69+
import { ChangelogNotification } from "@/lib/changelog/components/changelog-notification";
7070
const formatProjectDuration = ({
7171
duration,
7272
}: {
@@ -107,6 +107,7 @@ export default function ProjectsPage() {
107107
<div className="bg-background min-h-screen">
108108
<MigrationDialog />
109109
<StoragePersistenceDialog />
110+
<ChangelogNotification />
110111
<ProjectsHeader />
111112
<ProjectsToolbar projectIds={projectsToDisplay.map((p) => p.id)} />
112113
<main className="mx-auto px-4 pt-2 pb-6 flex flex-col gap-4">
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
"use client";
2+
3+
import { useState, useEffect } from "react";
4+
import Link from "next/link";
5+
import { Cancel01Icon } from "@hugeicons/core-free-icons";
6+
import { HugeiconsIcon } from "@hugeicons/react";
7+
import { Button } from "@/components/ui/button";
8+
import { getSortedReleases } from "../utils";
9+
import type { Release } from "../utils";
10+
11+
const STORAGE_KEY = "last-seen-version";
12+
13+
export function ChangelogNotification() {
14+
const [release, setRelease] = useState<Release | null>(null);
15+
16+
useEffect(() => {
17+
const releases = getSortedReleases();
18+
const latest = releases[0];
19+
if (!latest) return;
20+
21+
let storedVersion: string | null = null;
22+
try {
23+
storedVersion = localStorage.getItem(STORAGE_KEY);
24+
} catch {
25+
// localStorage unavailable
26+
}
27+
28+
// First-time visitor: record the version silently, nothing to announce.
29+
if (storedVersion === null) {
30+
try {
31+
localStorage.setItem(STORAGE_KEY, latest.version);
32+
} catch {
33+
// ignore
34+
}
35+
return;
36+
}
37+
38+
// Already seen this version.
39+
if (storedVersion === latest.version) return;
40+
41+
// New version since last visit: record it and show the notification.
42+
try {
43+
localStorage.setItem(STORAGE_KEY, latest.version);
44+
} catch {
45+
// ignore
46+
}
47+
48+
setRelease(latest);
49+
}, []);
50+
51+
if (!release) return null;
52+
53+
return (
54+
<div className="fixed bottom-5 left-5 z-50 flex w-72 flex-col gap-3 rounded-xl border bg-card p-4 shadow-lg">
55+
<div className="flex items-start justify-between gap-2">
56+
<div className="flex flex-col gap-1">
57+
<span className="text-sm font-semibold leading-snug">
58+
{release.title}
59+
</span>
60+
<span className="text-xs text-muted-foreground">
61+
v{release.version}
62+
</span>
63+
</div>
64+
<Button
65+
variant="ghost"
66+
size="icon"
67+
className="-mr-1 -mt-1 shrink-0"
68+
onClick={() => setRelease(null)}
69+
aria-label="Dismiss"
70+
>
71+
<HugeiconsIcon icon={Cancel01Icon} className="size-4" />
72+
</Button>
73+
</div>
74+
75+
{release.summary && (
76+
<p className="text-xs leading-relaxed text-muted-foreground">
77+
{release.summary}
78+
</p>
79+
)}
80+
81+
<div className="flex justify-end">
82+
<Button asChild size="sm">
83+
<Link href="/changelog" onClick={() => setRelease(null)}>
84+
See full changelog
85+
</Link>
86+
</Button>
87+
</div>
88+
</div>
89+
);
90+
}

apps/web/src/app/changelog/components/copy-markdown-button.tsx renamed to apps/web/src/lib/changelog/components/copy-markdown-button.tsx

File renamed without changes.
File renamed without changes.

apps/web/content/changelog/0.1.0.md renamed to apps/web/src/lib/changelog/entries/0.1.0.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ date: "2026-02-23"
44
published: true
55
title: "Editor foundation"
66
description: "This first release focuses on making editing faster, clearer, and more reliable for day-to-day use."
7+
summary: "Properties panel overhaul, 1,000+ fonts, blend modes, a new color picker, and direct manipulation in the preview."
78
changes:
89
- type: new
910
text: "Rebuilt the properties panel from scratch. Number inputs now support math expressions and click-drag scrubbing instead of just typing values in."

0 commit comments

Comments
 (0)