Commit
·
67ea2ab
0
Parent(s):
initial commit
Browse files- .eslintrc.json +3 -0
- .gitignore +40 -0
- Dockerfile +32 -0
- README.md +13 -0
- _types/index.ts +7 -0
- _utils/index.ts +132 -0
- _utils/prisma.ts +4 -0
- app/_actions/generate.ts +75 -0
- app/_components/gallery/index.tsx +23 -0
- app/_components/generation/index.tsx +75 -0
- app/_components/generation/step/brand.tsx +25 -0
- app/_components/generation/step/description.tsx +25 -0
- app/_components/generation/step/industry.tsx +32 -0
- app/_components/generation/step/list.tsx +74 -0
- app/_components/hero-header.tsx +71 -0
- app/_fonts/GeistMonoVF.woff +0 -0
- app/_fonts/GeistVF.woff +0 -0
- app/_fonts/nohemi/bold.woff +0 -0
- app/_fonts/nohemi/extrabold.woff +0 -0
- app/_fonts/nohemi/light.woff +0 -0
- app/_fonts/nohemi/regular.woff +0 -0
- app/_fonts/nohemi/semibold.woff +0 -0
- app/api/images/[id]/route.ts +33 -0
- app/api/logo/route.ts +15 -0
- app/favicon.ico +0 -0
- app/layout.tsx +60 -0
- app/page.tsx +29 -0
- assets/globals.css +53 -0
- assets/logo.png +0 -0
- assets/svg/arrow.svg +3 -0
- components/_button.tsx +0 -0
- components/_navigation/index.tsx +33 -0
- entrypoint.sh +2 -0
- next.config.mjs +15 -0
- package-lock.json +0 -0
- package.json +32 -0
- postcss.config.mjs +8 -0
- prisma/schema.prisma +18 -0
- tailwind.config.ts +22 -0
- tsconfig.json +26 -0
.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 |
+
}
|