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
Download video ;
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 |
82 |
86 |
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 |
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 |
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 |
38 | Render video
39 |
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 |
19 |
20 |
21 | Toggle theme
22 |
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 |
104 |
105 |
106 |
107 |
108 | {isEditActive && (
109 |
110 | {
115 | setIsEditMode(false);
116 | setActiveSnippetId(null);
117 | }}
118 | >
119 | Cancel Edit
120 |
121 |
122 | )}
123 |
124 |
125 |
126 |
127 |
128 | Edit
129 |
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 |
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 |
10 |
11 | {/* */}
12 |
13 |
14 | CodeFLow
15 |
16 |
17 |
29 |
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 |
42 |
43 |
44 |
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 |
34 | )
35 | )
36 | ) : (
37 |
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 |
36 | >
37 | );
38 | };
39 |
--------------------------------------------------------------------------------
/remotion/index.ts:
--------------------------------------------------------------------------------
1 | import { registerRoot } from "remotion";
2 | import { RemotionRoot } from "./Root";
3 | import "@/app/globals.css";
4 |
5 | registerRoot(RemotionRoot);
6 |
--------------------------------------------------------------------------------
/remotion/webpackOverride.ts:
--------------------------------------------------------------------------------
1 | import { WebpackOverrideFn } from "@remotion/cli/config";
2 | import { enableTailwind } from "@remotion/tailwind";
3 |
4 | export const webpackOverride: WebpackOverrideFn = (currentConfiguration) => {
5 | return enableTailwind({
6 | ...currentConfiguration,
7 | resolve: {
8 | ...currentConfiguration.resolve,
9 | alias: {
10 | ...(currentConfiguration.resolve?.alias ?? {}),
11 | "@": process.cwd(),
12 | },
13 | },
14 | });
15 | };
16 |
--------------------------------------------------------------------------------
/styles/global.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --background: #fff;
3 | --foreground: #000;
4 | --unfocused-border-color: #eaeaea;
5 | --focused-border-color: #666;
6 |
7 | --button-disabled-color: #fafafa;
8 | --disabled-text-color: #999;
9 |
10 | --geist-border-radius: 5px;
11 | --geist-quarter-pad: 6px;
12 | --geist-half-pad: 12px;
13 | --geist-pad: 24px;
14 | --geist-font: "Inter";
15 |
16 | --geist-error: #e00;
17 |
18 | --subtitle: #666;
19 | }
20 |
21 | * {
22 | box-sizing: border-box;
23 | }
24 |
25 | .cinematics {
26 | box-shadow: 0 0 200px rgba(0, 0, 0, 0.15);
27 | }
28 |
29 | @media (prefers-color-scheme: dark) {
30 | :root {
31 | --background: #000000;
32 | --unfocused-border-color: #333;
33 | --focused-border-color: #888;
34 | --foreground: #fff;
35 | --button-disabled-color: #111;
36 | --geist-error: red;
37 | --subtitle: #8D8D8D
38 |
39 | }
40 | .cinematics {
41 | box-shadow: 0 0 200px rgba(255, 255, 255, 0.15);
42 | }
43 | }
44 |
45 | body {
46 | background-color: var(--background);
47 | }
48 |
49 |
50 | input {
51 | border: 1px solid var(--unfocused-border-color);
52 | transition: border-color 0.15s ease;
53 | outline: none;
54 | }
55 |
56 | input:focus {
57 | border-color: var(--focused-border-color);
58 | }
59 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | darkMode: ["class"],
4 | content: [
5 | "./pages/**/*.{ts,tsx}",
6 | "./components/**/*.{ts,tsx}",
7 | "./app/**/*.{ts,tsx}",
8 | "./src/**/*.{ts,tsx}",
9 | "./remotion/**/*.{js,ts,tsx,mdx}",
10 | ],
11 | prefix: "",
12 | theme: {
13 | container: {
14 | center: true,
15 | padding: "2rem",
16 | screens: {
17 | "2xl": "1400px",
18 | },
19 | },
20 | extend: {
21 | colors: {
22 | border: "hsl(var(--border))",
23 | input: "hsl(var(--input))",
24 | ring: "hsl(var(--ring))",
25 | background: "hsl(var(--background))",
26 | foreground: "hsl(var(--foreground))",
27 | primary: {
28 | DEFAULT: "hsl(var(--primary))",
29 | foreground: "hsl(var(--primary-foreground))",
30 | },
31 | secondary: {
32 | DEFAULT: "hsl(var(--secondary))",
33 | foreground: "hsl(var(--secondary-foreground))",
34 | },
35 | destructive: {
36 | DEFAULT: "hsl(var(--destructive))",
37 | foreground: "hsl(var(--destructive-foreground))",
38 | },
39 | muted: {
40 | DEFAULT: "hsl(var(--muted))",
41 | foreground: "hsl(var(--muted-foreground))",
42 | },
43 | accent: {
44 | DEFAULT: "hsl(var(--accent))",
45 | foreground: "hsl(var(--accent-foreground))",
46 | },
47 | popover: {
48 | DEFAULT: "hsl(var(--popover))",
49 | foreground: "hsl(var(--popover-foreground))",
50 | },
51 | card: {
52 | DEFAULT: "hsl(var(--card))",
53 | foreground: "hsl(var(--card-foreground))",
54 | },
55 | },
56 | borderRadius: {
57 | lg: "var(--radius)",
58 | md: "calc(var(--radius) - 2px)",
59 | sm: "calc(var(--radius) - 4px)",
60 | },
61 | keyframes: {
62 | "accordion-down": {
63 | from: { height: "0" },
64 | to: { height: "var(--radix-accordion-content-height)" },
65 | },
66 | "accordion-up": {
67 | from: { height: "var(--radix-accordion-content-height)" },
68 | to: { height: "0" },
69 | },
70 | },
71 | animation: {
72 | "accordion-down": "accordion-down 0.2s ease-out",
73 | "accordion-up": "accordion-up 0.2s ease-out",
74 | },
75 | },
76 | },
77 | plugins: [require("tailwindcss-animate")],
78 | };
79 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "forceConsistentCasingInFileNames": true,
9 | "noEmit": true,
10 | "esModuleInterop": true,
11 | "module": "esnext",
12 | "moduleResolution": "node",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "jsx": "preserve",
16 | "incremental": true,
17 | "noUnusedLocals": true,
18 | "plugins": [
19 | {
20 | "name": "next"
21 | }
22 | ],
23 | "paths": {
24 | "@/*": ["./*"]
25 | }
26 | },
27 |
28 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
29 | "exclude": ["node_modules"]
30 | }
31 |
--------------------------------------------------------------------------------
/types/constants.ts:
--------------------------------------------------------------------------------
1 | import { z } from "zod";
2 | export const COMP_NAME = "MyComp";
3 |
4 | export const CompositionProps = z.object({
5 | data: z.union([
6 | z.string(),
7 | z.array(
8 | z.object({
9 | id: z.string().optional(),
10 | code: z.string().optional(),
11 | language: z.string().optional(),
12 | })
13 | ),
14 | ]),
15 | });
16 |
17 | export const defaultMyCompProps: z.infer = {
18 | data: `
19 | `,
33 | };
34 |
35 | export const DURATION_IN_FRAMES = 100;
36 | export const VIDEO_WIDTH = 1280;
37 | export const VIDEO_HEIGHT = 720;
38 | export const VIDEO_FPS = 30;
39 |
--------------------------------------------------------------------------------
/types/schema.ts:
--------------------------------------------------------------------------------
1 | import { z } from "zod";
2 | import { CompositionProps } from "./constants";
3 |
4 | export const RenderRequest = z.object({
5 | id: z.string(),
6 | inputProps: CompositionProps,
7 | });
8 |
9 | export const ProgressRequest = z.object({
10 | bucketName: z.string(),
11 | id: z.string(),
12 | });
13 |
14 | export type ProgressResponse =
15 | | {
16 | type: "error";
17 | message: string;
18 | }
19 | | {
20 | type: "progress";
21 | progress: number;
22 | }
23 | | {
24 | type: "done";
25 | url: string;
26 | size: number;
27 | };
28 |
--------------------------------------------------------------------------------
/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "buildCommand": "node deploy.mjs && next build"
3 | }
4 |
--------------------------------------------------------------------------------