diff --git a/package-lock.json b/package-lock.json index aa0316bb..fddb027f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,12 +10,14 @@ "dependencies": { "@ffmpeg/ffmpeg": "^0.12.10", "@ffmpeg/util": "^0.12.2", + "clsx": "^2.1.1", "lottie-react": "^2.4.0", "lottie-web": "^5.12.2", "lucide-react": "^0.469.0", "next": "^15.1.8", "react": "^19.0.0", - "react-dom": "^19.0.0" + "react-dom": "^19.0.0", + "tailwind-merge": "^3.6.0" }, "devDependencies": { "@types/node": "^22", @@ -355,9 +357,6 @@ "cpu": [ "arm" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -374,9 +373,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -393,9 +389,6 @@ "cpu": [ "ppc64" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -412,9 +405,6 @@ "cpu": [ "riscv64" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -431,9 +421,6 @@ "cpu": [ "s390x" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -450,9 +437,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -469,9 +453,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -488,9 +469,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -507,9 +485,6 @@ "cpu": [ "arm" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -532,9 +507,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -557,9 +529,6 @@ "cpu": [ "ppc64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -582,9 +551,6 @@ "cpu": [ "riscv64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -607,9 +573,6 @@ "cpu": [ "s390x" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -632,9 +595,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -657,9 +617,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -682,9 +639,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -895,9 +849,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -914,9 +865,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -933,9 +881,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -952,9 +897,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1470,9 +1412,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1487,9 +1426,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1504,9 +1440,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1521,9 +1454,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1538,9 +1468,6 @@ "riscv64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1555,9 +1482,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1572,9 +1496,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1589,9 +1510,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -2105,6 +2023,15 @@ "version": "0.0.1", "license": "MIT" }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "2.0.1", "dev": true, @@ -5116,6 +5043,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tailwind-merge": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.6.0.tgz", + "integrity": "sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, "node_modules/tailwindcss": { "version": "3.4.19", "dev": true, diff --git a/src/app/page.tsx b/src/app/page.tsx index e5b632eb..cf1e5092 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -18,5 +18,4 @@ export default function Home() {
- ); -} + diff --git a/src/components/CustomTemplateManager.tsx b/src/components/CustomTemplateManager.tsx new file mode 100644 index 00000000..38b4b575 --- /dev/null +++ b/src/components/CustomTemplateManager.tsx @@ -0,0 +1,88 @@ +"use client"; + +import { useEffect, useState } from "react"; + +import { + getTemplates, + deleteTemplate, + renameTemplate, +} from "@/lib/templateStorage"; + +interface Props { + onApplyTemplate: (recipe: any) => void; +} + +export default function CustomTemplateManager({ + onApplyTemplate, +}: Props) { + const [templates, setTemplates] = useState([]); + + const refreshTemplates = () => { + setTemplates(getTemplates()); + }; + + useEffect(() => { + refreshTemplates(); + }, []); + + return ( +
+ {templates.length === 0 && ( +

+ No saved templates +

+ )} + + {templates.map((template) => ( +
+

+ {template.name} +

+ +
+ + + + + +
+
+ ))} +
+ ); +} \ No newline at end of file diff --git a/src/components/VideoEditor.tsx b/src/components/VideoEditor.tsx index 7f4ce60e..022735ad 100644 --- a/src/components/VideoEditor.tsx +++ b/src/components/VideoEditor.tsx @@ -16,6 +16,9 @@ import { Layers, Crop, Scissors, RotateCw, Volume2, SlidersHorizontal, Zap, AlertTriangle, Github } from "lucide-react"; +import CustomTemplateManager from "./CustomTemplateManager"; + +import {saveTemplate,} from "@/lib/templateStorage"; interface SectionProps { icon: React.ReactNode; @@ -50,6 +53,19 @@ export default function VideoEditor() { } = useVideoEditor(); const isProcessing = status === "loading-engine" || status === "exporting"; + const handleSaveTemplate = () => { + const name = prompt( + "Template name" + ); + + if (!name) return; + + saveTemplate({ + id: crypto.randomUUID(), + name, + recipe, + }); +}; return (
@@ -132,15 +148,33 @@ export default function VideoEditor() { "space-y-5", isProcessing && "pointer-events-none opacity-50" )}> -
-
} title="Output size"> - -
- -
} title="Framing" delay={100}> - -
-
+
+ +
} title="Templates"> + + + + updateRecipe(savedRecipe) + } + /> +
+ +
} title="Output size"> + +
+ +
} title="Framing" delay={100}> + +
+ +