From d55d2583a71ae35fa8d0d1bfbec975e67e66aafa Mon Sep 17 00:00:00 2001 From: cashmere Date: Mon, 27 Jul 2026 01:21:20 +0200 Subject: feat: git-like project versioning (v1) Linear per-project history per notes/project-versioning.md: named checkpoints (thumbnail via renderer.captureFrame) plus auto-checkpoints on save (60s throttle, ring of 20, named never rotate). Restore writes a safety checkpoint first, then swaps the document outside undo/redo. Media assets referenced by any version are pinned against deletion. Migration v31-v32 stamps the schema; the version store lives in its own IDB database with an in-memory fallback for tests. Versions popover in the editor header for jump/rename/delete. 12 new tests. --- .../versions/components/version-name-dialog.tsx | 77 ++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 apps/web/src/versions/components/version-name-dialog.tsx (limited to 'apps/web/src/versions/components/version-name-dialog.tsx') diff --git a/apps/web/src/versions/components/version-name-dialog.tsx b/apps/web/src/versions/components/version-name-dialog.tsx new file mode 100644 index 00000000..f0514f5a --- /dev/null +++ b/apps/web/src/versions/components/version-name-dialog.tsx @@ -0,0 +1,77 @@ +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogBody, + DialogContent, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { Input } from "@/components/ui/input"; +import { useState } from "react"; +import { Label } from "@/components/ui/label"; + +export function VersionNameDialog({ + isOpen, + onOpenChange, + onConfirm, + mode, + initialName = "", +}: { + isOpen: boolean; + onOpenChange: (open: boolean) => void; + onConfirm: (name: string) => void; + mode: "create" | "rename"; + initialName?: string; +}) { + const [name, setName] = useState(initialName); + + const handleOpenChange = (open: boolean) => { + if (open) { + setName(initialName); + } + onOpenChange(open); + }; + + const title = mode === "create" ? "Create checkpoint" : "Rename checkpoint"; + const confirmLabel = mode === "create" ? "Create" : "Rename"; + + return ( + + + + {title} + + + + + setName(e.target.value)} + onKeyDown={(e) => { + if (e.key === "Enter") { + e.preventDefault(); + onConfirm(name); + } + }} + placeholder="e.g. Final cut" + /> + + + + + + + + + ); +} -- cgit v1.3.1