├── .env.example ├── .eslintrc.json ├── .gitignore ├── README.md ├── app ├── api │ └── lambda │ │ ├── progress │ │ └── route.ts │ │ └── render │ │ └── route.ts ├── dashboard │ └── page.tsx ├── favicon.ico ├── globals.css ├── layout.tsx └── page.tsx ├── components.json ├── components ├── AlignEnd.tsx ├── Button │ ├── Button.tsx │ └── styles.module.css ├── Code.tsx ├── CodeCanvasSettings.tsx ├── CodeCanvasToolBox.tsx ├── CodeCanvasTools │ ├── CodeCanvasFontSizeTool.tsx │ ├── CodeCanvasFontTool.tsx │ ├── CodeCanvasThemeTool.tsx │ ├── CodeCanvasWallpaperTool.tsx │ └── CodeLangTool.tsx ├── CodeToolBox.tsx ├── ComboBox.tsx ├── ComboBoxSettings.tsx ├── ComboBoxWalls.tsx ├── Container.tsx ├── DownloadButton.tsx ├── Error.tsx ├── Header.tsx ├── Icons.tsx ├── MainSection.tsx ├── ProgressBar.tsx ├── RemotioPlayer.tsx ├── RenderControls.tsx ├── ResizableSection.tsx ├── SliderKit.tsx ├── Spacing.tsx ├── Spinner │ ├── Spinner.tsx │ └── styles.module.css ├── ThemeToggle │ ├── theme-provider.tsx │ └── theme-toggle.tsx ├── Thumbnail.tsx ├── Timeline │ ├── TimeLine.tsx │ └── TimelineCard.tsx ├── Tips │ ├── Tips.tsx │ └── styles.module.css ├── landing │ ├── Banner.tsx │ ├── FeatureCard.tsx │ ├── Features.tsx │ ├── Hero.tsx │ ├── MovingBorder.tsx │ ├── Navbar.tsx │ └── TypeWriteEfx.tsx └── ui │ ├── badge.tsx │ ├── button.tsx │ ├── card.tsx │ ├── command.tsx │ ├── dialog.tsx │ ├── dropdown-menu.tsx │ ├── input.tsx │ ├── label.tsx │ ├── popover.tsx │ ├── resizable.tsx │ ├── select.tsx │ ├── separator.tsx │ ├── slider.tsx │ ├── sonner.tsx │ └── tabs.tsx ├── config.mjs ├── constants ├── data.ts ├── lang.ts ├── themes.ts └── walls.ts ├── deploy.mjs ├── helpers ├── api-response.ts └── use-rendering.ts ├── hooks └── useCodeCanvas.tsx ├── lambda └── api.ts ├── lib └── utils.ts ├── next.config.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── bg-placeholder.png ├── wallone.jpg └── walltwo.jpg ├── remotion.config.ts ├── remotion ├── MyComp │ ├── CodeSnap.tsx │ ├── Main.tsx │ ├── NextLogo.tsx │ ├── Rings.tsx │ └── TextFade.tsx ├── Root.tsx ├── index.ts └── webpackOverride.ts ├── styles └── global.css ├── tailwind.config.js ├── tsconfig.json ├── types ├── constants.ts └── schema.ts └── vercel.json /.env.example: -------------------------------------------------------------------------------- 1 | REMOTION_AWS_ACCESS_KEY_ID="" 2 | REMOTION_AWS_SECRET_ACCESS_KEY="" 3 | NEXT_PUBLIC_VERCEL_URL= "https://codeflowsnippet.vercel.app" 4 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next", 3 | "plugins": ["@remotion"], 4 | "overrides": [ 5 | { 6 | "files": ["remotion/**/*.{ts,tsx}"], 7 | "extends": ["plugin:@remotion/recommended"] 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | out/ 38 | .env -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |

Under Development 🚀

5 | 6 | -------------------------------------------------------------------------------- /app/api/lambda/progress/route.ts: -------------------------------------------------------------------------------- 1 | import { 2 | speculateFunctionName, 3 | AwsRegion, 4 | getRenderProgress, 5 | } from "@remotion/lambda/client"; 6 | import { DISK, RAM, REGION, TIMEOUT } from "../../../../config.mjs"; 7 | import { executeApi } from "../../../../helpers/api-response"; 8 | import { ProgressRequest, ProgressResponse } from "../../../../types/schema"; 9 | 10 | export const POST = executeApi( 11 | ProgressRequest, 12 | async (req, body) => { 13 | const renderProgress = await getRenderProgress({ 14 | bucketName: body.bucketName, 15 | functionName: speculateFunctionName({ 16 | diskSizeInMb: DISK, 17 | memorySizeInMb: RAM, 18 | timeoutInSeconds: TIMEOUT, 19 | }), 20 | region: REGION as AwsRegion, 21 | renderId: body.id, 22 | }); 23 | 24 | if (renderProgress.fatalErrorEncountered) { 25 | return { 26 | type: "error", 27 | message: renderProgress.errors[0].message, 28 | }; 29 | } 30 | 31 | if (renderProgress.done) { 32 | return { 33 | type: "done", 34 | url: renderProgress.outputFile as string, 35 | size: renderProgress.outputSizeInBytes as number, 36 | }; 37 | } 38 | 39 | return { 40 | type: "progress", 41 | progress: Math.max(0.03, renderProgress.overallProgress), 42 | }; 43 | } 44 | ); 45 | -------------------------------------------------------------------------------- /app/api/lambda/render/route.ts: -------------------------------------------------------------------------------- 1 | import { AwsRegion, RenderMediaOnLambdaOutput } from "@remotion/lambda/client"; 2 | import { 3 | renderMediaOnLambda, 4 | speculateFunctionName, 5 | } from "@remotion/lambda/client"; 6 | import { DISK, RAM, REGION, SITE_NAME, TIMEOUT } from "../../../../config.mjs"; 7 | import { executeApi } from "../../../../helpers/api-response"; 8 | import { RenderRequest } from "../../../../types/schema"; 9 | 10 | export const POST = executeApi( 11 | RenderRequest, 12 | async (req, body) => { 13 | if ( 14 | !process.env.AWS_ACCESS_KEY_ID && 15 | !process.env.REMOTION_AWS_ACCESS_KEY_ID 16 | ) { 17 | throw new TypeError( 18 | "Set up Remotion Lambda to render videos. See the README.md for how to do so." 19 | ); 20 | } 21 | if ( 22 | !process.env.AWS_SECRET_ACCESS_KEY && 23 | !process.env.REMOTION_AWS_SECRET_ACCESS_KEY 24 | ) { 25 | throw new TypeError( 26 | "The environment variable REMOTION_AWS_SECRET_ACCESS_KEY is missing. Add it to your .env file." 27 | ); 28 | } 29 | 30 | const result = await renderMediaOnLambda({ 31 | codec: "h264", 32 | functionName: speculateFunctionName({ 33 | diskSizeInMb: DISK, 34 | memorySizeInMb: RAM, 35 | timeoutInSeconds: TIMEOUT, 36 | }), 37 | region: REGION as AwsRegion, 38 | serveUrl: SITE_NAME, 39 | composition: body.id, 40 | inputProps: body.inputProps, 41 | framesPerLambda: 10, 42 | downloadBehavior: { 43 | type: "download", 44 | fileName: "video.mp4", 45 | }, 46 | }); 47 | 48 | return result; 49 | } 50 | ); 51 | -------------------------------------------------------------------------------- /app/dashboard/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { NextPage } from "next"; 4 | import MainSection from "@/components/MainSection"; 5 | import Header from "@/components/Header"; 6 | 7 | const Home: NextPage = () => { 8 | return ( 9 | <> 10 |
11 | 12 | 13 | ); 14 | }; 15 | 16 | export default Home; 17 | -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiranism/codeFlow/c82b085e98145dd81c972f69252083a8a6418cd2/app/favicon.ico -------------------------------------------------------------------------------- /app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | :root { 7 | --background: 0 0% 100%; 8 | --foreground: 240 10% 3.9%; 9 | --card: 0 0% 100%; 10 | --card-foreground: 240 10% 3.9%; 11 | --popover: 0 0% 100%; 12 | --popover-foreground: 240 10% 3.9%; 13 | --primary: 142.1 76.2% 36.3%; 14 | --primary-foreground: 355.7 100% 97.3%; 15 | --secondary: 240 4.8% 95.9%; 16 | --secondary-foreground: 240 5.9% 10%; 17 | --muted: 240 4.8% 95.9%; 18 | --muted-foreground: 240 3.8% 46.1%; 19 | --accent: 240 4.8% 95.9%; 20 | --accent-foreground: 240 5.9% 10%; 21 | --destructive: 0 84.2% 60.2%; 22 | --destructive-foreground: 0 0% 98%; 23 | --border: 240 5.9% 90%; 24 | --input: 240 5.9% 90%; 25 | --ring: 142.1 76.2% 36.3%; 26 | --radius: 0.5rem; 27 | } 28 | 29 | .dark { 30 | --background: 20 14.3% 4.1%; 31 | --foreground: 0 0% 95%; 32 | --card: 24 9.8% 10%; 33 | --card-foreground: 0 0% 95%; 34 | --popover: 0 0% 9%; 35 | --popover-foreground: 0 0% 95%; 36 | --primary: 142.1 70.6% 45.3%; 37 | --primary-foreground: 144.9 80.4% 10%; 38 | --secondary: 240 3.7% 15.9%; 39 | --secondary-foreground: 0 0% 98%; 40 | --muted: 0 0% 15%; 41 | --muted-foreground: 240 5% 64.9%; 42 | --accent: 12 6.5% 15.1%; 43 | --accent-foreground: 0 0% 98%; 44 | --destructive: 0 62.8% 30.6%; 45 | --destructive-foreground: 0 85.7% 97.3%; 46 | --border: 240 3.7% 15.9%; 47 | --input: 240 3.7% 15.9%; 48 | --ring: 142.4 71.8% 29.2%; 49 | } 50 | } 51 | 52 | @layer base { 53 | * { 54 | @apply border-border; 55 | } 56 | body { 57 | @apply bg-background text-foreground; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /app/layout.tsx: -------------------------------------------------------------------------------- 1 | import ThemeProvider from "@/components/ThemeToggle/theme-provider"; 2 | import { Toaster } from "@/components/ui/sonner"; 3 | import { GeistSans } from "geist/font/sans"; 4 | import { Metadata } from "next"; 5 | import "../styles/global.css"; 6 | import "./globals.css"; 7 | 8 | export const metadata: Metadata = { 9 | title: "CodeFlow Snippet", 10 | description: "Animate your code snippet.", 11 | viewport: "width=device-width, initial-scale=1, maximum-scale=1", 12 | }; 13 | 14 | export default function RootLayout({ 15 | children, 16 | }: { 17 | children: React.ReactNode; 18 | }) { 19 | return ( 20 | 21 | 22 | 23 | {children} 24 | 25 | 26 | 27 | 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /app/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import Banner from "@/components/landing/Banner"; 3 | import Features from "@/components/landing/Features"; 4 | import Hero from "@/components/landing/Hero"; 5 | import Navbar from "@/components/landing/Navbar"; 6 | import React from "react"; 7 | 8 | export default function page() { 9 | return ( 10 | <> 11 | 12 | 13 | 14 | 15 | 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.js", 8 | "css": "app/globals.css", 9 | "baseColor": "slate", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /components/AlignEnd.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const container: React.CSSProperties = { 4 | alignSelf: "flex-end", 5 | }; 6 | 7 | export const AlignEnd: React.FC<{ 8 | children: React.ReactNode; 9 | }> = ({ children }) => { 10 | return
{children}
; 11 | }; 12 | -------------------------------------------------------------------------------- /components/Button/Button.tsx: -------------------------------------------------------------------------------- 1 | import React, { forwardRef } from "react"; 2 | import { Spacing } from "../Spacing"; 3 | import { Spinner } from "../Spinner/Spinner"; 4 | import styles from "./styles.module.css"; 5 | 6 | const ButtonForward: React.ForwardRefRenderFunction< 7 | HTMLButtonElement, 8 | { 9 | onClick?: () => void; 10 | disabled?: boolean; 11 | children: React.ReactNode; 12 | loading?: boolean; 13 | secondary?: boolean; 14 | } 15 | > = ({ onClick, disabled, children, loading, secondary }, ref) => { 16 | return ( 17 | 34 | ); 35 | }; 36 | 37 | export const Button = forwardRef(ButtonForward); 38 | -------------------------------------------------------------------------------- /components/Button/styles.module.css: -------------------------------------------------------------------------------- 1 | .button { 2 | border: 1px solid var(--foreground); 3 | border-radius: var(--geist-border-radius); 4 | background-color: var(--foreground); 5 | color: var(--background); 6 | padding-left: var(--geist-half-pad); 7 | padding-right: var(--geist-half-pad); 8 | height: 40px; 9 | font-family: var(--geist-font); 10 | font-weight: 500; 11 | transition: all 0.15s ease; 12 | display: inline-flex; 13 | flex-direction: row; 14 | align-items: center; 15 | appearance: none; 16 | font-size: 14px; 17 | } 18 | 19 | .secondarybutton { 20 | background-color: var(--background); 21 | color: var(--foreground); 22 | border-color: var(--unfocused-border-color); 23 | } 24 | 25 | .button:hover { 26 | background-color: var(--background); 27 | cursor: pointer; 28 | color: var(--foreground); 29 | border-color: var(--focused-border-color); 30 | } 31 | 32 | .button:disabled { 33 | background-color: var(--button-disabled-color); 34 | color: var(--disabled-text-color); 35 | border-color: var(--unfocused-border-color); 36 | cursor: not-allowed 37 | } 38 | -------------------------------------------------------------------------------- /components/Code.tsx: -------------------------------------------------------------------------------- 1 | import { useCodeCanvasStore } from "@/hooks/useCodeCanvas"; 2 | import { langOptions } from "@/constants/lang"; 3 | import { vscodeDark } from "@uiw/codemirror-theme-vscode"; 4 | import CodeMirror from "@uiw/react-codemirror"; 5 | import React from "react"; 6 | import { toast } from "sonner"; 7 | import CodeToolBox from "./CodeToolBox"; 8 | import { Icons } from "./Icons"; 9 | import { Button } from "./ui/button"; 10 | 11 | function Code() { 12 | const [value, setValue] = React.useState("console.log('hello world!');"); 13 | const { codeSnippet, setCodeSnippet, isEditMode, setIsEditMode, codeLang } = 14 | useCodeCanvasStore(); 15 | 16 | const onChange = React.useCallback((val: string) => { 17 | setValue(val); 18 | }, []); 19 | const handleAddCodeSnap = () => { 20 | setCodeSnippet([ 21 | ...codeSnippet, 22 | { 23 | id: Date.now().toString(), 24 | code: value, 25 | language: "js", 26 | editable: false, 27 | }, 28 | ]); 29 | }; 30 | 31 | const handleEditCodeSnap = () => { 32 | const updatedCodeSnippet = codeSnippet.map((snippet) => 33 | snippet.editable ? { ...snippet, code: value } : snippet 34 | ); 35 | setCodeSnippet(updatedCodeSnippet); 36 | toast.success("Code updated successfully"); 37 | setIsEditMode(false); 38 | }; 39 | 40 | const editableSnippet = codeSnippet.find((snippet) => snippet.editable); 41 | const displayValue = isEditMode ? editableSnippet?.code : value; 42 | 43 | const matchActiveLang = 44 | langOptions.find( 45 | (lang) => lang.value.toUpperCase() === codeLang.toUpperCase() 46 | ) || langOptions[0]; 47 | 48 | const activeLang = matchActiveLang?.extensions; 49 | 50 | return ( 51 |
52 | 53 | 54 | 61 |
62 | {isEditMode ? ( 63 | 71 | ) : ( 72 | 75 | )} 76 |
77 |
78 | ); 79 | } 80 | export default Code; 81 | -------------------------------------------------------------------------------- /components/CodeCanvasSettings.tsx: -------------------------------------------------------------------------------- 1 | import { ComboBoxSettings } from "./ComboBoxSettings"; 2 | 3 | export default function CodeCanvasSettings() { 4 | return ( 5 |
6 | 7 |
8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /components/CodeCanvasToolBox.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import CodeCanvasFontTool from "./CodeCanvasTools/CodeCanvasFontTool"; 3 | import CodeCanvasFontSizeTool from "./CodeCanvasTools/CodeCanvasFontSizeTool"; 4 | import CodeCanvasThemeTool from "./CodeCanvasTools/CodeCanvasThemeTool"; 5 | import CodeCanvasWallpaperTool from "./CodeCanvasTools/CodeCanvasWallpaperTool"; 6 | import CodeCanvasSettings from "./CodeCanvasSettings"; 7 | 8 | export default function CodeCanvasToolBox() { 9 | return ( 10 |
11 | 12 | 13 | 14 | 15 | 16 |
17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /components/CodeCanvasTools/CodeCanvasFontSizeTool.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Input } from "../ui/input"; 3 | import { useCodeCanvasStore } from "@/hooks/useCodeCanvas"; 4 | 5 | export default function CodeCanvasFontSizeTool() { 6 | const { setFontSize, fontSize } = useCodeCanvasStore(); 7 | return ( 8 |
9 | setFontSize(Number(e.target.value))} 15 | /> 16 |
17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /components/CodeCanvasTools/CodeCanvasFontTool.tsx: -------------------------------------------------------------------------------- 1 | import { fontFamilyOptions } from "@/constants/data"; 2 | import { useCodeCanvasStore } from "@/hooks/useCodeCanvas"; 3 | import { ComboBox } from "../ComboBox"; 4 | 5 | export default function CodeCanvasFontTool() { 6 | const { fontFamily, setFontFamily } = useCodeCanvasStore(); 7 | return ( 8 |
9 | 17 |
18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /components/CodeCanvasTools/CodeCanvasThemeTool.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ComboBox } from "../ComboBox"; 3 | import { useCodeCanvasStore } from "@/hooks/useCodeCanvas"; 4 | import { themeOptions } from "@/constants/themes"; 5 | 6 | export default function CodeCanvasThemeTool() { 7 | const { codeTheme, setCodeTheme } = useCodeCanvasStore(); 8 | return ( 9 |
10 | 18 |
19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /components/CodeCanvasTools/CodeCanvasWallpaperTool.tsx: -------------------------------------------------------------------------------- 1 | import { useCodeCanvasStore } from "@/hooks/useCodeCanvas"; 2 | import { ComboBoxWalls } from "../ComboBoxWalls"; 3 | 4 | export default function CodeCanvasWallpaperTool() { 5 | const { codeWallpaper, setCodeWallpaper } = useCodeCanvasStore(); 6 | return ( 7 |
8 | 15 |
16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /components/CodeCanvasTools/CodeLangTool.tsx: -------------------------------------------------------------------------------- 1 | import { langOptions } from "@/constants/lang"; 2 | import { useCodeCanvasStore } from "@/hooks/useCodeCanvas"; 3 | import { ComboBox } from "../ComboBox"; 4 | 5 | export default function CodeLangTool() { 6 | const { codeLang, setCodeLang } = useCodeCanvasStore(); 7 | return ( 8 |
9 | 17 |
18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /components/CodeToolBox.tsx: -------------------------------------------------------------------------------- 1 | import CodeLangTool from "./CodeCanvasTools/CodeLangTool"; 2 | import { Icons } from "./Icons"; 3 | import { Button } from "./ui/button"; 4 | 5 | export default function CodeToolBox({ 6 | isEditMode, 7 | onChange, 8 | }: { 9 | isEditMode: boolean; 10 | onChange: (val: string) => void; 11 | }) { 12 | return ( 13 |
14 | 15 |

16 | {isEditMode ? "Edit Mode" : null} 17 |

18 | 21 |
22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /components/ComboBox.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { Check, ChevronsUpDown, LucideIcon } from "lucide-react"; 3 | import * as React from "react"; 4 | 5 | import Image from "next/image"; 6 | import { cn } from "./../lib/utils"; 7 | import { Icons } from "./Icons"; 8 | import { Button } from "./ui/button"; 9 | import { Command, CommandGroup, CommandItem } from "./ui/command"; 10 | import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover"; 11 | 12 | type TComboBoxProps = { 13 | placeholder: string; 14 | options: { value: string; label: string }[]; 15 | icon?: keyof typeof Icons; 16 | img?: string; 17 | value: string; 18 | id: string; 19 | setValue: (value: string) => void; 20 | }; 21 | export function ComboBox({ 22 | options, 23 | placeholder, 24 | icon, 25 | img, 26 | value, 27 | setValue, 28 | id, 29 | }: TComboBoxProps) { 30 | const [open, setOpen] = React.useState(false); 31 | 32 | const SelectedIcon = (Icons as unknown as { [key: string]: LucideIcon })[ 33 | icon ?? "add" 34 | ]; 35 | 36 | const selectedOptionLabel = options.find( 37 | (option) => option.value.toUpperCase() === value.toUpperCase() 38 | )?.label; 39 | 40 | return ( 41 | 42 | 43 | 68 | 69 | 70 | 71 | {/* */} 72 | {/* No option found. */} 73 | 74 | {options.map((option) => ( 75 | { 79 | setValue( 80 | currentValue?.toUpperCase() === value.toUpperCase() 81 | ? "" 82 | : currentValue 83 | ); 84 | setOpen(false); 85 | }} 86 | > 87 | 95 | {option.label} 96 | 97 | ))} 98 | 99 | 100 | 101 | 102 | ); 103 | } 104 | -------------------------------------------------------------------------------- /components/ComboBoxSettings.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { Button } from "@/components/ui/button"; 3 | import { 4 | DropdownMenu, 5 | DropdownMenuContent, 6 | DropdownMenuLabel, 7 | DropdownMenuSeparator, 8 | DropdownMenuTrigger, 9 | } from "@/components/ui/dropdown-menu"; 10 | import { useCodeCanvasStore } from "@/hooks/useCodeCanvas"; 11 | import { Icons } from "./Icons"; 12 | import SliderKit from "./SliderKit"; 13 | import { useEffect } from "react"; 14 | 15 | export function ComboBoxSettings() { 16 | const { outerPadding, setOuterPadding, innerPadding, setInnerPadding } = 17 | useCodeCanvasStore(); 18 | useEffect(() => { 19 | useCodeCanvasStore.persist.rehydrate(); 20 | }, []); 21 | 22 | return ( 23 | 24 | 25 | 28 | 29 | 30 | 31 | Outer Padding 32 | 33 | 34 | 35 | 36 | Inner Padding 37 | 38 | 39 | 40 | 41 | 42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /components/ComboBoxWalls.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { Button } from "@/components/ui/button"; 3 | import { 4 | DropdownMenu, 5 | DropdownMenuContent, 6 | DropdownMenuLabel, 7 | DropdownMenuSeparator, 8 | DropdownMenuTrigger, 9 | } from "@/components/ui/dropdown-menu"; 10 | import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; 11 | import { codeWallOptions, gradientOptions } from "@/constants/walls"; 12 | import { cn } from "@/lib/utils"; 13 | import Image from "next/image"; 14 | import { Icons } from "./Icons"; 15 | 16 | type TComboBoxProps = { 17 | placeholder: string; 18 | options?: { value: string; label: string }[]; 19 | icon?: keyof typeof Icons; 20 | img?: string; 21 | value: string; 22 | id: string; 23 | setValue: (value: string) => void; 24 | }; 25 | 26 | export function ComboBoxWalls({ value, setValue }: TComboBoxProps) { 27 | const isGradient = value?.toLowerCase().includes("gradient"); 28 | 29 | return ( 30 | 31 | 32 | 50 | 51 | 52 | 53 | Background 54 | 55 | 56 | 57 | 58 | 59 | Wallpapers 60 | 61 | 62 | Gradients 63 | 64 | 65 | 66 |
67 | {codeWallOptions.map((option) => ( 68 | wallpaper setValue(option.value)} 76 | /> 77 | ))} 78 |
79 |
80 | 81 |
82 | {gradientOptions.map((option) => ( 83 |
setValue(option.value)} 90 | /> 91 | ))} 92 |
93 | 94 | 95 | 96 | 97 | ); 98 | } 99 | -------------------------------------------------------------------------------- /components/Container.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const inputContainer: React.CSSProperties = { 4 | border: "1px solid var(--unfocused-border-color)", 5 | padding: "var(--geist-pad)", 6 | borderRadius: "var(--geist-border-radius)", 7 | backgroundColor: "var(--background)", 8 | display: "flex", 9 | flexDirection: "column", 10 | }; 11 | 12 | export const InputContainer: React.FC<{ 13 | children: React.ReactNode; 14 | }> = ({ children }) => { 15 | return
{children}
; 16 | }; 17 | -------------------------------------------------------------------------------- /components/DownloadButton.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { State } from "../helpers/use-rendering"; 3 | import { Button } from "./Button/Button"; 4 | import { Spacing } from "./Spacing"; 5 | 6 | const light: React.CSSProperties = { 7 | opacity: 0.6, 8 | }; 9 | 10 | const link: React.CSSProperties = { 11 | textDecoration: "none", 12 | }; 13 | 14 | const row: React.CSSProperties = { 15 | display: "flex", 16 | flexDirection: "row", 17 | }; 18 | 19 | const Megabytes: React.FC<{ 20 | sizeInBytes: number; 21 | }> = ({ sizeInBytes }) => { 22 | const megabytes = Intl.NumberFormat("en", { 23 | notation: "compact", 24 | style: "unit", 25 | unit: "byte", 26 | unitDisplay: "narrow", 27 | }).format(sizeInBytes); 28 | return {megabytes}; 29 | }; 30 | 31 | export const DownloadButton: React.FC<{ 32 | state: State; 33 | undo: () => void; 34 | }> = ({ state, undo }) => { 35 | if (state.status === "rendering") { 36 | return ; 37 | } 38 | 39 | if (state.status !== "done") { 40 | throw new Error("Download button should not be rendered when not done"); 41 | } 42 | 43 | return ( 44 | 57 | ); 58 | }; 59 | 60 | const UndoIcon: React.FC = () => { 61 | return ( 62 | 63 | 67 | 68 | ); 69 | }; 70 | -------------------------------------------------------------------------------- /components/Error.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const container: React.CSSProperties = { 4 | color: "var(--geist-error)", 5 | fontFamily: "var(--geist-font)", 6 | paddingTop: "var(--geist-half-pad)", 7 | paddingBottom: "var(--geist-half-pad)", 8 | }; 9 | 10 | const icon: React.CSSProperties = { 11 | height: 20, 12 | verticalAlign: "text-bottom", 13 | marginRight: 6, 14 | }; 15 | 16 | export const ErrorComp: React.FC<{ 17 | message: string; 18 | }> = ({ message }) => { 19 | return ( 20 |
21 | 31 | 32 | 33 | 34 | 35 | Error: {message} 36 |
37 | ); 38 | }; 39 | -------------------------------------------------------------------------------- /components/Header.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ThemeToggle from "./ThemeToggle/theme-toggle"; 3 | 4 | export default function Header() { 5 | return ( 6 |
7 | 8 |
9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /components/Icons.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | AlertTriangle, 3 | ArrowRight, 4 | Check, 5 | ChevronLeft, 6 | ChevronRight, 7 | CircuitBoardIcon, 8 | CodeIcon, 9 | Command, 10 | CreditCard, 11 | File, 12 | FileText, 13 | HelpCircle, 14 | Image, 15 | Laptop, 16 | LayoutDashboardIcon, 17 | LigatureIcon, 18 | ListPlusIcon, 19 | Loader2, 20 | LogIn, 21 | LucideIcon, 22 | LucideProps, 23 | Moon, 24 | MoreVertical, 25 | PaletteIcon, 26 | Pizza, 27 | Plus, 28 | Settings, 29 | SunMedium, 30 | Trash, 31 | Twitter, 32 | User, 33 | User2Icon, 34 | UserX2Icon, 35 | X, 36 | } from "lucide-react"; 37 | 38 | export type Icon = LucideIcon; 39 | 40 | export const Icons = { 41 | dashboard: LayoutDashboardIcon, 42 | logo: Command, 43 | fontIcon: LigatureIcon, 44 | login: LogIn, 45 | timeline: ListPlusIcon, 46 | close: X, 47 | code: CodeIcon, 48 | theme: PaletteIcon, 49 | profile: User2Icon, 50 | spinner: Loader2, 51 | kanban: CircuitBoardIcon, 52 | chevronLeft: ChevronLeft, 53 | chevronRight: ChevronRight, 54 | trash: Trash, 55 | employee: UserX2Icon, 56 | post: FileText, 57 | page: File, 58 | media: Image, 59 | settings: Settings, 60 | billing: CreditCard, 61 | ellipsis: MoreVertical, 62 | add: Plus, 63 | warning: AlertTriangle, 64 | user: User, 65 | arrowRight: ArrowRight, 66 | help: HelpCircle, 67 | pizza: Pizza, 68 | sun: SunMedium, 69 | moon: Moon, 70 | laptop: Laptop, 71 | gitHub: ({ ...props }: LucideProps) => ( 72 | 87 | ), 88 | twitter: Twitter, 89 | check: Check, 90 | }; 91 | -------------------------------------------------------------------------------- /components/MainSection.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ResizableSec from "./ResizableSection"; 3 | import TimeLine from "./Timeline/TimeLine"; 4 | 5 | export default function MainSection() { 6 | return ( 7 |
8 | 9 | 10 |
11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /components/ProgressBar.tsx: -------------------------------------------------------------------------------- 1 | import React, { useMemo } from "react"; 2 | 3 | export const ProgressBar: React.FC<{ 4 | progress: number; 5 | }> = ({ progress }) => { 6 | const style: React.CSSProperties = useMemo(() => { 7 | return { 8 | width: "100%", 9 | height: 10, 10 | borderRadius: 5, 11 | appearance: "none", 12 | backgroundColor: "var(--unfocused-border-color)", 13 | marginTop: 10, 14 | marginBottom: 25, 15 | }; 16 | }, []); 17 | 18 | const fill: React.CSSProperties = useMemo(() => { 19 | return { 20 | backgroundColor: "var(--foreground)", 21 | height: 10, 22 | borderRadius: 5, 23 | transition: "width 0.1s ease-in-out", 24 | width: `${progress * 100}%`, 25 | }; 26 | }, [progress]); 27 | 28 | return ( 29 |
30 |
31 |
32 |
33 |
34 | ); 35 | }; 36 | -------------------------------------------------------------------------------- /components/RemotioPlayer.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { TCodesnippet, useCodeCanvasStore } from "@/hooks/useCodeCanvas"; 3 | import { Player } from "@remotion/player"; 4 | import React, { useEffect, useMemo } from "react"; 5 | import { z } from "zod"; 6 | import { Main } from "../remotion/MyComp/Main"; 7 | import { 8 | CompositionProps, 9 | DURATION_IN_FRAMES, 10 | VIDEO_FPS, 11 | VIDEO_HEIGHT, 12 | VIDEO_WIDTH, 13 | } from "../types/constants"; 14 | const outer: React.CSSProperties = { 15 | // borderRadius: "var(--geist-border-radius)", 16 | overflow: "hidden", 17 | boxShadow: "0 0 200px rgba(0, 0, 0, 0.15)", 18 | // marginBottom: 40, 19 | // marginTop: 60, 20 | }; 21 | 22 | const player: React.CSSProperties = { 23 | width: "100%", 24 | maxHeight: "500px", 25 | }; 26 | 27 | type TRemotionPlayerProps = { 28 | autoPlay: boolean; 29 | controls: boolean; 30 | mode: "timeline" | "canvas" | "landing"; 31 | title?: TCodesnippet[]; 32 | }; 33 | 34 | export default function RemotioPlayer({ 35 | autoPlay, 36 | controls, 37 | mode, 38 | title, 39 | }: TRemotionPlayerProps) { 40 | const { codeSnippet } = useCodeCanvasStore(); 41 | useEffect(() => { 42 | useCodeCanvasStore.persist.rehydrate(); 43 | }, []); 44 | 45 | const inputProps: z.infer = useMemo(() => { 46 | return { 47 | data: mode === "canvas" ? codeSnippet || "" : title || "", 48 | }; 49 | }, [title, mode, codeSnippet]); 50 | 51 | const codeSnipLength = 52 | mode === "landing" 53 | ? title?.length || 0 54 | : codeSnippet?.length === 0 55 | ? 1 56 | : codeSnippet?.length; 57 | return ( 58 |
59 | 71 |
72 | ); 73 | } 74 | -------------------------------------------------------------------------------- /components/RenderControls.tsx: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { useRendering } from "../helpers/use-rendering"; 3 | import { COMP_NAME, CompositionProps } from "../types/constants"; 4 | import { AlignEnd } from "./AlignEnd"; 5 | import { Button } from "./Button/Button"; 6 | import { InputContainer } from "./Container"; 7 | import { DownloadButton } from "./DownloadButton"; 8 | import { ErrorComp } from "./Error"; 9 | 10 | import { ProgressBar } from "./ProgressBar"; 11 | import { Spacing } from "./Spacing"; 12 | 13 | export const RenderControls: React.FC<{ 14 | text: string; 15 | setText: React.Dispatch>; 16 | inputProps: z.infer; 17 | }> = ({ text, setText, inputProps }) => { 18 | const { renderMedia, state, undo } = useRendering(COMP_NAME, inputProps); 19 | 20 | return ( 21 | 22 | {state.status === "init" || 23 | state.status === "invoking" || 24 | state.status === "error" ? ( 25 | <> 26 | {/* */} 31 | 32 | 33 | 40 | 41 | {state.status === "error" ? ( 42 | 43 | ) : null} 44 | 45 | ) : null} 46 | {state.status === "rendering" || state.status === "done" ? ( 47 | <> 48 | 51 | 52 | 53 | 54 | 55 | 56 | ) : null} 57 | 58 | ); 59 | }; 60 | -------------------------------------------------------------------------------- /components/ResizableSection.tsx: -------------------------------------------------------------------------------- 1 | import Code from "./Code"; 2 | import CodeCanvasToolBox from "./CodeCanvasToolBox"; 3 | import RemotioPlayer from "./RemotioPlayer"; 4 | import { 5 | ResizableHandle, 6 | ResizablePanel, 7 | ResizablePanelGroup, 8 | } from "./ui/resizable"; 9 | 10 | export default function ResizableSec() { 11 | return ( 12 | 16 | 22 |
23 | 24 |
25 |
26 | 27 | 28 |
29 | 30 | 31 | {/* 36 | 37 | 38 | 39 | 40 | */} 41 |
42 |
43 |
44 | ); 45 | } 46 | -------------------------------------------------------------------------------- /components/SliderKit.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Slider } from "./ui/slider"; 3 | import { cn } from "@/lib/utils"; 4 | 5 | type SliderProps = React.ComponentProps; 6 | export default function SliderKit({ className, ...props }: SliderProps) { 7 | return ( 8 | 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /components/Spacing.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export const Spacing: React.FC = () => { 4 | return ( 5 |
11 | ); 12 | }; 13 | -------------------------------------------------------------------------------- /components/Spinner/Spinner.tsx: -------------------------------------------------------------------------------- 1 | import React, { useMemo } from "react"; 2 | import { makeRect } from "@remotion/shapes"; 3 | import { translatePath } from "@remotion/paths"; 4 | import styles from "./styles.module.css"; 5 | 6 | const viewBox = 100; 7 | const lines = 12; 8 | const width = viewBox * 0.08; 9 | 10 | const { path } = makeRect({ 11 | height: viewBox * 0.24, 12 | width, 13 | cornerRadius: width / 2, 14 | }); 15 | 16 | const translated = translatePath(path, viewBox / 2 - width / 2, viewBox * 0.03); 17 | 18 | export const Spinner: React.FC<{ 19 | size: number; 20 | }> = ({ size }) => { 21 | const style = useMemo(() => { 22 | return { 23 | width: size, 24 | height: size, 25 | }; 26 | }, [size]); 27 | 28 | return ( 29 | 30 | {new Array(lines).fill(true).map((_, index) => { 31 | return ( 32 | 43 | ); 44 | })} 45 | 46 | ); 47 | }; 48 | -------------------------------------------------------------------------------- /components/Spinner/styles.module.css: -------------------------------------------------------------------------------- 1 | @keyframes spinner { 2 | 0% { 3 | opacity: 1; 4 | } 5 | 100% { 6 | opacity: 0.15; 7 | } 8 | } 9 | 10 | .line { 11 | animation: spinner 1.2s linear infinite; 12 | } 13 | -------------------------------------------------------------------------------- /components/ThemeToggle/theme-provider.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { ThemeProvider as NextThemesProvider } from "next-themes"; 4 | import { type ThemeProviderProps } from "next-themes/dist/types"; 5 | 6 | export default function ThemeProvider({ 7 | children, 8 | ...props 9 | }: ThemeProviderProps) { 10 | return ( 11 | 12 | {children} 13 | 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /components/ThemeToggle/theme-toggle.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { useTheme } from "next-themes"; 3 | 4 | import { Button } from "@/components/ui/button"; 5 | import { 6 | DropdownMenu, 7 | DropdownMenuContent, 8 | DropdownMenuItem, 9 | DropdownMenuTrigger, 10 | } from "@/components/ui/dropdown-menu"; 11 | import { Icons } from "../Icons"; 12 | type CompProps = {}; 13 | export default function ThemeToggle({}: CompProps) { 14 | const { setTheme } = useTheme(); 15 | return ( 16 | 17 | 18 | 23 | 24 | 25 | setTheme("light")}> 26 | Light 27 | 28 | setTheme("dark")}> 29 | Dark 30 | 31 | setTheme("system")}> 32 | System 33 | 34 | 35 | 36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /components/Thumbnail.tsx: -------------------------------------------------------------------------------- 1 | import { useCodeCanvasStore } from "@/hooks/useCodeCanvas"; 2 | import { Thumbnail as RemotionThumbnail } from "@remotion/player"; 3 | import React, { useMemo } from "react"; 4 | import { z } from "zod"; 5 | import { Main } from "../remotion/MyComp/Main"; 6 | import { 7 | CompositionProps, 8 | DURATION_IN_FRAMES, 9 | VIDEO_FPS, 10 | VIDEO_HEIGHT, 11 | VIDEO_WIDTH, 12 | } from "../types/constants"; 13 | const outer: React.CSSProperties = { 14 | // borderRadius: "var(--geist-border-radius)", 15 | overflow: "hidden", 16 | boxShadow: "0 0 200px rgba(0, 0, 0, 0.15)", 17 | // marginBottom: 40, 18 | // marginTop: 60, 19 | }; 20 | 21 | const timeline: React.CSSProperties = { 22 | width: "100", 23 | objectFit: "cover", 24 | borderRadius: "6px", 25 | }; 26 | 27 | type TRemotionThumbnailProps = { 28 | mode: "timeline" | "canvas"; 29 | title?: string; 30 | }; 31 | 32 | const Thumbnail = ({ mode, title }: TRemotionThumbnailProps) => { 33 | const { codeSnippet } = useCodeCanvasStore(); 34 | const inputProps: z.infer = useMemo(() => { 35 | return { 36 | data: mode === "canvas" ? codeSnippet || "" : title || "", 37 | }; 38 | }, [title, mode, codeSnippet]); 39 | return ( 40 |
41 | 51 |
52 | ); 53 | }; 54 | 55 | export default Thumbnail; 56 | -------------------------------------------------------------------------------- /components/Timeline/TimeLine.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { useCodeCanvasStore } from "@/hooks/useCodeCanvas"; 3 | import { 4 | DndContext, 5 | DragEndEvent, 6 | DragOverlay, 7 | DragStartEvent, 8 | KeyboardSensor, 9 | PointerSensor, 10 | UniqueIdentifier, 11 | closestCenter, 12 | useSensor, 13 | useSensors, 14 | } from "@dnd-kit/core"; 15 | import { 16 | SortableContext, 17 | arrayMove, 18 | horizontalListSortingStrategy, 19 | sortableKeyboardCoordinates, 20 | } from "@dnd-kit/sortable"; 21 | import { useEffect, useState } from "react"; 22 | import TimelineCard from "./TimelineCard"; 23 | 24 | export default function TimeLine() { 25 | const { codeSnippet, setCodeSnippet } = useCodeCanvasStore(); 26 | const [activeId, setActiveId] = useState(null); 27 | useEffect(() => { 28 | useCodeCanvasStore.persist.rehydrate(); 29 | }, []); 30 | 31 | const sensors = useSensors( 32 | useSensor(PointerSensor, { 33 | activationConstraint: { 34 | distance: 10, 35 | }, 36 | }), 37 | useSensor(KeyboardSensor, { 38 | coordinateGetter: sortableKeyboardCoordinates, 39 | }) 40 | ); 41 | 42 | function handleDragStart(event: DragStartEvent) { 43 | const { active } = event; 44 | if (!active.id) return; 45 | setActiveId(active.id); 46 | } 47 | 48 | function handleDragEnd(event: DragEndEvent) { 49 | const { active, over } = event; 50 | if (!over) return; 51 | const activeId = active.id; 52 | const overId = over.id; 53 | if (activeId !== overId) { 54 | const activeColumnIndex = codeSnippet.findIndex( 55 | (col) => col.id === activeId 56 | ); 57 | const overColumnIndex = codeSnippet.findIndex((col) => col.id === overId); 58 | setCodeSnippet( 59 | arrayMove(codeSnippet, activeColumnIndex, overColumnIndex) 60 | ); 61 | } 62 | setActiveId(null); 63 | } 64 | 65 | const activeItem = codeSnippet.find((item) => item.id === activeId); 66 | const findActiveIndex = codeSnippet.findIndex((item) => item.id === activeId); 67 | 68 | return ( 69 |
70 | 76 | 80 | {codeSnippet?.map((item, index) => ( 81 | 82 | ))} 83 | 84 | {activeItem && ( 85 | 90 | )} 91 | 92 | 93 | 94 |
95 | ); 96 | } 97 | -------------------------------------------------------------------------------- /components/Timeline/TimelineCard.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from "@/components/ui/button"; 2 | import { 3 | Card, 4 | CardContent, 5 | CardFooter, 6 | CardHeader, 7 | } from "@/components/ui/card"; 8 | import { Separator } from "@/components/ui/separator"; 9 | import { TCodesnippet, useCodeCanvasStore } from "@/hooks/useCodeCanvas"; 10 | import { useSortable } from "@dnd-kit/sortable"; 11 | import { CSS } from "@dnd-kit/utilities"; 12 | import { cva } from "class-variance-authority"; 13 | import { useCallback, useMemo } from "react"; 14 | import { toast } from "sonner"; 15 | import { Icons } from "../Icons"; 16 | import Thumbnail from "../Thumbnail"; 17 | type TTimelineProps = { 18 | item: TCodesnippet; 19 | index?: number; 20 | isOverlay?: boolean; 21 | }; 22 | const TimelineCard = ({ item, index, isOverlay }: TTimelineProps) => { 23 | const { 24 | setIsEditMode, 25 | codeSnippet, 26 | setCodeSnippet, 27 | isEditMode, 28 | activeSnippetId, 29 | setActiveSnippetId, 30 | } = useCodeCanvasStore(); 31 | const { 32 | attributes, 33 | listeners, 34 | setNodeRef, 35 | transform, 36 | transition, 37 | isDragging, 38 | } = useSortable({ id: item.id }); 39 | 40 | const handleEdit = useCallback(() => { 41 | setIsEditMode(true); 42 | setActiveSnippetId(item.id); 43 | const updatedCodeSnippet = codeSnippet.map((snippet) => 44 | snippet.id === item.id 45 | ? { ...snippet, editable: true } 46 | : { ...snippet, editable: false } 47 | ); 48 | setCodeSnippet(updatedCodeSnippet); 49 | }, [item.id, codeSnippet]); 50 | 51 | const handleDelete = useCallback(() => { 52 | const updatedCodeSnippet = codeSnippet.filter( 53 | (snippet) => snippet.id !== item.id 54 | ); 55 | setCodeSnippet(updatedCodeSnippet); 56 | toast.success("Code deleted successfully"); 57 | }, [item.id, codeSnippet]); 58 | 59 | const variants = cva("w-[350px]", { 60 | variants: { 61 | dragging: { 62 | default: "border-2 border-transparent", 63 | over: "ring-2 opacity-30", 64 | edit: "border border-primary", 65 | overlay: "ring-2 ring-primary", 66 | }, 67 | }, 68 | }); 69 | 70 | const style = { 71 | transform: CSS.Transform.toString(transform), 72 | transition, 73 | }; 74 | 75 | const isEditActive = useMemo( 76 | () => isEditMode && item.id === activeSnippetId, 77 | [activeSnippetId, isEditMode] 78 | ); 79 | 80 | return ( 81 | 96 | 97 |

Scene {index ? index + 1 : 1}

98 | 106 |
107 | 108 | {isEditActive && ( 109 |
110 | 121 |
122 | )} 123 | 124 |
125 | 126 | 127 | 130 | 131 |
132 | ); 133 | }; 134 | 135 | export default TimelineCard; 136 | -------------------------------------------------------------------------------- /components/Tips/Tips.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styles from "./styles.module.css"; 3 | 4 | const titlerow: React.CSSProperties = { 5 | display: "flex", 6 | flexDirection: "row", 7 | alignItems: "center", 8 | justifyContent: "flex-start", 9 | }; 10 | 11 | const titlestyle: React.CSSProperties = { 12 | marginBottom: "0.75em", 13 | marginTop: "0.75em", 14 | color: "var(--foreground)", 15 | }; 16 | 17 | const a: React.CSSProperties = { 18 | textDecoration: "none", 19 | color: "inherit", 20 | flex: 1, 21 | }; 22 | 23 | const Tip: React.FC<{ 24 | title: React.ReactNode; 25 | description: React.ReactNode; 26 | href: string; 27 | }> = ({ title, description, href }) => { 28 | return ( 29 | 30 |
31 |
32 |

{title}

33 |
34 | 35 | 39 | 40 |
41 |

{description}

42 |
43 |
44 | ); 45 | }; 46 | 47 | export const Tips: React.FC = () => { 48 | return ( 49 |
50 | 55 | 60 | 65 |
66 | ); 67 | }; 68 | -------------------------------------------------------------------------------- /components/Tips/styles.module.css: -------------------------------------------------------------------------------- 1 | .row { 2 | display: flex; 3 | flex-direction: row; 4 | font-family: Inter; 5 | } 6 | 7 | .item { 8 | cursor: pointer; 9 | transition: transform 0.2s ease-in-out; 10 | padding: 10px; 11 | } 12 | 13 | .icon { 14 | opacity: 0; 15 | transition: opacity 0.2s ease-in-out; 16 | } 17 | 18 | .item:hover { 19 | transform: translateY(-2px); 20 | .icon { 21 | opacity: 1; 22 | } 23 | } 24 | 25 | .p { 26 | font-size: 14px; 27 | margin-top: 0; 28 | line-height: 1.5; 29 | color: var(--subtitle); 30 | } 31 | 32 | .flex { 33 | flex: 1; 34 | } 35 | 36 | @media (max-width: 768px) { 37 | .row { 38 | flex-direction: column; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /components/landing/Banner.tsx: -------------------------------------------------------------------------------- 1 | const Banner = () => { 2 | // const [state, handleSubmit] = useForm('mjvqrzpz') 3 | // const [theme, setTheme] = useTheme() 4 | return ( 5 |
6 |
7 |
8 |
9 |
10 |
11 |

12 | 13 | Empower{" "} 14 | 15 | 16 | and{" "} 17 | 18 | 19 | boost{" "} 20 | 21 | 22 | your{" "} 23 | 24 | 25 | business's{" "} 26 | 27 | 28 | productivity 29 | 30 |

31 |
32 |

33 | Unlock the potential of your business with CodeFlow. Experience 34 | simplified operations. 35 |

36 |
37 |
38 |
39 |
40 |
41 | ); 42 | }; 43 | 44 | export default Banner; 45 | -------------------------------------------------------------------------------- /components/landing/FeatureCard.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils"; 2 | import { ReactNode } from "react"; 3 | 4 | export type TFeatureCardProps = { 5 | title: string; 6 | description: string; 7 | icon: ReactNode; 8 | backgroundColor: string; 9 | }; 10 | const FeatureCard = ({ 11 | title, 12 | description, 13 | icon, 14 | backgroundColor, 15 | }: TFeatureCardProps) => { 16 | return ( 17 |
23 | 29 | {icon} 30 | 31 |
32 |

33 | {title} 34 |

35 |

{description}

36 |
37 |
38 | ); 39 | }; 40 | 41 | export default FeatureCard; 42 | -------------------------------------------------------------------------------- /components/landing/Features.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { 3 | BlocksIcon, 4 | DollarSignIcon, 5 | MessagesSquareIcon, 6 | SettingsIcon, 7 | ShieldCheckIcon, 8 | ZapIcon, 9 | } from "lucide-react"; 10 | import FeatureCard, { TFeatureCardProps } from "./FeatureCard"; 11 | 12 | const featuresData: TFeatureCardProps[] = [ 13 | { 14 | title: "Highly customizable", 15 | description: "Customize the platform to your needs and make it your own.", 16 | icon: , 17 | backgroundColor: "from-blue-500/20 to-blue-500/5", 18 | }, 19 | { 20 | title: "Secure and reliable", 21 | description: "We take security seriously and ensure your data is safe.", 22 | icon: , 23 | backgroundColor: "from-green-500/20 to-green-500/5", 24 | }, 25 | { 26 | title: "Easy to use", 27 | description: "The platform is easy to use and requires no training.", 28 | icon: , 29 | backgroundColor: "from-yellow-500/20 to-yellow-500/5", 30 | }, 31 | { 32 | title: "Powerful integrations", 33 | description: "Integrate with your favorite tools and services, and more.", 34 | icon: , 35 | backgroundColor: "from-red-500/20 to-red-500/5", 36 | }, 37 | { 38 | title: "Affordable pricing", 39 | description: "We offer affordable pricing plans for all business sizes.", 40 | icon: , 41 | backgroundColor: "from-pink-500/20 to-pink-500/5", 42 | }, 43 | { 44 | title: "24/7 support", 45 | description: "Our support team is available 24/7 to help you out.", 46 | icon: , 47 | backgroundColor: "from-purple-500/20 to-purple-500/5", 48 | }, 49 | ]; 50 | 51 | const Features = () => { 52 | return ( 53 |
54 |
55 |
56 | 57 | Powerful Features 58 | 59 |

60 | 61 | Features to boost your creativity. 62 | 63 |

64 |
65 |

66 | 67 | Explore a diverse range of cutting-edge tools meticulously crafted 68 | to drive your business towards unparalleled success. 69 | 70 | 71 | Explore a diverse range of cutting-edge tools crafted for business 72 | success. 73 | 74 |

75 |
76 |
77 |
    78 | {featuresData.map((feature, i) => ( 79 |
  • 80 | 86 |
  • 87 | ))} 88 |
89 |
90 |
91 | ); 92 | }; 93 | 94 | export default Features; 95 | -------------------------------------------------------------------------------- /components/landing/Hero.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils"; 2 | import { motion } from "framer-motion"; 3 | import Link from "next/link"; 4 | import CodeCanvasFontSizeTool from "../CodeCanvasTools/CodeCanvasFontSizeTool"; 5 | import CodeCanvasFontTool from "../CodeCanvasTools/CodeCanvasFontTool"; 6 | import CodeCanvasWallpaperTool from "../CodeCanvasTools/CodeCanvasWallpaperTool"; 7 | import RemotioPlayer from "../RemotioPlayer"; 8 | import { buttonVariants } from "../ui/button"; 9 | import { Card, CardContent, CardFooter } from "../ui/card"; 10 | import { TypeWriteEfx } from "./TypeWriteEfx"; 11 | 12 | const Hero = () => { 13 | const words = [ 14 | { 15 | text: "CodeFlow.", 16 | }, 17 | ]; 18 | 19 | return ( 20 |
21 |
22 | 28 | Under Development 29 | 30 | 39 |
44 | 49 |
50 | 51 | Animate 52 | {" "} 53 | 54 | Your{" "} 55 | 56 | 57 | Code{" "} 58 | 59 | 60 | Snippet{" "} 61 | 62 |
63 | 70 | Welcome to CodeFlow, create 71 | effortlessly beautiful code animations in seconds and share them in 72 | your socials..{" "} 73 | 74 | 81 | Try Now 82 | 83 |
84 |
85 | 86 | {/* */} 87 | 88 | res){ 101 | res = curr 102 | } 103 | } 104 | return res; 105 | } 106 | 107 | const op = largestNoFromArr(arr); 108 | console.log(op)`, 109 | id: "1", 110 | language: "js", 111 | editable: false, 112 | }, 113 | { 114 | code: `function App() { 115 | // create an array of objects 116 | const users = [ 117 | {id: 1, name: 'John', status: 'active'}, 118 | {id: 2, name: 'Rusk', status: 'inactive'}, 119 | {id: 3, name: 'Erlic', status: 'active'}, 120 | {id: 4, name: 'Sara', status: 'active'}, 121 | {id: 5, name: 'Kat', status: 'inactive'} 122 | ] 123 | 124 | // filter array based on active status 125 | const filterd_users = users.filter( user => user.status === 'active' ); 126 | 127 | }`, 128 | id: "1", 129 | language: "js", 130 | editable: false, 131 | }, 132 | ]} 133 | /> 134 | 135 | 136 |
137 | 138 | 139 |
140 |
141 |
142 |
143 |
144 | ); 145 | }; 146 | 147 | export default Hero; 148 | -------------------------------------------------------------------------------- /components/landing/MovingBorder.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import React from "react"; 3 | import { 4 | motion, 5 | useAnimationFrame, 6 | useMotionTemplate, 7 | useMotionValue, 8 | useTransform, 9 | } from "framer-motion"; 10 | import { useRef } from "react"; 11 | import { cn } from "@/lib/utils"; 12 | 13 | export function Button({ 14 | borderRadius = "1.75rem", 15 | children, 16 | as: Component = "button", 17 | containerClassName, 18 | borderClassName, 19 | duration, 20 | className, 21 | ...otherProps 22 | }: { 23 | borderRadius?: string; 24 | children: React.ReactNode; 25 | as?: any; 26 | containerClassName?: string; 27 | borderClassName?: string; 28 | duration?: number; 29 | className?: string; 30 | [key: string]: any; 31 | }) { 32 | return ( 33 | 43 |
47 | 48 |
54 | 55 |
56 | 57 |
66 | {children} 67 |
68 | 69 | ); 70 | } 71 | 72 | export const MovingBorder = ({ 73 | children, 74 | duration = 2000, 75 | rx, 76 | ry, 77 | ...otherProps 78 | }: { 79 | children: React.ReactNode; 80 | duration?: number; 81 | rx?: string; 82 | ry?: string; 83 | [key: string]: any; 84 | }) => { 85 | const pathRef = useRef(); 86 | const progress = useMotionValue(0); 87 | 88 | useAnimationFrame((time) => { 89 | const length = pathRef.current?.getTotalLength(); 90 | if (length) { 91 | const pxPerMillisecond = length / duration; 92 | progress.set((time * pxPerMillisecond) % length); 93 | } 94 | }); 95 | 96 | const x = useTransform( 97 | progress, 98 | (val) => pathRef.current?.getPointAtLength(val).x 99 | ); 100 | const y = useTransform( 101 | progress, 102 | (val) => pathRef.current?.getPointAtLength(val).y 103 | ); 104 | 105 | const transform = useMotionTemplate`translateX(${x}px) translateY(${y}px) translateX(-50%) translateY(-50%)`; 106 | 107 | return ( 108 | <> 109 | 117 | 125 | 126 | 135 | {children} 136 | 137 | 138 | ); 139 | }; 140 | -------------------------------------------------------------------------------- /components/landing/Navbar.tsx: -------------------------------------------------------------------------------- 1 | import { GithubIcon } from "lucide-react"; 2 | 3 | import Link from "next/link"; 4 | import ThemeToggle from "../ThemeToggle/theme-toggle"; 5 | 6 | const Navbar = () => { 7 | return ( 8 |
9 | 30 |
31 | ); 32 | }; 33 | 34 | export default Navbar; 35 | -------------------------------------------------------------------------------- /components/landing/TypeWriteEfx.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { cn } from "@/lib/utils"; 4 | import { motion, stagger, useAnimate, useInView } from "framer-motion"; 5 | import { useEffect } from "react"; 6 | 7 | export const TypeWriteEfx = ({ 8 | words, 9 | className, 10 | cursorClassName, 11 | }: { 12 | words: { 13 | text: string; 14 | className?: string; 15 | }[]; 16 | className?: string; 17 | cursorClassName?: string; 18 | }) => { 19 | // split text inside of words into array of characters 20 | const wordsArray = words.map((word) => { 21 | return { 22 | ...word, 23 | text: word.text.split(""), 24 | }; 25 | }); 26 | 27 | const [scope, animate] = useAnimate(); 28 | const isInView = useInView(scope); 29 | useEffect(() => { 30 | if (isInView) { 31 | animate( 32 | "span", 33 | { 34 | display: "inline-block", 35 | opacity: 1, 36 | }, 37 | { 38 | duration: 0.3, 39 | delay: stagger(0.1), 40 | ease: "easeInOut", 41 | } 42 | ); 43 | } 44 | }, [isInView]); 45 | 46 | const renderWords = () => { 47 | return ( 48 | 49 | {wordsArray.map((word, idx) => { 50 | return ( 51 |
52 | {word.text.map((char, index) => ( 53 | 61 | {char} 62 | 63 | ))} 64 |   65 |
66 | ); 67 | })} 68 |
69 | ); 70 | }; 71 | return ( 72 |
78 | {renderWords()} 79 | 96 |
97 | ); 98 | }; 99 | 100 | export const TypewriterEffectSmooth = ({ 101 | words, 102 | className, 103 | cursorClassName, 104 | }: { 105 | words: { 106 | text: string; 107 | className?: string; 108 | }[]; 109 | className?: string; 110 | cursorClassName?: string; 111 | }) => { 112 | // split text inside of words into array of characters 113 | const wordsArray = words.map((word) => { 114 | return { 115 | ...word, 116 | text: word.text.split(""), 117 | }; 118 | }); 119 | const renderWords = () => { 120 | return ( 121 |
122 | {wordsArray.map((word, idx) => { 123 | return ( 124 |
125 | {word.text.map((char, index) => ( 126 | 130 | {char} 131 | 132 | ))} 133 |   134 |
135 | ); 136 | })} 137 |
138 | ); 139 | }; 140 | 141 | return ( 142 |
143 | 157 |
163 | {renderWords()}{" "} 164 |
{" "} 165 |
166 | 184 |
185 | ); 186 | }; 187 | -------------------------------------------------------------------------------- /components/ui/badge.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { cva, type VariantProps } from "class-variance-authority"; 3 | 4 | import { cn } from "@/lib/utils"; 5 | 6 | const badgeVariants = cva( 7 | "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", 8 | { 9 | variants: { 10 | variant: { 11 | default: 12 | "border-transparent bg-primary text-primary-foreground hover:bg-primary/80", 13 | secondary: 14 | "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", 15 | destructive: 16 | "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80", 17 | outline: "text-foreground", 18 | }, 19 | }, 20 | defaultVariants: { 21 | variant: "default", 22 | }, 23 | } 24 | ); 25 | 26 | export interface BadgeProps 27 | extends React.HTMLAttributes, 28 | VariantProps {} 29 | 30 | function Badge({ className, variant, ...props }: BadgeProps) { 31 | return ( 32 |
33 | ); 34 | } 35 | 36 | export { Badge, badgeVariants }; 37 | -------------------------------------------------------------------------------- /components/ui/button.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { Slot } from "@radix-ui/react-slot"; 3 | import { cva, type VariantProps } from "class-variance-authority"; 4 | 5 | import { cn } from "@/lib/utils"; 6 | 7 | const buttonVariants = cva( 8 | "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", 9 | { 10 | variants: { 11 | variant: { 12 | default: "bg-primary text-primary-foreground hover:bg-primary/90", 13 | destructive: 14 | "bg-destructive text-destructive-foreground hover:bg-destructive/90", 15 | outline: 16 | "border border-input bg-background hover:bg-accent hover:text-accent-foreground", 17 | secondary: 18 | "bg-secondary text-secondary-foreground hover:bg-secondary/80", 19 | ghost: "hover:bg-accent hover:text-accent-foreground", 20 | link: "text-primary underline-offset-4 hover:underline", 21 | }, 22 | size: { 23 | default: "h-10 px-4 py-2", 24 | xs: "h-8 rounded-md px-3", 25 | sm: "h-9 rounded-md px-3", 26 | lg: "h-11 rounded-md px-8", 27 | icon: "h-10 w-10", 28 | iconXs: "h-7 w-7", 29 | }, 30 | }, 31 | defaultVariants: { 32 | variant: "default", 33 | size: "default", 34 | }, 35 | } 36 | ); 37 | 38 | export interface ButtonProps 39 | extends React.ButtonHTMLAttributes, 40 | VariantProps { 41 | asChild?: boolean; 42 | } 43 | 44 | const Button = React.forwardRef( 45 | ({ className, variant, size, asChild = false, ...props }, ref) => { 46 | const Comp = asChild ? Slot : "button"; 47 | return ( 48 | 53 | ); 54 | } 55 | ); 56 | Button.displayName = "Button"; 57 | 58 | export { Button, buttonVariants }; 59 | -------------------------------------------------------------------------------- /components/ui/card.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { cn } from "@/lib/utils"; 4 | 5 | const Card = React.forwardRef< 6 | HTMLDivElement, 7 | React.HTMLAttributes 8 | >(({ className, ...props }, ref) => ( 9 |
17 | )); 18 | Card.displayName = "Card"; 19 | 20 | const CardHeader = React.forwardRef< 21 | HTMLDivElement, 22 | React.HTMLAttributes 23 | >(({ className, ...props }, ref) => ( 24 |
29 | )); 30 | CardHeader.displayName = "CardHeader"; 31 | 32 | const CardTitle = React.forwardRef< 33 | HTMLParagraphElement, 34 | React.HTMLAttributes 35 | >(({ className, ...props }, ref) => ( 36 |

44 | )); 45 | CardTitle.displayName = "CardTitle"; 46 | 47 | const CardDescription = React.forwardRef< 48 | HTMLParagraphElement, 49 | React.HTMLAttributes 50 | >(({ className, ...props }, ref) => ( 51 |

56 | )); 57 | CardDescription.displayName = "CardDescription"; 58 | 59 | const CardContent = React.forwardRef< 60 | HTMLDivElement, 61 | React.HTMLAttributes 62 | >(({ className, ...props }, ref) => ( 63 |

64 | )); 65 | CardContent.displayName = "CardContent"; 66 | 67 | const CardFooter = React.forwardRef< 68 | HTMLDivElement, 69 | React.HTMLAttributes 70 | >(({ className, ...props }, ref) => ( 71 |
76 | )); 77 | CardFooter.displayName = "CardFooter"; 78 | 79 | export { 80 | Card, 81 | CardHeader, 82 | CardFooter, 83 | CardTitle, 84 | CardDescription, 85 | CardContent, 86 | }; 87 | -------------------------------------------------------------------------------- /components/ui/command.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | import { type DialogProps } from "@radix-ui/react-dialog"; 5 | import { Command as CommandPrimitive } from "cmdk"; 6 | import { Search } from "lucide-react"; 7 | 8 | import { cn } from "@/lib/utils"; 9 | import { Dialog, DialogContent } from "@/components/ui/dialog"; 10 | 11 | const Command = React.forwardRef< 12 | React.ElementRef, 13 | React.ComponentPropsWithoutRef 14 | >(({ className, ...props }, ref) => ( 15 | 23 | )); 24 | Command.displayName = CommandPrimitive.displayName; 25 | 26 | interface CommandDialogProps extends DialogProps {} 27 | 28 | const CommandDialog = ({ children, ...props }: CommandDialogProps) => { 29 | return ( 30 | 31 | 32 | 33 | {children} 34 | 35 | 36 | 37 | ); 38 | }; 39 | 40 | const CommandInput = React.forwardRef< 41 | React.ElementRef, 42 | React.ComponentPropsWithoutRef 43 | >(({ className, ...props }, ref) => ( 44 |
45 | 46 | 54 |
55 | )); 56 | 57 | CommandInput.displayName = CommandPrimitive.Input.displayName; 58 | 59 | const CommandList = React.forwardRef< 60 | React.ElementRef, 61 | React.ComponentPropsWithoutRef 62 | >(({ className, ...props }, ref) => ( 63 | 68 | )); 69 | 70 | CommandList.displayName = CommandPrimitive.List.displayName; 71 | 72 | const CommandEmpty = React.forwardRef< 73 | React.ElementRef, 74 | React.ComponentPropsWithoutRef 75 | >((props, ref) => ( 76 | 81 | )); 82 | 83 | CommandEmpty.displayName = CommandPrimitive.Empty.displayName; 84 | 85 | const CommandGroup = React.forwardRef< 86 | React.ElementRef, 87 | React.ComponentPropsWithoutRef 88 | >(({ className, ...props }, ref) => ( 89 | 97 | )); 98 | 99 | CommandGroup.displayName = CommandPrimitive.Group.displayName; 100 | 101 | const CommandSeparator = React.forwardRef< 102 | React.ElementRef, 103 | React.ComponentPropsWithoutRef 104 | >(({ className, ...props }, ref) => ( 105 | 110 | )); 111 | CommandSeparator.displayName = CommandPrimitive.Separator.displayName; 112 | 113 | const CommandItem = React.forwardRef< 114 | React.ElementRef, 115 | React.ComponentPropsWithoutRef 116 | >(({ className, ...props }, ref) => ( 117 | 125 | )); 126 | 127 | CommandItem.displayName = CommandPrimitive.Item.displayName; 128 | 129 | const CommandShortcut = ({ 130 | className, 131 | ...props 132 | }: React.HTMLAttributes) => { 133 | return ( 134 | 141 | ); 142 | }; 143 | CommandShortcut.displayName = "CommandShortcut"; 144 | 145 | export { 146 | Command, 147 | CommandDialog, 148 | CommandInput, 149 | CommandList, 150 | CommandEmpty, 151 | CommandGroup, 152 | CommandItem, 153 | CommandShortcut, 154 | CommandSeparator, 155 | }; 156 | -------------------------------------------------------------------------------- /components/ui/dialog.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | import * as DialogPrimitive from "@radix-ui/react-dialog"; 5 | import { X } from "lucide-react"; 6 | 7 | import { cn } from "@/lib/utils"; 8 | 9 | const Dialog = DialogPrimitive.Root; 10 | 11 | const DialogTrigger = DialogPrimitive.Trigger; 12 | 13 | const DialogPortal = DialogPrimitive.Portal; 14 | 15 | const DialogClose = DialogPrimitive.Close; 16 | 17 | const DialogOverlay = React.forwardRef< 18 | React.ElementRef, 19 | React.ComponentPropsWithoutRef 20 | >(({ className, ...props }, ref) => ( 21 | 29 | )); 30 | DialogOverlay.displayName = DialogPrimitive.Overlay.displayName; 31 | 32 | const DialogContent = React.forwardRef< 33 | React.ElementRef, 34 | React.ComponentPropsWithoutRef 35 | >(({ className, children, ...props }, ref) => ( 36 | 37 | 38 | 46 | {children} 47 | 48 | 49 | Close 50 | 51 | 52 | 53 | )); 54 | DialogContent.displayName = DialogPrimitive.Content.displayName; 55 | 56 | const DialogHeader = ({ 57 | className, 58 | ...props 59 | }: React.HTMLAttributes) => ( 60 |
67 | ); 68 | DialogHeader.displayName = "DialogHeader"; 69 | 70 | const DialogFooter = ({ 71 | className, 72 | ...props 73 | }: React.HTMLAttributes) => ( 74 |
81 | ); 82 | DialogFooter.displayName = "DialogFooter"; 83 | 84 | const DialogTitle = React.forwardRef< 85 | React.ElementRef, 86 | React.ComponentPropsWithoutRef 87 | >(({ className, ...props }, ref) => ( 88 | 96 | )); 97 | DialogTitle.displayName = DialogPrimitive.Title.displayName; 98 | 99 | const DialogDescription = React.forwardRef< 100 | React.ElementRef, 101 | React.ComponentPropsWithoutRef 102 | >(({ className, ...props }, ref) => ( 103 | 108 | )); 109 | DialogDescription.displayName = DialogPrimitive.Description.displayName; 110 | 111 | export { 112 | Dialog, 113 | DialogPortal, 114 | DialogOverlay, 115 | DialogClose, 116 | DialogTrigger, 117 | DialogContent, 118 | DialogHeader, 119 | DialogFooter, 120 | DialogTitle, 121 | DialogDescription, 122 | }; 123 | -------------------------------------------------------------------------------- /components/ui/dropdown-menu.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu" 5 | import { Check, ChevronRight, Circle } from "lucide-react" 6 | 7 | import { cn } from "@/lib/utils" 8 | 9 | const DropdownMenu = DropdownMenuPrimitive.Root 10 | 11 | const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger 12 | 13 | const DropdownMenuGroup = DropdownMenuPrimitive.Group 14 | 15 | const DropdownMenuPortal = DropdownMenuPrimitive.Portal 16 | 17 | const DropdownMenuSub = DropdownMenuPrimitive.Sub 18 | 19 | const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup 20 | 21 | const DropdownMenuSubTrigger = React.forwardRef< 22 | React.ElementRef, 23 | React.ComponentPropsWithoutRef & { 24 | inset?: boolean 25 | } 26 | >(({ className, inset, children, ...props }, ref) => ( 27 | 36 | {children} 37 | 38 | 39 | )) 40 | DropdownMenuSubTrigger.displayName = 41 | DropdownMenuPrimitive.SubTrigger.displayName 42 | 43 | const DropdownMenuSubContent = React.forwardRef< 44 | React.ElementRef, 45 | React.ComponentPropsWithoutRef 46 | >(({ className, ...props }, ref) => ( 47 | 55 | )) 56 | DropdownMenuSubContent.displayName = 57 | DropdownMenuPrimitive.SubContent.displayName 58 | 59 | const DropdownMenuContent = React.forwardRef< 60 | React.ElementRef, 61 | React.ComponentPropsWithoutRef 62 | >(({ className, sideOffset = 4, ...props }, ref) => ( 63 | 64 | 73 | 74 | )) 75 | DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName 76 | 77 | const DropdownMenuItem = React.forwardRef< 78 | React.ElementRef, 79 | React.ComponentPropsWithoutRef & { 80 | inset?: boolean 81 | } 82 | >(({ className, inset, ...props }, ref) => ( 83 | 92 | )) 93 | DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName 94 | 95 | const DropdownMenuCheckboxItem = React.forwardRef< 96 | React.ElementRef, 97 | React.ComponentPropsWithoutRef 98 | >(({ className, children, checked, ...props }, ref) => ( 99 | 108 | 109 | 110 | 111 | 112 | 113 | {children} 114 | 115 | )) 116 | DropdownMenuCheckboxItem.displayName = 117 | DropdownMenuPrimitive.CheckboxItem.displayName 118 | 119 | const DropdownMenuRadioItem = React.forwardRef< 120 | React.ElementRef, 121 | React.ComponentPropsWithoutRef 122 | >(({ className, children, ...props }, ref) => ( 123 | 131 | 132 | 133 | 134 | 135 | 136 | {children} 137 | 138 | )) 139 | DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName 140 | 141 | const DropdownMenuLabel = React.forwardRef< 142 | React.ElementRef, 143 | React.ComponentPropsWithoutRef & { 144 | inset?: boolean 145 | } 146 | >(({ className, inset, ...props }, ref) => ( 147 | 156 | )) 157 | DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName 158 | 159 | const DropdownMenuSeparator = React.forwardRef< 160 | React.ElementRef, 161 | React.ComponentPropsWithoutRef 162 | >(({ className, ...props }, ref) => ( 163 | 168 | )) 169 | DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName 170 | 171 | const DropdownMenuShortcut = ({ 172 | className, 173 | ...props 174 | }: React.HTMLAttributes) => { 175 | return ( 176 | 180 | ) 181 | } 182 | DropdownMenuShortcut.displayName = "DropdownMenuShortcut" 183 | 184 | export { 185 | DropdownMenu, 186 | DropdownMenuTrigger, 187 | DropdownMenuContent, 188 | DropdownMenuItem, 189 | DropdownMenuCheckboxItem, 190 | DropdownMenuRadioItem, 191 | DropdownMenuLabel, 192 | DropdownMenuSeparator, 193 | DropdownMenuShortcut, 194 | DropdownMenuGroup, 195 | DropdownMenuPortal, 196 | DropdownMenuSub, 197 | DropdownMenuSubContent, 198 | DropdownMenuSubTrigger, 199 | DropdownMenuRadioGroup, 200 | } 201 | -------------------------------------------------------------------------------- /components/ui/input.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { Icons } from "../Icons"; 4 | import { LucideIcon } from "lucide-react"; 5 | import { cn } from "@/lib/utils"; 6 | 7 | export interface InputProps 8 | extends React.InputHTMLAttributes { 9 | icon?: keyof typeof Icons; 10 | } 11 | 12 | const Input = React.forwardRef( 13 | ({ className, type, icon, ...props }, ref) => { 14 | const SelectedIcon = (Icons as unknown as { [key: string]: LucideIcon })[ 15 | icon ?? "add" 16 | ]; 17 | return ( 18 |
24 | 25 | 26 | 27 | 28 | 34 |
35 | ); 36 | } 37 | ); 38 | Input.displayName = "Input"; 39 | 40 | export { Input }; 41 | -------------------------------------------------------------------------------- /components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as LabelPrimitive from "@radix-ui/react-label" 5 | import { cva, type VariantProps } from "class-variance-authority" 6 | 7 | import { cn } from "@/lib/utils" 8 | 9 | const labelVariants = cva( 10 | "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" 11 | ) 12 | 13 | const Label = React.forwardRef< 14 | React.ElementRef, 15 | React.ComponentPropsWithoutRef & 16 | VariantProps 17 | >(({ className, ...props }, ref) => ( 18 | 23 | )) 24 | Label.displayName = LabelPrimitive.Root.displayName 25 | 26 | export { Label } 27 | -------------------------------------------------------------------------------- /components/ui/popover.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | import * as PopoverPrimitive from "@radix-ui/react-popover"; 5 | 6 | import { cn } from "@/lib/utils"; 7 | 8 | const Popover = PopoverPrimitive.Root; 9 | 10 | const PopoverTrigger = PopoverPrimitive.Trigger; 11 | 12 | const PopoverContent = React.forwardRef< 13 | React.ElementRef, 14 | React.ComponentPropsWithoutRef 15 | >(({ className, align = "center", sideOffset = 4, ...props }, ref) => ( 16 | 17 | 27 | 28 | )); 29 | PopoverContent.displayName = PopoverPrimitive.Content.displayName; 30 | 31 | export { Popover, PopoverTrigger, PopoverContent }; 32 | -------------------------------------------------------------------------------- /components/ui/resizable.tsx: -------------------------------------------------------------------------------- 1 | import { GripVertical } from "lucide-react"; 2 | import * as ResizablePrimitive from "react-resizable-panels"; 3 | import { cn } from "../../lib/utils"; 4 | 5 | const ResizablePanelGroup = ({ 6 | className, 7 | ...props 8 | }: React.ComponentProps) => ( 9 | 16 | ); 17 | 18 | const ResizablePanel = ResizablePrimitive.Panel; 19 | 20 | const ResizableHandle = ({ 21 | withHandle, 22 | className, 23 | ...props 24 | }: React.ComponentProps & { 25 | withHandle?: boolean; 26 | }) => ( 27 | div]:rotate-90", 30 | className 31 | )} 32 | {...props} 33 | > 34 | {withHandle && ( 35 |
36 | 37 |
38 | )} 39 |
40 | ); 41 | 42 | export { ResizablePanelGroup, ResizablePanel, ResizableHandle }; 43 | -------------------------------------------------------------------------------- /components/ui/select.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as SelectPrimitive from "@radix-ui/react-select" 5 | import { Check, ChevronDown, ChevronUp } from "lucide-react" 6 | 7 | import { cn } from "@/lib/utils" 8 | 9 | const Select = SelectPrimitive.Root 10 | 11 | const SelectGroup = SelectPrimitive.Group 12 | 13 | const SelectValue = SelectPrimitive.Value 14 | 15 | const SelectTrigger = React.forwardRef< 16 | React.ElementRef, 17 | React.ComponentPropsWithoutRef 18 | >(({ className, children, ...props }, ref) => ( 19 | span]:line-clamp-1", 23 | className 24 | )} 25 | {...props} 26 | > 27 | {children} 28 | 29 | 30 | 31 | 32 | )) 33 | SelectTrigger.displayName = SelectPrimitive.Trigger.displayName 34 | 35 | const SelectScrollUpButton = React.forwardRef< 36 | React.ElementRef, 37 | React.ComponentPropsWithoutRef 38 | >(({ className, ...props }, ref) => ( 39 | 47 | 48 | 49 | )) 50 | SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName 51 | 52 | const SelectScrollDownButton = React.forwardRef< 53 | React.ElementRef, 54 | React.ComponentPropsWithoutRef 55 | >(({ className, ...props }, ref) => ( 56 | 64 | 65 | 66 | )) 67 | SelectScrollDownButton.displayName = 68 | SelectPrimitive.ScrollDownButton.displayName 69 | 70 | const SelectContent = React.forwardRef< 71 | React.ElementRef, 72 | React.ComponentPropsWithoutRef 73 | >(({ className, children, position = "popper", ...props }, ref) => ( 74 | 75 | 86 | 87 | 94 | {children} 95 | 96 | 97 | 98 | 99 | )) 100 | SelectContent.displayName = SelectPrimitive.Content.displayName 101 | 102 | const SelectLabel = React.forwardRef< 103 | React.ElementRef, 104 | React.ComponentPropsWithoutRef 105 | >(({ className, ...props }, ref) => ( 106 | 111 | )) 112 | SelectLabel.displayName = SelectPrimitive.Label.displayName 113 | 114 | const SelectItem = React.forwardRef< 115 | React.ElementRef, 116 | React.ComponentPropsWithoutRef 117 | >(({ className, children, ...props }, ref) => ( 118 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | {children} 133 | 134 | )) 135 | SelectItem.displayName = SelectPrimitive.Item.displayName 136 | 137 | const SelectSeparator = React.forwardRef< 138 | React.ElementRef, 139 | React.ComponentPropsWithoutRef 140 | >(({ className, ...props }, ref) => ( 141 | 146 | )) 147 | SelectSeparator.displayName = SelectPrimitive.Separator.displayName 148 | 149 | export { 150 | Select, 151 | SelectGroup, 152 | SelectValue, 153 | SelectTrigger, 154 | SelectContent, 155 | SelectLabel, 156 | SelectItem, 157 | SelectSeparator, 158 | SelectScrollUpButton, 159 | SelectScrollDownButton, 160 | } 161 | -------------------------------------------------------------------------------- /components/ui/separator.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | import * as SeparatorPrimitive from "@radix-ui/react-separator"; 5 | 6 | import { cn } from "@/lib/utils"; 7 | 8 | const Separator = React.forwardRef< 9 | React.ElementRef, 10 | React.ComponentPropsWithoutRef 11 | >( 12 | ( 13 | { className, orientation = "horizontal", decorative = true, ...props }, 14 | ref 15 | ) => ( 16 | 27 | ) 28 | ); 29 | Separator.displayName = SeparatorPrimitive.Root.displayName; 30 | 31 | export { Separator }; 32 | -------------------------------------------------------------------------------- /components/ui/slider.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | import * as SliderPrimitive from "@radix-ui/react-slider"; 5 | 6 | import { cn } from "@/lib/utils"; 7 | 8 | const Slider = React.forwardRef< 9 | React.ElementRef, 10 | React.ComponentPropsWithoutRef 11 | >(({ className, ...props }, ref) => ( 12 | 20 | 21 | 22 | 23 | 24 | 25 | )); 26 | Slider.displayName = SliderPrimitive.Root.displayName; 27 | 28 | export { Slider }; 29 | -------------------------------------------------------------------------------- /components/ui/sonner.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { useTheme } from "next-themes" 4 | import { Toaster as Sonner } from "sonner" 5 | 6 | type ToasterProps = React.ComponentProps 7 | 8 | const Toaster = ({ ...props }: ToasterProps) => { 9 | const { theme = "system" } = useTheme() 10 | 11 | return ( 12 | 28 | ) 29 | } 30 | 31 | export { Toaster } 32 | -------------------------------------------------------------------------------- /components/ui/tabs.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | import * as TabsPrimitive from "@radix-ui/react-tabs"; 5 | 6 | import { cn } from "@/lib/utils"; 7 | 8 | const Tabs = TabsPrimitive.Root; 9 | 10 | const TabsList = React.forwardRef< 11 | React.ElementRef, 12 | React.ComponentPropsWithoutRef 13 | >(({ className, ...props }, ref) => ( 14 | 22 | )); 23 | TabsList.displayName = TabsPrimitive.List.displayName; 24 | 25 | const TabsTrigger = React.forwardRef< 26 | React.ElementRef, 27 | React.ComponentPropsWithoutRef 28 | >(({ className, ...props }, ref) => ( 29 | 37 | )); 38 | TabsTrigger.displayName = TabsPrimitive.Trigger.displayName; 39 | 40 | const TabsContent = React.forwardRef< 41 | React.ElementRef, 42 | React.ComponentPropsWithoutRef 43 | >(({ className, ...props }, ref) => ( 44 | 52 | )); 53 | TabsContent.displayName = TabsPrimitive.Content.displayName; 54 | 55 | export { Tabs, TabsList, TabsTrigger, TabsContent }; 56 | -------------------------------------------------------------------------------- /config.mjs: -------------------------------------------------------------------------------- 1 | import { VERSION } from "remotion/version"; 2 | 3 | /** 4 | * Use autocomplete to get a list of available regions. 5 | * @type {import('@remotion/lambda').AwsRegion} 6 | */ 7 | export const REGION = "us-east-1"; 8 | 9 | export const SITE_NAME = "my-next-app"; 10 | export const RAM = 3009; 11 | export const DISK = 2048; 12 | export const TIMEOUT = 240; 13 | -------------------------------------------------------------------------------- /constants/data.ts: -------------------------------------------------------------------------------- 1 | import * as ABeeZee from "@remotion/google-fonts/ABeeZee"; 2 | import * as Inter from "@remotion/google-fonts/Inter"; 3 | import * as Poppins from "@remotion/google-fonts/Poppins"; 4 | import * as Teko from "@remotion/google-fonts/Teko"; 5 | 6 | ABeeZee.loadFont(); 7 | Inter.loadFont(); 8 | Poppins.loadFont(); 9 | Teko.loadFont(); 10 | 11 | export const fontFamilyOptions = [ 12 | { value: ABeeZee.fontFamily, label: "ABeeZee" }, 13 | { value: Inter.fontFamily, label: "Inter" }, 14 | { value: Poppins.fontFamily, label: "Poppins" }, 15 | { value: Teko.fontFamily, label: "Teko" }, 16 | ]; 17 | -------------------------------------------------------------------------------- /constants/lang.ts: -------------------------------------------------------------------------------- 1 | import { langs } from "@uiw/codemirror-extensions-langs"; 2 | 3 | const js = [langs.javascript({ jsx: true })]; 4 | const python = [langs.python()]; 5 | const css = [langs.css()]; 6 | const html = [ 7 | langs.html({ 8 | autoCloseTags: true, 9 | matchClosingTags: true, 10 | selfClosingTags: true, 11 | }), 12 | ]; 13 | 14 | export const langOptions = [ 15 | { value: "js", label: "Javascript", extensions: js }, 16 | { value: "html", label: "Html", extensions: html }, 17 | { value: "python", label: "Python", extensions: python }, 18 | { value: "css", label: "css", extensions: css }, 19 | ]; 20 | -------------------------------------------------------------------------------- /constants/themes.ts: -------------------------------------------------------------------------------- 1 | import { 2 | nord, 3 | darcula, 4 | nightOwl, 5 | github, 6 | vs2015, 7 | atomOneDark, 8 | androidstudio, 9 | shadesOfPurple, 10 | tomorrowNightBlue, 11 | } from "react-syntax-highlighter/dist/esm/styles/hljs"; 12 | 13 | export const themeOptions = [ 14 | { label: "Nord", value: "nord", theme: nord }, 15 | { label: "Darcula", value: "darcula", theme: darcula }, 16 | { label: "Night Owl", value: "nightOwl", theme: nightOwl }, 17 | { label: "Github", value: "github", theme: github }, 18 | { label: "VS2015", value: "vs2015", theme: vs2015 }, 19 | { label: "Atom One Dark", value: "atomOneDark", theme: atomOneDark }, 20 | { label: "Android Studio", value: "androidstudio", theme: androidstudio }, 21 | { label: "Shades Of Purple", value: "shadesOfPurple", theme: shadesOfPurple }, 22 | { 23 | label: "Tomorrow Night Blue", 24 | value: "tomorrowNightBlue", 25 | theme: tomorrowNightBlue, 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /constants/walls.ts: -------------------------------------------------------------------------------- 1 | export const codeWallOptions = [ 2 | { value: "wallone.jpg", label: "Choose Wallpaper" }, 3 | { value: "walltwo.jpg", label: "Choose Wallpaper" }, 4 | ]; 5 | 6 | export const gradientOptions = [ 7 | { 8 | value: 9 | "linear-gradient(to right, rgb(236, 72, 153), rgb(239, 68, 68), rgb(234, 179, 8))", 10 | lable: "Choose Gradient", 11 | }, 12 | { 13 | value: 14 | "linear-gradient(to right, rgb(134, 239, 172), rgb(59, 130, 246), rgb(147, 51, 234))", 15 | lable: "Choose Gradient", 16 | }, 17 | { 18 | value: 19 | "linear-gradient(to right, rgb(249, 168, 212), rgb(216, 180, 254), rgb(129, 140, 248))", 20 | lable: "Choose Gradient", 21 | }, 22 | { 23 | value: 24 | "linear-gradient(to right, rgb(55, 65, 81), rgb(17, 24, 39), rgb(0, 0, 0))", 25 | lable: "Choose Gradient", 26 | }, 27 | { 28 | value: 29 | "linear-gradient(to right, rgb(199, 210, 254), rgb(254, 202, 202), rgb(254, 249, 195))", 30 | lable: "Choose Gradient", 31 | }, 32 | { 33 | value: 34 | "linear-gradient(to right, rgb(254, 249, 195), rgb(253, 224, 71), rgb(234, 179, 8))", 35 | lable: "Choose Gradient", 36 | }, 37 | { 38 | value: 39 | "linear-gradient(to right, rgb(254, 240, 138), rgb(187, 247, 208), rgb(34, 197, 94))", 40 | lable: "Choose Gradient", 41 | }, 42 | 43 | { 44 | value: 45 | "radial-gradient(at center top, rgb(209, 213, 219), rgb(192, 38, 211), rgb(234, 88, 12))", 46 | lable: "Choose Gradient", 47 | }, 48 | ]; 49 | -------------------------------------------------------------------------------- /deploy.mjs: -------------------------------------------------------------------------------- 1 | import { 2 | deployFunction, 3 | deploySite, 4 | getOrCreateBucket, 5 | } from "@remotion/lambda"; 6 | import dotenv from "dotenv"; 7 | import path from "path"; 8 | import { RAM, REGION, SITE_NAME, TIMEOUT } from "./config.mjs"; 9 | 10 | console.log("Selected region:", REGION); 11 | dotenv.config(); 12 | 13 | if (!process.env.AWS_ACCESS_KEY_ID && !process.env.REMOTION_AWS_ACCESS_KEY_ID) { 14 | console.log( 15 | 'The environment variable "REMOTION_AWS_ACCESS_KEY_ID" is not set.' 16 | ); 17 | console.log("Lambda renders were not set up."); 18 | console.log( 19 | "Complete the Lambda setup: at https://www.remotion.dev/docs/lambda/setup" 20 | ); 21 | process.exit(0); 22 | } 23 | if ( 24 | !process.env.AWS_SECRET_ACCESS_KEY && 25 | !process.env.REMOTION_AWS_SECRET_ACCESS_KEY 26 | ) { 27 | console.log( 28 | 'The environment variable "REMOTION_REMOTION_AWS_SECRET_ACCESS_KEY" is not set.' 29 | ); 30 | console.log("Lambda renders were not set up."); 31 | console.log( 32 | "Complete the Lambda setup: at https://www.remotion.dev/docs/lambda/setup" 33 | ); 34 | process.exit(0); 35 | } 36 | 37 | process.stdout.write("Deploying Lambda function... "); 38 | 39 | const { functionName, alreadyExisted: functionAlreadyExisted } = 40 | await deployFunction({ 41 | createCloudWatchLogGroup: true, 42 | memorySizeInMb: RAM, 43 | region: REGION, 44 | timeoutInSeconds: TIMEOUT, 45 | }); 46 | console.log( 47 | functionName, 48 | functionAlreadyExisted ? "(already existed)" : "(created)" 49 | ); 50 | 51 | process.stdout.write("Ensuring bucket... "); 52 | const { bucketName, alreadyExisted: bucketAlreadyExisted } = 53 | await getOrCreateBucket({ 54 | region: REGION, 55 | }); 56 | console.log( 57 | bucketName, 58 | bucketAlreadyExisted ? "(already existed)" : "(created)" 59 | ); 60 | 61 | process.stdout.write("Deploying site... "); 62 | const { siteName } = await deploySite({ 63 | bucketName, 64 | entryPoint: path.join(process.cwd(), "remotion", "index.ts"), 65 | siteName: SITE_NAME, 66 | region: REGION, 67 | }); 68 | 69 | console.log(siteName); 70 | 71 | console.log(); 72 | console.log("You now have everything you need to render videos!"); 73 | console.log("Re-run this command when:"); 74 | console.log(" 1) you changed the video template"); 75 | console.log(" 2) you changed config.mjs"); 76 | console.log(" 3) you upgraded Remotion to a newer version"); 77 | -------------------------------------------------------------------------------- /helpers/api-response.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from "next/server"; 2 | import { z, ZodType } from "zod"; 3 | 4 | export type ApiResponse = 5 | | { 6 | type: "error"; 7 | message: string; 8 | } 9 | | { 10 | type: "success"; 11 | data: Res; 12 | }; 13 | 14 | export const executeApi = 15 | ( 16 | schema: Req, 17 | handler: (req: Request, body: z.infer) => Promise 18 | ) => 19 | async (req: Request) => { 20 | try { 21 | const payload = await req.json(); 22 | const parsed = schema.parse(payload); 23 | const data = await handler(req, parsed); 24 | return NextResponse.json({ 25 | type: "success", 26 | data: data, 27 | }); 28 | } catch (err) { 29 | return NextResponse.json( 30 | { type: "error", message: (err as Error).message }, 31 | { 32 | status: 500, 33 | } 34 | ); 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /helpers/use-rendering.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { useCallback, useMemo, useState } from "react"; 3 | import { getProgress, renderVideo } from "../lambda/api"; 4 | import { CompositionProps } from "../types/constants"; 5 | 6 | export type State = 7 | | { 8 | status: "init"; 9 | } 10 | | { 11 | status: "invoking"; 12 | } 13 | | { 14 | renderId: string; 15 | bucketName: string; 16 | progress: number; 17 | status: "rendering"; 18 | } 19 | | { 20 | renderId: string | null; 21 | status: "error"; 22 | error: Error; 23 | } 24 | | { 25 | url: string; 26 | size: number; 27 | status: "done"; 28 | }; 29 | 30 | const wait = async (milliSeconds: number) => { 31 | await new Promise((resolve) => { 32 | setTimeout(() => { 33 | resolve(); 34 | }, milliSeconds); 35 | }); 36 | }; 37 | 38 | export const useRendering = ( 39 | id: string, 40 | inputProps: z.infer 41 | ) => { 42 | const [state, setState] = useState({ 43 | status: "init", 44 | }); 45 | 46 | const renderMedia = useCallback(async () => { 47 | setState({ 48 | status: "invoking", 49 | }); 50 | try { 51 | const { renderId, bucketName } = await renderVideo({ id, inputProps }); 52 | setState({ 53 | status: "rendering", 54 | progress: 0, 55 | renderId: renderId, 56 | bucketName: bucketName, 57 | }); 58 | 59 | let pending = true; 60 | 61 | while (pending) { 62 | const result = await getProgress({ 63 | id: renderId, 64 | bucketName: bucketName, 65 | }); 66 | switch (result.type) { 67 | case "error": { 68 | setState({ 69 | status: "error", 70 | renderId: renderId, 71 | error: new Error(result.message), 72 | }); 73 | pending = false; 74 | break; 75 | } 76 | case "done": { 77 | setState({ 78 | size: result.size, 79 | url: result.url, 80 | status: "done", 81 | }); 82 | pending = false; 83 | break; 84 | } 85 | case "progress": { 86 | setState({ 87 | status: "rendering", 88 | bucketName: bucketName, 89 | progress: result.progress, 90 | renderId: renderId, 91 | }); 92 | await wait(1000); 93 | } 94 | } 95 | } 96 | } catch (err) { 97 | setState({ 98 | status: "error", 99 | error: err as Error, 100 | renderId: null, 101 | }); 102 | } 103 | }, [id, inputProps]); 104 | 105 | const undo = useCallback(() => { 106 | setState({ status: "init" }); 107 | }, []); 108 | 109 | return useMemo(() => { 110 | return { 111 | renderMedia, 112 | state, 113 | undo, 114 | }; 115 | }, [renderMedia, state, undo]); 116 | }; 117 | -------------------------------------------------------------------------------- /hooks/useCodeCanvas.tsx: -------------------------------------------------------------------------------- 1 | // Importing create function from the Zustand library 2 | import { fontFamilyOptions } from "@/constants/data"; 3 | import { themeOptions } from "@/constants/themes"; 4 | import { codeWallOptions } from "@/constants/walls"; 5 | import { create } from "zustand"; 6 | import { persist } from "zustand/middleware"; 7 | 8 | export type TCodesnippet = { 9 | id: string; 10 | code: string; 11 | language: string; 12 | editable: boolean; 13 | }; 14 | // Defining an interface for the store's state 15 | type TCodeCanvas = { 16 | fontFamily: string; 17 | setFontFamily: (fontFamily: string) => void; 18 | fontSize: number; 19 | setFontSize: (fontSize: number) => void; 20 | codeTheme: string; 21 | setCodeTheme: (codeTheme: string) => void; 22 | codeWallpaper: string; 23 | setCodeWallpaper: (codeWallpaper: string) => void; 24 | codeSnippet: TCodesnippet[]; 25 | setCodeSnippet: (codeSnippet: TCodesnippet[]) => void; 26 | isEditMode: boolean; 27 | setIsEditMode: (val: boolean) => void; 28 | activeSnippetId: string | null; // Track the ID of the active snippet 29 | setActiveSnippetId: (val: string | null) => void; 30 | setCodeLang: (val: string) => void; 31 | codeLang: string; 32 | innerPadding: number[]; 33 | outerPadding: number[]; 34 | setInnerPadding: (val: number[]) => void; 35 | setOuterPadding: (val: number[]) => void; 36 | }; 37 | 38 | const initialValues: Partial = { 39 | activeSnippetId: "", 40 | codeLang: "", 41 | codeSnippet: [ 42 | { 43 | id: Date.now().toString(), 44 | code: "console.log", 45 | language: "js", 46 | editable: false, 47 | }, 48 | ] as TCodesnippet[], 49 | codeTheme: "", 50 | codeWallpaper: "", 51 | fontFamily: "", 52 | fontSize: 18, 53 | isEditMode: false, 54 | }; 55 | 56 | // create our store 57 | export const useCodeCanvasStore = create()( 58 | persist( 59 | (set) => ({ 60 | fontFamily: fontFamilyOptions[0].value, 61 | setFontFamily: (fontFamily: string) => set({ fontFamily }), 62 | fontSize: 18, 63 | setFontSize: (fontSize: number) => set({ fontSize }), 64 | codeTheme: themeOptions[0].value, 65 | setCodeTheme: (codeTheme: string) => set({ codeTheme }), 66 | codeWallpaper: codeWallOptions[0].value, 67 | setCodeWallpaper: (codeWallpaper: string) => set({ codeWallpaper }), 68 | codeSnippet: [ 69 | { 70 | id: Date.now().toString(), 71 | code: "console.log", 72 | language: "js", 73 | editable: false, 74 | }, 75 | ], 76 | setCodeSnippet: (codeSnippet: TCodesnippet[]) => set({ codeSnippet }), 77 | isEditMode: false, 78 | setIsEditMode: (val) => set({ isEditMode: val }), 79 | activeSnippetId: null, // Track the ID of the active snippet 80 | setActiveSnippetId: (id) => set({ activeSnippetId: id }), 81 | setCodeLang: (codeLang: string) => set({ codeLang }), 82 | codeLang: "", 83 | outerPadding: [40], 84 | setOuterPadding: (outerPadding: number[]) => set({ outerPadding }), 85 | innerPadding: [10], 86 | setInnerPadding: (innerPadding: number[]) => set({ innerPadding }), 87 | 88 | reset: () => { 89 | set(initialValues); 90 | }, 91 | }), 92 | { name: "code-store", skipHydration: true } 93 | ) 94 | ); 95 | -------------------------------------------------------------------------------- /lambda/api.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import type { RenderMediaOnLambdaOutput } from "@remotion/lambda/client"; 3 | import { 4 | ProgressRequest, 5 | ProgressResponse, 6 | RenderRequest, 7 | } from "../types/schema"; 8 | import { CompositionProps } from "../types/constants"; 9 | import { ApiResponse } from "../helpers/api-response"; 10 | 11 | const makeRequest = async ( 12 | endpoint: string, 13 | body: unknown 14 | ): Promise => { 15 | const result = await fetch(endpoint, { 16 | method: "post", 17 | body: JSON.stringify(body), 18 | headers: { 19 | "content-type": "application/json", 20 | }, 21 | }); 22 | const json = (await result.json()) as ApiResponse; 23 | if (json.type === "error") { 24 | throw new Error(json.message); 25 | } 26 | 27 | return json.data; 28 | }; 29 | 30 | export const renderVideo = async ({ 31 | id, 32 | inputProps, 33 | }: { 34 | id: string; 35 | inputProps: z.infer; 36 | }) => { 37 | const body: z.infer = { 38 | id, 39 | inputProps, 40 | }; 41 | 42 | return makeRequest("/api/lambda/render", body); 43 | }; 44 | 45 | export const getProgress = async ({ 46 | id, 47 | bucketName, 48 | }: { 49 | id: string; 50 | bucketName: string; 51 | }) => { 52 | const body: z.infer = { 53 | id, 54 | bucketName, 55 | }; 56 | 57 | return makeRequest("/api/lambda/progress", body); 58 | }; 59 | -------------------------------------------------------------------------------- /lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx" 2 | import { twMerge } from "tailwind-merge" 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)) 6 | } 7 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | swcMinify: true, 5 | images: { 6 | domains: ["codeflowsnippet.vercel.app", "localhost"], 7 | }, 8 | webpack: (config, { webpack, isServer, nextRuntime }) => { 9 | // Workaround for the following issue: 10 | // https://github.com/aws-amplify/amplify-js/issues/11030#issuecomment-1598207365 11 | 12 | // Avoid AWS SDK Node.js require issue 13 | if (isServer && nextRuntime === "nodejs") 14 | config.plugins.push( 15 | new webpack.IgnorePlugin({ resourceRegExp: /^aws-crt$/ }), 16 | new webpack.IgnorePlugin({ 17 | resourceRegExp: /^@aws-sdk\/signature-v4-crt$/, 18 | }) 19 | ); 20 | return config; 21 | }, 22 | }; 23 | 24 | module.exports = nextConfig; 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "remotion-next-example", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "eslint .", 10 | "remotion": "remotion studio", 11 | "render": "remotion render", 12 | "deploy": "node deploy.mjs" 13 | }, 14 | "dependencies": { 15 | "@codemirror/lang-javascript": "^6.2.1", 16 | "@dnd-kit/core": "^6.1.0", 17 | "@dnd-kit/sortable": "^8.0.0", 18 | "@radix-ui/react-dialog": "^1.0.5", 19 | "@radix-ui/react-dropdown-menu": "^2.0.6", 20 | "@radix-ui/react-label": "^2.0.2", 21 | "@radix-ui/react-popover": "^1.0.7", 22 | "@radix-ui/react-select": "^2.0.0", 23 | "@radix-ui/react-separator": "^1.0.3", 24 | "@radix-ui/react-slider": "^1.1.2", 25 | "@radix-ui/react-slot": "^1.0.2", 26 | "@radix-ui/react-tabs": "^1.0.4", 27 | "@remotion/bundler": "^4.0.77", 28 | "@remotion/cli": "^4.0.77", 29 | "@remotion/google-fonts": "^4.0.77", 30 | "@remotion/lambda": "^4.0.77", 31 | "@remotion/paths": "^4.0.77", 32 | "@remotion/player": "^4.0.77", 33 | "@remotion/shapes": "^4.0.77", 34 | "@remotion/transitions": "^4.0.83", 35 | "@uiw/codemirror-extensions-langs": "^4.21.21", 36 | "@uiw/codemirror-theme-andromeda": "^4.21.21", 37 | "@uiw/codemirror-theme-dracula": "^4.21.21", 38 | "@uiw/codemirror-theme-github": "^4.21.21", 39 | "@uiw/codemirror-theme-tokyo-night": "^4.21.21", 40 | "@uiw/codemirror-theme-vscode": "^4.21.21", 41 | "@uiw/react-codemirror": "^4.21.21", 42 | "class-variance-authority": "^0.7.0", 43 | "clsx": "^2.1.0", 44 | "cmdk": "^0.2.0", 45 | "framer-motion": "^10.16.16", 46 | "geist": "^1.2.0", 47 | "lucide-react": "^0.303.0", 48 | "mini-svg-data-uri": "^1.4.4", 49 | "next": "^13.4.13", 50 | "next-themes": "^0.2.1", 51 | "react": "^18.2.0", 52 | "react-dom": "^18.2.0", 53 | "react-resizable-panels": "^1.0.6", 54 | "react-syntax-highlighter": "^15.5.0", 55 | "remotion": "^4.0.77", 56 | "sonner": "^1.3.1", 57 | "tailwind-merge": "^2.2.0", 58 | "tailwindcss-animate": "^1.0.7", 59 | "zod": "^3.21.4", 60 | "zustand": "^4.4.7" 61 | }, 62 | "devDependencies": { 63 | "@remotion/eslint-config": "^4.0.77", 64 | "@remotion/tailwind": "^4.0.83", 65 | "@types/node": "18.7.23", 66 | "@types/react": "18.0.26", 67 | "@types/react-dom": "18.0.6", 68 | "@types/react-syntax-highlighter": "^15.5.11", 69 | "@types/web": "^0.0.61", 70 | "autoprefixer": "^10.4.16", 71 | "dotenv": "^16.0.3", 72 | "eslint": "^8.43.0", 73 | "eslint-config-next": "^13.4.13", 74 | "postcss": "^8.4.32", 75 | "prettier": "^2.8.8", 76 | "tailwindcss": "^3.4.0", 77 | "typescript": "4.8.3" 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /public/bg-placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiranism/codeFlow/c82b085e98145dd81c972f69252083a8a6418cd2/public/bg-placeholder.png -------------------------------------------------------------------------------- /public/wallone.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiranism/codeFlow/c82b085e98145dd81c972f69252083a8a6418cd2/public/wallone.jpg -------------------------------------------------------------------------------- /public/walltwo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kiranism/codeFlow/c82b085e98145dd81c972f69252083a8a6418cd2/public/walltwo.jpg -------------------------------------------------------------------------------- /remotion.config.ts: -------------------------------------------------------------------------------- 1 | // See all configuration options: https://remotion.dev/docs/config 2 | // Each option also is available as a CLI flag: https://remotion.dev/docs/cli 3 | 4 | // Note: When using the Node.JS APIs, the config file doesn't apply. Instead, pass options directly to the APIs 5 | 6 | import { Config } from "@remotion/cli/config"; 7 | import { webpackOverride } from "./remotion/webpackOverride"; 8 | 9 | Config.setVideoImageFormat("jpeg"); 10 | 11 | Config.overrideWebpackConfig(webpackOverride); 12 | -------------------------------------------------------------------------------- /remotion/MyComp/CodeSnap.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { Button } from "@/components/ui/button"; 3 | import { themeOptions } from "@/constants/themes"; 4 | import { useCodeCanvasStore } from "@/hooks/useCodeCanvas"; 5 | import React, { useEffect, useMemo } from "react"; 6 | import SyntaxHighlighter from "react-syntax-highlighter"; 7 | 8 | export const CodeSnap: React.FC<{ title: string }> = ({ title }) => { 9 | const { fontSize, fontFamily, codeTheme, innerPadding, outerPadding } = 10 | useCodeCanvasStore(); 11 | useEffect(() => { 12 | useCodeCanvasStore.persist.rehydrate(); 13 | }, []); 14 | 15 | const titleStyle: React.CSSProperties = useMemo(() => { 16 | return { fontFamily, fontSize, padding: innerPadding[0] * 2 }; 17 | }, [fontSize, fontFamily, innerPadding]); 18 | 19 | const activeThemeFind = 20 | themeOptions?.find( 21 | (val) => val.value.toUpperCase() === codeTheme.toUpperCase() 22 | ) || themeOptions[0]; 23 | const activeTheme = activeThemeFind.theme; 24 | 25 | const themeBackgroundColor = activeTheme.hljs.background; 26 | // theme={tokyoNightInit({ settings: { fontFamily } })} 27 | return ( 28 |
32 |
39 | {/* CodeNation */} 40 |
41 |
45 |
46 | 47 | 53 | {title} 54 | 55 |
56 | ); 57 | }; 58 | -------------------------------------------------------------------------------- /remotion/MyComp/Main.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { TCodesnippet, useCodeCanvasStore } from "@/hooks/useCodeCanvas"; 3 | import { TransitionSeries, linearTiming } from "@remotion/transitions"; 4 | import { fade } from "@remotion/transitions/fade"; 5 | import Image from "next/image"; 6 | import { Fragment, useEffect } from "react"; 7 | import { AbsoluteFill, Easing } from "remotion"; 8 | import { z } from "zod"; 9 | import { CompositionProps } from "../../types/constants"; 10 | import { CodeSnap } from "./CodeSnap"; 11 | 12 | export const Main = ({ data }: z.infer) => { 13 | const { codeWallpaper, codeSnippet } = useCodeCanvasStore(); 14 | 15 | useEffect(() => { 16 | useCodeCanvasStore.persist.rehydrate(); 17 | }, []); 18 | // check for grad 19 | const grad = codeWallpaper?.toLowerCase().includes("gradient"); 20 | const isCodeSnippets = !!codeSnippet.length; 21 | 22 | return ( 23 | 24 | {isCodeSnippets ? ( 25 | grad ? ( 26 | 27 | ) : ( 28 | codeWallpaper && ( 29 | codebg 34 | ) 35 | ) 36 | ) : ( 37 | codebg 42 | )} 43 | 44 | 45 | {Array.isArray(data) ? ( 46 | data?.map((item: Partial, index: number) => ( 47 | 48 | 52 | 53 | 54 | 55 | 62 | 63 | )) 64 | ) : ( 65 | 66 | 67 | 68 | )} 69 | 70 | 71 | ); 72 | }; 73 | -------------------------------------------------------------------------------- /remotion/MyComp/NextLogo.tsx: -------------------------------------------------------------------------------- 1 | import { evolvePath } from "@remotion/paths"; 2 | import React, { useMemo } from "react"; 3 | import { interpolate, spring, useCurrentFrame, useVideoConfig } from "remotion"; 4 | 5 | const mask: React.CSSProperties = { 6 | maskType: "alpha", 7 | }; 8 | 9 | const nStroke = 10 | "M149.508 157.52L69.142 54H54V125.97H66.1136V69.3836L139.999 164.845C143.333 162.614 146.509 160.165 149.508 157.52Z"; 11 | 12 | export const NextLogo: React.FC<{ 13 | outProgress: number; 14 | }> = ({ outProgress }) => { 15 | const { fps } = useVideoConfig(); 16 | const frame = useCurrentFrame(); 17 | 18 | const evolve1 = spring({ 19 | fps, 20 | frame, 21 | config: { 22 | damping: 200, 23 | }, 24 | }); 25 | const evolve2 = spring({ 26 | fps, 27 | frame: frame - 15, 28 | config: { 29 | damping: 200, 30 | }, 31 | }); 32 | const evolve3 = spring({ 33 | fps, 34 | frame: frame - 30, 35 | config: { 36 | damping: 200, 37 | mass: 3, 38 | }, 39 | durationInFrames: 30, 40 | }); 41 | 42 | const style: React.CSSProperties = useMemo(() => { 43 | return { 44 | height: 140, 45 | borderRadius: 70, 46 | scale: String(1 - outProgress), 47 | }; 48 | }, [outProgress]); 49 | 50 | const firstPath = `M 60.0568 54 v 71.97`; 51 | const secondPath = `M 63.47956 56.17496 L 144.7535 161.1825`; 52 | const thirdPath = `M 121 54 L 121 126`; 53 | 54 | const evolution1 = evolvePath(evolve1, firstPath); 55 | const evolution2 = evolvePath(evolve2, secondPath); 56 | const evolution3 = evolvePath( 57 | interpolate(evolve3, [0, 1], [0, 0.7]), 58 | thirdPath 59 | ); 60 | 61 | return ( 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 78 | 84 | 85 | 92 | 93 | 94 | 102 | 103 | 104 | 105 | 113 | 114 | 115 | 116 | 117 | 118 | ); 119 | }; 120 | -------------------------------------------------------------------------------- /remotion/MyComp/Rings.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { AbsoluteFill, interpolateColors, useVideoConfig } from "remotion"; 3 | 4 | const RadialGradient: React.FC<{ 5 | radius: number; 6 | color: string; 7 | }> = ({ radius, color }) => { 8 | const height = radius * 2; 9 | const width = radius * 2; 10 | 11 | return ( 12 | 18 |
28 |
29 | ); 30 | }; 31 | 32 | export const Rings: React.FC<{ 33 | outProgress: number; 34 | }> = ({ outProgress }) => { 35 | const scale = 1 / (1 - outProgress); 36 | const { height } = useVideoConfig(); 37 | 38 | return ( 39 | 44 | {new Array(5) 45 | .fill(true) 46 | .map((_, i) => { 47 | return ( 48 | 53 | ); 54 | }) 55 | .reverse()} 56 | 57 | ); 58 | }; 59 | -------------------------------------------------------------------------------- /remotion/MyComp/TextFade.tsx: -------------------------------------------------------------------------------- 1 | import React, { useMemo } from "react"; 2 | import { AbsoluteFill } from "remotion"; 3 | 4 | const outer: React.CSSProperties = {}; 5 | 6 | export const TextFade: React.FC<{ 7 | children: React.ReactNode; 8 | }> = ({ children }) => { 9 | const container: React.CSSProperties = useMemo(() => { 10 | return { 11 | justifyContent: "center", 12 | alignItems: "center", 13 | }; 14 | }, []); 15 | 16 | return ( 17 | 18 | 19 |
{children}
20 |
21 |
22 | ); 23 | }; 24 | -------------------------------------------------------------------------------- /remotion/Root.tsx: -------------------------------------------------------------------------------- 1 | import { Composition } from "remotion"; 2 | import { Main } from "./MyComp/Main"; 3 | import { 4 | COMP_NAME, 5 | defaultMyCompProps, 6 | DURATION_IN_FRAMES, 7 | VIDEO_FPS, 8 | VIDEO_HEIGHT, 9 | VIDEO_WIDTH, 10 | } from "../types/constants"; 11 | import { NextLogo } from "./MyComp/NextLogo"; 12 | 13 | export const RemotionRoot: React.FC = () => { 14 | return ( 15 | <> 16 | 25 |