enzostvs HF Staff commited on
Commit
67ea2ab
·
0 Parent(s):

initial commit

Browse files
.eslintrc.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ {
2
+ "extends": ["next/core-web-vitals", "next/typescript"]
3
+ }
.gitignore ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ .yarn/install-state.gz
8
+
9
+ # testing
10
+ /coverage
11
+
12
+ # next.js
13
+ /.next/
14
+ /out/
15
+
16
+ # production
17
+ /build
18
+
19
+ # misc
20
+ .DS_Store
21
+ *.pem
22
+
23
+ # debug
24
+ npm-debug.log*
25
+ yarn-debug.log*
26
+ yarn-error.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
+
38
+ uploads/*
39
+ prisma/dev.db
40
+ .env
Dockerfile ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Dockerfile
2
+
3
+ # Use an official Node.js runtime as the base image
4
+ FROM node:18
5
+
6
+ USER 1000
7
+
8
+ # Set the working directory in the container
9
+ WORKDIR /usr/src/app
10
+
11
+ # Copy package.json and package-lock.json to the container
12
+ COPY --chown=1000 package.json package-lock.json ./
13
+
14
+ # Install dependencies
15
+ RUN npm install
16
+
17
+ VOLUME /data
18
+
19
+ # Copy the rest of the application files to the container
20
+ COPY --chown=1000 . .
21
+ RUN chmod +x entrypoint.sh
22
+
23
+ # Build the Next.js application for production
24
+ # RUN npm run build
25
+
26
+ # Expose the application port (assuming your app runs on port 3000)
27
+ EXPOSE 3000
28
+
29
+ RUN npx prisma generate
30
+
31
+ # Start the application
32
+ ENTRYPOINT ["/usr/src/app/entrypoint.sh"]
README.md ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Logo.Ai
3
+ emoji: 🍋
4
+ colorFrom: gray
5
+ colorTo: gray
6
+ sdk: docker
7
+ pinned: true
8
+ app_port: 3000
9
+ short_description: Generate a Logo in 2 clicks
10
+ license: mit
11
+ ---
12
+
13
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
_types/index.ts ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ export interface Form {
2
+ brand_name: string;
3
+ description: string;
4
+ display_name: boolean;
5
+ industry: string;
6
+ style: string;
7
+ }
_utils/index.ts ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { PiIslandFill } from "react-icons/pi";
2
+ import { MdSportsVolleyball } from "react-icons/md";
3
+ import { RiShoppingCart2Fill } from "react-icons/ri";
4
+ import { MdRealEstateAgent } from "react-icons/md";
5
+ import { TbWorld } from "react-icons/tb";
6
+ import { GrTechnology } from "react-icons/gr";
7
+ import { BiSolidParty } from "react-icons/bi";
8
+ import { IoMdMedical } from "react-icons/io";
9
+ import { MdRestaurant } from "react-icons/md";
10
+ import { FaPiggyBank } from "react-icons/fa6";
11
+ import { MdMovieFilter } from "react-icons/md";
12
+ import { IoBuildSharp } from "react-icons/io5";
13
+ import { MdSchool } from "react-icons/md";
14
+ import { GiLipstick } from "react-icons/gi";
15
+ import { FaCar } from "react-icons/fa";
16
+ import { MdPets } from "react-icons/md";
17
+ import { FaPaintBrush } from "react-icons/fa";
18
+ import { IoMusicalNotes } from "react-icons/io5";
19
+ import { MdPhotoCamera } from "react-icons/md";
20
+
21
+ export const INDUSTRIES = [
22
+ {
23
+ name: "Travel",
24
+ icon: PiIslandFill,
25
+ },
26
+ {
27
+ name: "Sports Fitness",
28
+ icon: MdSportsVolleyball,
29
+ },
30
+ {
31
+ name: "Retail",
32
+ icon: RiShoppingCart2Fill,
33
+ },
34
+ {
35
+ name: "Real Estate",
36
+ icon: MdRealEstateAgent,
37
+ },
38
+ {
39
+ name: "Internet",
40
+ icon: TbWorld,
41
+ },
42
+ {
43
+ name: "Technology",
44
+ icon: GrTechnology,
45
+ },
46
+ {
47
+ name: "Events",
48
+ icon: BiSolidParty,
49
+ },
50
+ {
51
+ name: "Medical",
52
+ icon: IoMdMedical,
53
+ },
54
+ {
55
+ name: "Restaurant",
56
+ icon: MdRestaurant,
57
+ },
58
+ {
59
+ name: "Finance",
60
+ icon: FaPiggyBank,
61
+ },
62
+ {
63
+ name: "Entertainment",
64
+ icon: MdMovieFilter,
65
+ },
66
+ {
67
+ name: "Construction",
68
+ icon: IoBuildSharp,
69
+ },
70
+ {
71
+ name: "Education",
72
+ icon: MdSchool,
73
+ },
74
+ {
75
+ name: "Beauty",
76
+ icon: GiLipstick,
77
+ },
78
+ {
79
+ name: "Automotive",
80
+ icon: FaCar,
81
+ },
82
+ {
83
+ name: "Animal Pets",
84
+ icon: MdPets,
85
+ },
86
+ {
87
+ name: "Art",
88
+ icon: FaPaintBrush,
89
+ },
90
+ {
91
+ name: "Music",
92
+ icon: IoMusicalNotes,
93
+ },
94
+ {
95
+ name: "Photography",
96
+ icon: MdPhotoCamera,
97
+ },
98
+ {
99
+ name: "Other",
100
+ },
101
+ ];
102
+
103
+ export const THEMES = [
104
+ {
105
+ name: "Warm",
106
+ },
107
+ {
108
+ name: "Pastel",
109
+ },
110
+ {
111
+ name: "Cold",
112
+ },
113
+ {
114
+ name: "Contrast",
115
+ },
116
+ {
117
+ name: "Greyscale",
118
+ },
119
+ {
120
+ name: "Gradient",
121
+ },
122
+ ];
123
+
124
+ export const arrayBufferToBase64 = (buffer: ArrayBuffer) => {
125
+ let binary = "";
126
+ const bytes = new Uint8Array(buffer);
127
+ const len = bytes.byteLength;
128
+ for (let i = 0; i < len; i++) {
129
+ binary += String.fromCharCode(bytes[i]);
130
+ }
131
+ return window.btoa(binary);
132
+ };
_utils/prisma.ts ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ import { PrismaClient } from "@prisma/client";
2
+
3
+ const prisma = new PrismaClient();
4
+ export default prisma;
app/_actions/generate.ts ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use server";
2
+
3
+ import { HfInference } from "@huggingface/inference";
4
+ import fs from "fs/promises";
5
+
6
+ import { Form } from "@/_types";
7
+ import prisma from "@/_utils/prisma";
8
+
9
+ export async function generate({ brand_name, industry, description }: Form) {
10
+ if (!process.env.PUBLIC_FILE_UPLOAD_DIR) {
11
+ throw new Error("PUBLIC_FILE_UPLOAD_DIR is not set");
12
+ }
13
+ const inference = new HfInference(process.env.HF_ACCESS_TOKEN);
14
+
15
+ const prompt = await inference.chatCompletion({
16
+ model: "meta-llama/Meta-Llama-3.1-70B-Instruct",
17
+ messages: [
18
+ { role: "user", content: "lee, a noodle restaurant" },
19
+ {
20
+ role: "assistant",
21
+ content:
22
+ 'logo,Minimalist,A pair of chopsticks and a bowl of rice with the word "Lee",',
23
+ },
24
+ { role: "user", content: "cat shop" },
25
+ { role: "assistant", content: "wablogo,Minimalist,Leaf and cat,logo," },
26
+ { role: "user", content: "Ato, real estate company" },
27
+ {
28
+ role: "assistant",
29
+ content:
30
+ 'logo,Minimalist,A man stands in front of a door,his shadow forming the word "A",',
31
+ },
32
+ { role: "user", content: `${brand_name}, ${description}, ${industry}` },
33
+ ],
34
+ temperature: 0.5,
35
+ max_tokens: 1024,
36
+ top_p: 0.7,
37
+ });
38
+
39
+ if (prompt?.choices[0]?.message?.content) {
40
+ const hfRequest = await inference.textToImage({
41
+ inputs: prompt.choices[0].message.content,
42
+ model: "Shakker-Labs/FLUX.1-dev-LoRA-Logo-Design",
43
+ parameters: {
44
+ num_inference_steps: 24,
45
+ guidance_scale: 3.5,
46
+ },
47
+ });
48
+
49
+ const buffer = await hfRequest.arrayBuffer();
50
+ const array = new Uint8Array(buffer);
51
+
52
+ const newImage = await prisma.logo.create({
53
+ data: {
54
+ name: prompt.choices[0].message.content,
55
+ },
56
+ });
57
+
58
+ const indexFile = newImage.id;
59
+
60
+ const dir = await fs
61
+ .opendir(process.env.PUBLIC_FILE_UPLOAD_DIR)
62
+ .catch(() => null);
63
+ if (!dir) await fs.mkdir(process.env.PUBLIC_FILE_UPLOAD_DIR);
64
+ await fs.writeFile(
65
+ `${process.env.PUBLIC_FILE_UPLOAD_DIR}/${indexFile}.png`,
66
+ array
67
+ );
68
+
69
+ return { data: indexFile };
70
+ }
71
+
72
+ return {
73
+ error: "Failed to generate logo",
74
+ };
75
+ }
app/_components/gallery/index.tsx ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export const Gallery = ({ logos }: { logos: Array<number> }) => {
2
+ return (
3
+ <section id="gallery" className="w-full py-16">
4
+ <div className="mx-auto bg-amber-500/10 border border-amber-500/15 text-amber-500 px-3 py-1.5 rounded-full flex items-center gap-1 justify-center max-w-max text-xs mb-4">
5
+ <span className="text-xs">⚡</span>
6
+ Increase your creativity
7
+ </div>
8
+ <h3 className="max-w-4xl text-3xl text-[#aaaaaa] font-semibold mb-12 text-center mx-auto">
9
+ See our <span className="text-white">last designs</span>.
10
+ </h3>
11
+ <div className="flex items-start justify-center gap-6 flex-wrap">
12
+ {logos.map((index) => (
13
+ <img
14
+ key={index}
15
+ src={`/api/images/${index}`}
16
+ alt="Generated logo"
17
+ className="rounded-2xl w-72 h-52 object-cover"
18
+ />
19
+ ))}
20
+ </div>
21
+ </section>
22
+ );
23
+ };
app/_components/generation/index.tsx ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+
5
+ import { Form } from "@/_types";
6
+ import { generate } from "@/app/_actions/generate";
7
+
8
+ import { Brand } from "./step/brand";
9
+ import { Steps } from "./step/list";
10
+ import { Industry } from "./step/industry";
11
+ import { Description } from "./step/description";
12
+ import classNames from "classnames";
13
+
14
+ export const Generation = () => {
15
+ const [form, setForm] = useState<Form>({
16
+ brand_name: "",
17
+ display_name: false,
18
+ description: "",
19
+ industry: "",
20
+ style: "",
21
+ });
22
+
23
+ const [step, setStep] = useState<number>(0);
24
+ const [loading, setLoading] = useState<boolean>(false);
25
+ const [result, setResult] = useState<number | undefined>(undefined);
26
+
27
+ const handleGenerate = async () => {
28
+ if (loading) return;
29
+ setLoading(true);
30
+ const response = await generate(form);
31
+ if (response.data) {
32
+ setResult(response.data);
33
+ } else {
34
+ console.log("Error: ", response.error);
35
+ }
36
+ setLoading(false);
37
+ };
38
+
39
+ return (
40
+ <main id="generation" className="w-full py-20">
41
+ <h3 className="max-w-4xl text-3xl text-[#aaaaaa] font-semibold mb-12 text-center mx-auto">
42
+ Start your <span className="text-white">generation</span> here.
43
+ </h3>
44
+ <Steps step={step} form={form} />
45
+ <div className="grid grid-cols-3 gap-20">
46
+ <Brand form={form} setForm={setForm} />
47
+ <Description form={form} setForm={setForm} />
48
+ <Industry form={form} setForm={setForm} />
49
+ <div className="col-span-3 flex items-center justify-center">
50
+ <button
51
+ className={classNames(
52
+ "rounded-full bg-white text-zinc-950 font-medium text-sm px-6 py-3 hover:bg-opacity-80 transition-all duration-150 disabled:bg-zinc-500 disabled:text-zinc-700",
53
+ {
54
+ "animate-pulse": loading,
55
+ }
56
+ )}
57
+ disabled={!form.brand_name || !form.description || !form.industry}
58
+ onClick={handleGenerate}
59
+ >
60
+ {loading ? "Generating..." : "Generate my Logo"}
61
+ </button>
62
+ </div>
63
+ {result && (
64
+ <div className="col-span-3 flex items-center justify-center">
65
+ <img
66
+ src={`/api/images/${result}`}
67
+ alt="Generated logo"
68
+ className="h-[300px]"
69
+ />
70
+ </div>
71
+ )}
72
+ </div>
73
+ </main>
74
+ );
75
+ };
app/_components/generation/step/brand.tsx ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Form } from "@/_types";
2
+
3
+ export const Brand = ({
4
+ form,
5
+ setForm,
6
+ }: {
7
+ form: Form;
8
+ setForm: React.Dispatch<React.SetStateAction<Form>>;
9
+ }) => {
10
+ return (
11
+ <div className="">
12
+ <label htmlFor="brand_name" className="text-zinc-300 mb-1 block text-sm">
13
+ Brand name
14
+ </label>
15
+ <input
16
+ type="text"
17
+ id="brand_name"
18
+ placeholder="Hugging Face"
19
+ value={form.brand_name}
20
+ className="border bg-zinc-900 border-zinc-800 rounded-lg py-2 px-4 text-gray-200 outline-none w-full placeholder:text-gray-600"
21
+ onChange={(e) => setForm({ ...form, brand_name: e.target.value })}
22
+ />
23
+ </div>
24
+ );
25
+ };
app/_components/generation/step/description.tsx ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Form } from "@/_types";
2
+
3
+ export const Description = ({
4
+ form,
5
+ setForm,
6
+ }: {
7
+ form: Form;
8
+ setForm: React.Dispatch<React.SetStateAction<Form>>;
9
+ }) => {
10
+ return (
11
+ <div className="">
12
+ <label htmlFor="description" className="text-zinc-300 mb-1 block text-sm">
13
+ Short Description
14
+ </label>
15
+ <input
16
+ type="text"
17
+ id="description"
18
+ placeholder="A platform for building and sharing models"
19
+ value={form.description}
20
+ className="border bg-zinc-900 border-zinc-800 rounded-lg py-2 px-4 text-gray-200 outline-none w-full placeholder:text-gray-600"
21
+ onChange={(e) => setForm({ ...form, description: e.target.value })}
22
+ />
23
+ </div>
24
+ );
25
+ };
app/_components/generation/step/industry.tsx ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Form } from "@/_types";
2
+
3
+ import { INDUSTRIES } from "@/_utils";
4
+
5
+ export const Industry = ({
6
+ form,
7
+ setForm,
8
+ }: {
9
+ form: Form;
10
+ setForm: React.Dispatch<React.SetStateAction<Form>>;
11
+ }) => {
12
+ return (
13
+ <div className="">
14
+ <label htmlFor="industry" className="text-zinc-300 mb-1 block text-sm">
15
+ Industry
16
+ </label>
17
+ <select
18
+ id="industry"
19
+ className="border bg-zinc-900 border-zinc-800 rounded-lg py-3 px-4 text-gray-200 outline-none w-full"
20
+ onChange={(e) => setForm({ ...form, industry: e.target.value })}
21
+ >
22
+ <option value="">Select an industry</option>
23
+ {INDUSTRIES.map((industry, i) => (
24
+ <option key={i} value={industry.name}>
25
+ {/* <industry.icon className="mr-2 inline-block" /> */}
26
+ {industry.name}
27
+ </option>
28
+ ))}
29
+ </select>
30
+ </div>
31
+ );
32
+ };
app/_components/generation/step/list.tsx ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { TiLightbulb } from "react-icons/ti";
2
+ import { MdWorkOutline } from "react-icons/md";
3
+ import { IoMdCheckmark } from "react-icons/io";
4
+ import { MdOutlinePermIdentity } from "react-icons/md";
5
+ import classNames from "classnames";
6
+
7
+ import { Form } from "@/_types";
8
+
9
+ const STEPS = [
10
+ {
11
+ title: "Brand",
12
+ description: "Tell us about your brand.",
13
+ icon: MdOutlinePermIdentity,
14
+ active: "bg-amber-500 !border-amber-500",
15
+ key: "brand_name",
16
+ },
17
+ {
18
+ title: "Concept",
19
+ description: "What's your brand about?",
20
+ icon: TiLightbulb,
21
+ active: "bg-violet-500 !border-violet-500",
22
+ key: "description",
23
+ },
24
+ {
25
+ title: "Industry",
26
+ description: "What industry are you in?",
27
+ icon: MdWorkOutline,
28
+ active: "bg-emerald-500 !border-emerald-500",
29
+ key: "industry",
30
+ },
31
+ ];
32
+
33
+ export const Steps = ({ step, form }: { step: number; form: Form }) => {
34
+ return (
35
+ <div className="w-full flex items-center justify-center gap-2 mb-12">
36
+ {STEPS.map((s, i) => (
37
+ <>
38
+ <div
39
+ key={i}
40
+ className={classNames(
41
+ "text-center flex flex-col items-center min-w-60 cursor-pointer",
42
+ {
43
+ "opacity-40": form[s.key as keyof typeof form] === "",
44
+ }
45
+ )}
46
+ >
47
+ <div
48
+ className={classNames(
49
+ "size-7 border border-white text-white flex items-center justify-center rounded-2xl mb-3",
50
+ {
51
+ [s.active]: form[s.key as keyof typeof form],
52
+ }
53
+ )}
54
+ >
55
+ {form[s.key as keyof typeof form] ? (
56
+ <IoMdCheckmark className="text-xl" />
57
+ ) : (
58
+ <s.icon className="text-base" />
59
+ )}
60
+ </div>
61
+ <p className="text-white text-xl font-semibold">{s.title}</p>
62
+ <p className="text-white text-sm font-mono">{s.description}</p>
63
+ </div>
64
+ {i !== STEPS.length - 1 && (
65
+ <div
66
+ key={"linear_" + i}
67
+ className="h-0 flex-1 border-gray-100/20 border-dashed border-b"
68
+ />
69
+ )}
70
+ </>
71
+ ))}
72
+ </div>
73
+ );
74
+ };
app/_components/hero-header.tsx ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client";
2
+
3
+ import classNames from "classnames";
4
+ import { useState } from "react";
5
+ import { useInterval } from "react-use";
6
+
7
+ export const HeroHeader = () => {
8
+ const [selectedWord, setSelectedWord] = useState("Think.");
9
+
10
+ useInterval(() => {
11
+ setSelectedWord((prev) => {
12
+ switch (prev) {
13
+ case "Think.":
14
+ return "Customize.";
15
+ case "Customize.":
16
+ return "Generate.";
17
+ case "Generate.":
18
+ return "Think.";
19
+ default:
20
+ return "Think.";
21
+ }
22
+ });
23
+ }, 2000);
24
+
25
+ return (
26
+ <header className="py-20 grid grid-cols-1 gap-5">
27
+ <h1 className="text-5xl font-semibold text-[#aaaaaa] max-w-max mx-auto text-center">
28
+ <span
29
+ className={classNames("transition-all duration-300 text-opacity-0", {
30
+ "text-white !text-opacity-100": selectedWord === "Think.",
31
+ })}
32
+ >
33
+ Think.
34
+ </span>{" "}
35
+ <span
36
+ className={classNames("transition-all duration-300 text-opacity-0", {
37
+ "text-white !text-opacity-100": selectedWord === "Customize.",
38
+ })}
39
+ >
40
+ Customize.
41
+ </span>{" "}
42
+ <br />
43
+ and{" "}
44
+ <span
45
+ className={classNames("transition-all duration-300 text-opacity-0", {
46
+ "text-white !text-opacity-100": selectedWord === "Generate.",
47
+ })}
48
+ >
49
+ Generate.
50
+ </span>
51
+ </h1>
52
+ <h2 className="text-lg font-light text-center text-[#898989] max-w-sm mx-auto">
53
+ An AI powered tool that helps you create a logo for your brand.
54
+ </h2>
55
+ <div className="flex items-center justify-center gap-6 mt-3">
56
+ <a
57
+ href="#generation"
58
+ className="rounded-full bg-white text-zinc-950 font-medium text-base px-6 py-3 hover:bg-opacity-80 transition-all duration-150"
59
+ >
60
+ Start generation
61
+ </a>
62
+ <a
63
+ href="#gallery"
64
+ className="rounded-full text-zinc-300 bg-zinc-900 font-medium text-base px-6 py-3 hover:bg-opacity-80 transition-all duration-150"
65
+ >
66
+ View examples
67
+ </a>
68
+ </div>
69
+ </header>
70
+ );
71
+ };
app/_fonts/GeistMonoVF.woff ADDED
Binary file (67.9 kB). View file
 
app/_fonts/GeistVF.woff ADDED
Binary file (66.3 kB). View file
 
app/_fonts/nohemi/bold.woff ADDED
Binary file (24.9 kB). View file
 
app/_fonts/nohemi/extrabold.woff ADDED
Binary file (24.7 kB). View file
 
app/_fonts/nohemi/light.woff ADDED
Binary file (32.9 kB). View file
 
app/_fonts/nohemi/regular.woff ADDED
Binary file (25.6 kB). View file
 
app/_fonts/nohemi/semibold.woff ADDED
Binary file (25.4 kB). View file
 
app/api/images/[id]/route.ts ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { NextRequest } from "next/server";
2
+ import fs from "fs/promises";
3
+
4
+ import prisma from "@/_utils/prisma";
5
+
6
+ export async function GET(
7
+ req: NextRequest,
8
+ { params }: { params: { id: string } }
9
+ ) {
10
+ const { id } = params;
11
+
12
+ const image = await prisma.logo.findUnique({
13
+ where: { id: Number(id) },
14
+ });
15
+
16
+ if (!image) {
17
+ return new Response(null, { status: 404 });
18
+ }
19
+
20
+ const buffer = await fs.readFile(
21
+ `${process.env.PUBLIC_FILE_UPLOAD_DIR}/${image.id}.png`
22
+ );
23
+
24
+ if (!buffer) {
25
+ return new Response(null, { status: 404 });
26
+ }
27
+
28
+ return new Response(buffer, {
29
+ headers: {
30
+ "Content-Type": "image/png",
31
+ },
32
+ });
33
+ }
app/api/logo/route.ts ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { NextRequest } from "next/server";
2
+ import prisma from "@/_utils/prisma";
3
+
4
+ export async function GET(req: NextRequest) {
5
+ const images = await prisma.logo.findMany({
6
+ select: {
7
+ id: true,
8
+ },
9
+ take: 24,
10
+ orderBy: {
11
+ id: "desc",
12
+ },
13
+ });
14
+ return Response.json(images.map((image) => image.id));
15
+ }
app/favicon.ico ADDED
app/layout.tsx ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import type { Metadata } from "next";
2
+ import localFont from "next/font/local";
3
+ import "@/assets/globals.css";
4
+ import { Navigation } from "@/components/_navigation";
5
+
6
+ const nohemiRegular = localFont({
7
+ src: [
8
+ {
9
+ path: "./_fonts/nohemi/light.woff",
10
+ weight: "300",
11
+ },
12
+ {
13
+ path: "./_fonts/nohemi/regular.woff",
14
+ weight: "400",
15
+ },
16
+ {
17
+ path: "./_fonts/nohemi/semibold.woff",
18
+ weight: "600",
19
+ },
20
+ {
21
+ path: "./_fonts/nohemi/bold.woff",
22
+ weight: "700",
23
+ },
24
+ {
25
+ path: "./_fonts/nohemi/extrabold.woff",
26
+ weight: "900",
27
+ },
28
+ ],
29
+ variable: "--font-nohemi-sans",
30
+ });
31
+
32
+ const geistMono = localFont({
33
+ src: "./_fonts/GeistMonoVF.woff",
34
+ variable: "--font-geist-mono",
35
+ weight: "100 900",
36
+ });
37
+
38
+ export const metadata: Metadata = {
39
+ title: "Create Next App",
40
+ description: "Generated by create next app",
41
+ };
42
+
43
+ export default function RootLayout({
44
+ children,
45
+ }: Readonly<{
46
+ children: React.ReactNode;
47
+ }>) {
48
+ return (
49
+ <html lang="en">
50
+ <body
51
+ className={`${nohemiRegular.variable} ${geistMono.variable} antialiased`}
52
+ >
53
+ <div className="min-h-screen w-full overflow-auto font-[family-name:var(--font-nohemi-sans)] p-6 scroll-smooth">
54
+ <Navigation />
55
+ {children}
56
+ </div>
57
+ </body>
58
+ </html>
59
+ );
60
+ }
app/page.tsx ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Gallery } from "./_components/gallery";
2
+ import { Generation } from "./_components/generation";
3
+ import { HeroHeader } from "./_components/hero-header";
4
+
5
+ async function getLogos() {
6
+ try {
7
+ const logos = await fetch(process.env.API_URL + "/api/logo")
8
+ .then((res) => res.json())
9
+ .then((data) => data);
10
+ return logos;
11
+ } catch (error: any) {
12
+ console.error("Error fetching logos: ", error.message);
13
+ return [];
14
+ }
15
+ }
16
+ export const revalidate = 0;
17
+
18
+ export default async function Home() {
19
+ const logos = await getLogos();
20
+ return (
21
+ <section>
22
+ <div className="max-w-4xl mx-auto">
23
+ <HeroHeader />
24
+ <Generation />
25
+ </div>
26
+ <Gallery logos={logos} />
27
+ </section>
28
+ );
29
+ }
assets/globals.css ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+ :root {
6
+ --background: #ffffff;
7
+ --foreground: #171717;
8
+ }
9
+
10
+ @media (prefers-color-scheme: dark) {
11
+ :root {
12
+ --background: #0a0a0a;
13
+ --foreground: #ededed;
14
+ }
15
+ }
16
+
17
+ body {
18
+ color: var(--foreground);
19
+ background: var(--background);
20
+ font-family: Arial, Helvetica, sans-serif;
21
+ }
22
+
23
+ html,
24
+ body {
25
+ @apply scroll-smooth;
26
+ }
27
+
28
+ @keyframes jiggle {
29
+ 0% {
30
+ transform: rotate(0deg);
31
+ }
32
+ 25% {
33
+ transform: rotate(5deg);
34
+ }
35
+ 50% {
36
+ transform: rotate(-5deg);
37
+ }
38
+ 75% {
39
+ transform: rotate(5deg);
40
+ }
41
+ 100% {
42
+ transform: rotate(0deg);
43
+ }
44
+ }
45
+
46
+ @layer utilities {
47
+ .text-balance {
48
+ text-wrap: balance;
49
+ }
50
+ .animate-jiggle {
51
+ animation: jiggle 0.5s infinite;
52
+ }
53
+ }
assets/logo.png ADDED
assets/svg/arrow.svg ADDED
components/_button.tsx ADDED
File without changes
components/_navigation/index.tsx ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import Image from "next/image";
2
+
3
+ import Arrow from "@/assets/svg/arrow.svg";
4
+ import Logo from "@/assets/logo.png";
5
+
6
+ export const Navigation = () => {
7
+ return (
8
+ <div className="rounded-full bg-zinc-950 border border-white/10 px-6 py-4 flex items-center justify-between max-w-xl w-full mx-auto shadow-md relative">
9
+ <div className="flex items-center justify-center gap-3 relative">
10
+ <div className="relative">
11
+ <Image src={Logo} alt="logo" className="size-6" />
12
+ <div className="absolute left-0 -translate-x-[calc(100%+12px)] top-5 text-white flex justify-end gap-1 -rotate-6">
13
+ <p className="font-mono text-sm text-white">AI Generated</p>
14
+ <Image
15
+ src={Arrow}
16
+ alt="arrow"
17
+ className="w-[30px] absolute -right-3 -top-1"
18
+ />
19
+ </div>
20
+ </div>
21
+ <p className="font-semibold text-lg">LogoAI</p>
22
+ </div>
23
+ <ul className="flex items-center justify-right gap-3">
24
+ <li>
25
+ <a href="#">X</a>
26
+ </li>
27
+ <li>
28
+ <a href="#">HF</a>
29
+ </li>
30
+ </ul>
31
+ </div>
32
+ );
33
+ };
entrypoint.sh ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ #!/bin/bash
2
+ npm run build && npx prisma generate && npx prisma migrate deploy && npx prisma db push && npm start
next.config.mjs ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /** @type {import('next').NextConfig} */
2
+ const nextConfig = {
3
+ webpack: (config) => {
4
+ // Ignore node-specific modules when bundling for the browser
5
+ // See https://webpack.js.org/configuration/resolve/#resolvealias
6
+ config.resolve.alias = {
7
+ ...config.resolve.alias,
8
+ sharp$: false,
9
+ "onnxruntime-node$": false,
10
+ };
11
+ return config;
12
+ },
13
+ };
14
+
15
+ export default nextConfig;
package-lock.json ADDED
The diff for this file is too large to render. See raw diff
 
package.json ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "logoai.dev",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "dev": "next dev",
7
+ "build": "next build",
8
+ "start": "next start",
9
+ "lint": "next lint"
10
+ },
11
+ "dependencies": {
12
+ "@huggingface/inference": "^2.8.0",
13
+ "@prisma/client": "^5.19.1",
14
+ "@xenova/transformers": "^2.17.2",
15
+ "classnames": "^2.5.1",
16
+ "next": "14.2.12",
17
+ "react": "^18",
18
+ "react-dom": "^18",
19
+ "react-icons": "^5.3.0",
20
+ "react-use": "^17.5.1"
21
+ },
22
+ "devDependencies": {
23
+ "@types/node": "^20",
24
+ "@types/react": "^18",
25
+ "@types/react-dom": "^18",
26
+ "eslint": "^8",
27
+ "eslint-config-next": "14.2.12",
28
+ "postcss": "^8",
29
+ "tailwindcss": "^3.4.1",
30
+ "typescript": "^5"
31
+ }
32
+ }
postcss.config.mjs ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ /** @type {import('postcss-load-config').Config} */
2
+ const config = {
3
+ plugins: {
4
+ tailwindcss: {},
5
+ },
6
+ };
7
+
8
+ export default config;
prisma/schema.prisma ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // This is your Prisma schema file,
2
+ // learn more about it in the docs: https://pris.ly/d/prisma-schema
3
+
4
+ generator client {
5
+ provider = "prisma-client-js"
6
+ }
7
+
8
+ datasource db {
9
+ provider = "sqlite"
10
+ url = env("DATABASE_URL")
11
+ }
12
+
13
+ model Logo {
14
+ id Int @id @default(autoincrement())
15
+ name String
16
+ createdAt DateTime @default(now())
17
+ updatedAt DateTime @default(now())
18
+ }
tailwind.config.ts ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import type { Config } from "tailwindcss";
2
+
3
+ const config: Config = {
4
+ content: [
5
+ "./pages/**/*.{js,ts,jsx,tsx,mdx}",
6
+ "./components/**/*.{js,ts,jsx,tsx,mdx}",
7
+ "./app/**/*.{js,ts,jsx,tsx,mdx}",
8
+ ],
9
+ theme: {
10
+ extend: {
11
+ colors: {
12
+ background: "var(--background)",
13
+ foreground: "var(--foreground)",
14
+ },
15
+ fontFamily: {
16
+ mono: ["var(--font-geist-mono)"],
17
+ },
18
+ },
19
+ },
20
+ plugins: [],
21
+ };
22
+ export default config;
tsconfig.json ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "compilerOptions": {
3
+ "lib": ["dom", "dom.iterable", "esnext"],
4
+ "allowJs": true,
5
+ "skipLibCheck": true,
6
+ "strict": true,
7
+ "noEmit": true,
8
+ "esModuleInterop": true,
9
+ "module": "esnext",
10
+ "moduleResolution": "bundler",
11
+ "resolveJsonModule": true,
12
+ "isolatedModules": true,
13
+ "jsx": "preserve",
14
+ "incremental": true,
15
+ "plugins": [
16
+ {
17
+ "name": "next"
18
+ }
19
+ ],
20
+ "paths": {
21
+ "@/*": ["./*"]
22
+ }
23
+ },
24
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
25
+ "exclude": ["node_modules"]
26
+ }