diff options
| author | Maze Winther <mazewinther@gmail.com> | 2026-04-15 02:05:41 +0200 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2026-04-15 02:05:41 +0200 |
| commit | 370944f443cb997ef71dda0c8ef6f4e92bea87d7 (patch) | |
| tree | 7a841c0291adb7c261f692121b81a3e9d1a9c615 | |
| parent | c839db92341b9058eac3ff92cc586df060b768da (diff) | |
| download | opencut-370944f443cb997ef71dda0c8ef6f4e92bea87d7.tar.gz | |
feat: new-release notice and colocate changelog under domain
14 files changed, 111 insertions, 11 deletions
diff --git a/apps/web/content-collections.ts b/apps/web/content-collections.ts index bde2ba94..0fdd7234 100644 --- a/apps/web/content-collections.ts +++ b/apps/web/content-collections.ts @@ -3,7 +3,7 @@ import { z } from "zod"; const changelog = defineCollection({
name: "changelog",
- directory: "content/changelog",
+ directory: "src/lib/changelog/entries",
include: "*.md",
schema: z.object({
content: z.string(),
@@ -12,6 +12,7 @@ const changelog = defineCollection({ published: z.boolean().default(true),
title: z.string(),
description: z.string().optional(),
+ summary: z.string().optional(),
changes: z.array(
z.object({
type: z.string(),
@@ -25,7 +26,8 @@ const changelog = defineCollection({ const sorted = [...publishedDocs].sort((a, b) =>
b.version.localeCompare(a.version, undefined, { numeric: true }),
);
- const isLatest = doc.published !== false && sorted[0]?.version === doc.version;
+ const isLatest =
+ doc.published !== false && sorted[0]?.version === doc.version;
return { ...doc, isLatest };
},
});
diff --git a/apps/web/src/app/changelog/[version]/page.tsx b/apps/web/src/app/changelog/[version]/page.tsx index 522f454a..f069832b 100644 --- a/apps/web/src/app/changelog/[version]/page.tsx +++ b/apps/web/src/app/changelog/[version]/page.tsx @@ -3,15 +3,15 @@ import Link from "next/link"; import { notFound } from "next/navigation";
import { BasePage } from "@/app/base-page";
import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
-import { getReleaseByVersion, getSortedReleases } from "../utils";
+import { getReleaseByVersion, getSortedReleases } from "@/lib/changelog/utils";
import {
ReleaseArticle,
ReleaseMeta,
ReleaseTitle,
ReleaseDescription,
ReleaseChanges,
-} from "../components/release";
-import { CopyMarkdownButton } from "../components/copy-markdown-button";
+} from "@/lib/changelog/components/release";
+import { CopyMarkdownButton } from "@/lib/changelog/components/copy-markdown-button";
type Props = { params: Promise<{ version: string }> };
diff --git a/apps/web/src/app/changelog/page.tsx b/apps/web/src/app/changelog/page.tsx index 20d599a6..8440eaa2 100644 --- a/apps/web/src/app/changelog/page.tsx +++ b/apps/web/src/app/changelog/page.tsx @@ -1,14 +1,17 @@ import type { Metadata } from "next"; import { BasePage } from "@/app/base-page"; import { Separator } from "@/components/ui/separator"; -import { type Release as ReleaseType, getSortedReleases } from "./utils"; +import { + type Release as ReleaseType, + getSortedReleases, +} from "@/lib/changelog/utils"; import { ReleaseArticle, ReleaseMeta, ReleaseTitle, ReleaseDescription, ReleaseChanges, -} from "./components/release"; +} from "@/lib/changelog/components/release"; export const metadata: Metadata = { title: "Changelog - OpenCut", diff --git a/apps/web/src/app/editor/[project_id]/page.tsx b/apps/web/src/app/editor/[project_id]/page.tsx index 8456d624..a3a7afa5 100644 --- a/apps/web/src/app/editor/[project_id]/page.tsx +++ b/apps/web/src/app/editor/[project_id]/page.tsx @@ -22,6 +22,7 @@ import { useEditor } from "@/hooks/use-editor"; import { Cancel01Icon } from "@hugeicons/core-free-icons"; import { HugeiconsIcon } from "@hugeicons/react"; import { Button } from "@/components/ui/button"; +import { ChangelogNotification } from "@/lib/changelog/components/changelog-notification"; export default function Editor() { const params = useParams(); @@ -38,6 +39,7 @@ export default function Editor() { </div> <Onboarding /> <MigrationDialog /> + <ChangelogNotification /> </div> </EditorProvider> </MobileGate> @@ -51,9 +53,7 @@ function DegradedRendererBanner() { return ( <div className="bg-accent border-b h-9 flex items-center justify-center gap-2 text-xs text-muted-foreground"> - <span> - For the best experience, open OpenCut in Chrome. - </span> + <span>For the best experience, open OpenCut in Chrome.</span> <Button variant="text" size="icon" diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx index d10dc9d3..6d4878c5 100644 --- a/apps/web/src/app/layout.tsx +++ b/apps/web/src/app/layout.tsx @@ -2,6 +2,7 @@ import { ThemeProvider } from "next-themes"; import Script from "next/script"; import "./globals.css"; import { Toaster } from "../components/ui/sonner"; +import { ChangelogNotification } from "@/lib/changelog/components/changelog-notification"; import { TooltipProvider } from "../components/ui/tooltip"; import { baseMetaData } from "./metadata"; import { BotIdClient } from "botid/client"; diff --git a/apps/web/src/app/projects/page.tsx b/apps/web/src/app/projects/page.tsx index 93d6e189..e8b09291 100644 --- a/apps/web/src/app/projects/page.tsx +++ b/apps/web/src/app/projects/page.tsx @@ -66,7 +66,7 @@ import { DeleteProjectDialog } from "@/components/editor/dialogs/delete-project- import { ProjectInfoDialog } from "@/components/editor/dialogs/project-info-dialog"; import { RenameProjectDialog } from "@/components/editor/dialogs/rename-project-dialog"; import { cn } from "@/utils/ui"; - +import { ChangelogNotification } from "@/lib/changelog/components/changelog-notification"; const formatProjectDuration = ({ duration, }: { @@ -107,6 +107,7 @@ export default function ProjectsPage() { <div className="bg-background min-h-screen"> <MigrationDialog /> <StoragePersistenceDialog /> + <ChangelogNotification /> <ProjectsHeader /> <ProjectsToolbar projectIds={projectsToDisplay.map((p) => p.id)} /> <main className="mx-auto px-4 pt-2 pb-6 flex flex-col gap-4"> diff --git a/apps/web/src/lib/changelog/components/changelog-notification.tsx b/apps/web/src/lib/changelog/components/changelog-notification.tsx new file mode 100644 index 00000000..c45eaaac --- /dev/null +++ b/apps/web/src/lib/changelog/components/changelog-notification.tsx @@ -0,0 +1,90 @@ +"use client"; + +import { useState, useEffect } from "react"; +import Link from "next/link"; +import { Cancel01Icon } from "@hugeicons/core-free-icons"; +import { HugeiconsIcon } from "@hugeicons/react"; +import { Button } from "@/components/ui/button"; +import { getSortedReleases } from "../utils"; +import type { Release } from "../utils"; + +const STORAGE_KEY = "last-seen-version"; + +export function ChangelogNotification() { + const [release, setRelease] = useState<Release | null>(null); + + useEffect(() => { + const releases = getSortedReleases(); + const latest = releases[0]; + if (!latest) return; + + let storedVersion: string | null = null; + try { + storedVersion = localStorage.getItem(STORAGE_KEY); + } catch { + // localStorage unavailable + } + + // First-time visitor: record the version silently, nothing to announce. + if (storedVersion === null) { + try { + localStorage.setItem(STORAGE_KEY, latest.version); + } catch { + // ignore + } + return; + } + + // Already seen this version. + if (storedVersion === latest.version) return; + + // New version since last visit: record it and show the notification. + try { + localStorage.setItem(STORAGE_KEY, latest.version); + } catch { + // ignore + } + + setRelease(latest); + }, []); + + if (!release) return null; + + return ( + <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"> + <div className="flex items-start justify-between gap-2"> + <div className="flex flex-col gap-1"> + <span className="text-sm font-semibold leading-snug"> + {release.title} + </span> + <span className="text-xs text-muted-foreground"> + v{release.version} + </span> + </div> + <Button + variant="ghost" + size="icon" + className="-mr-1 -mt-1 shrink-0" + onClick={() => setRelease(null)} + aria-label="Dismiss" + > + <HugeiconsIcon icon={Cancel01Icon} className="size-4" /> + </Button> + </div> + + {release.summary && ( + <p className="text-xs leading-relaxed text-muted-foreground"> + {release.summary} + </p> + )} + + <div className="flex justify-end"> + <Button asChild size="sm"> + <Link href="/changelog" onClick={() => setRelease(null)}> + See full changelog + </Link> + </Button> + </div> + </div> + ); +} diff --git a/apps/web/src/app/changelog/components/copy-markdown-button.tsx b/apps/web/src/lib/changelog/components/copy-markdown-button.tsx index bc7e94bf..bc7e94bf 100644 --- a/apps/web/src/app/changelog/components/copy-markdown-button.tsx +++ b/apps/web/src/lib/changelog/components/copy-markdown-button.tsx diff --git a/apps/web/src/app/changelog/components/release.tsx b/apps/web/src/lib/changelog/components/release.tsx index 475b0a6d..475b0a6d 100644 --- a/apps/web/src/app/changelog/components/release.tsx +++ b/apps/web/src/lib/changelog/components/release.tsx diff --git a/apps/web/content/changelog/0.1.0.md b/apps/web/src/lib/changelog/entries/0.1.0.md index f2000c76..33c5413c 100644 --- a/apps/web/content/changelog/0.1.0.md +++ b/apps/web/src/lib/changelog/entries/0.1.0.md @@ -4,6 +4,7 @@ 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."
+summary: "Properties panel overhaul, 1,000+ fonts, blend modes, a new color picker, and direct manipulation in the preview."
changes:
- type: new
text: "Rebuilt the properties panel from scratch. Number inputs now support math expressions and click-drag scrubbing instead of just typing values in."
diff --git a/apps/web/content/changelog/0.2.0.md b/apps/web/src/lib/changelog/entries/0.2.0.md index 18c8c48c..261b47d2 100644 --- a/apps/web/content/changelog/0.2.0.md +++ b/apps/web/src/lib/changelog/entries/0.2.0.md @@ -4,6 +4,7 @@ date: "2026-03-01" published: true title: "Motion & effects" description: "This release adds the foundation for motion and effects, alongside many improvements & fixes." +summary: "Keyframe animation, a new effects system with per-clip blur, and ripple editing mode." changes: - type: new text: "Keyframe animation system for animating element properties over time." diff --git a/apps/web/content/changelog/0.3.0.md b/apps/web/src/lib/changelog/entries/0.3.0.md index 37acf7c8..92848c33 100644 --- a/apps/web/content/changelog/0.3.0.md +++ b/apps/web/src/lib/changelog/entries/0.3.0.md @@ -4,6 +4,7 @@ date: "2026-04-15" published: true title: "Masks, animation & more" description: "This release adds masks, a graph editor for keyframe curves, volume and speed controls, preview zoom, canvas backgrounds, stickers, and a lot more." +summary: "Masks, graph editor for curves, volume & speed controls, preview zoom, canvas backgrounds, and stickers." changes: - type: new text: "Added a brand page with downloadable brand assets. Assets are also accessible directly from the header logo context menu." diff --git a/apps/web/content/changelog/0.4.0.md b/apps/web/src/lib/changelog/entries/0.4.0.md index 1c1257e2..1c1257e2 100644 --- a/apps/web/content/changelog/0.4.0.md +++ b/apps/web/src/lib/changelog/entries/0.4.0.md diff --git a/apps/web/src/app/changelog/utils.ts b/apps/web/src/lib/changelog/utils.ts index 682d376a..682d376a 100644 --- a/apps/web/src/app/changelog/utils.ts +++ b/apps/web/src/lib/changelog/utils.ts |
