├── .env.example ├── .eslintrc.json ├── app ├── favicon.ico ├── globals.css ├── layout.tsx ├── components │ ├── buttons │ │ ├── button.tsx │ │ └── imageButton.tsx │ ├── fields │ │ ├── inputField.tsx │ │ └── dropDownField.tsx │ ├── header.tsx │ └── footer.tsx ├── api │ └── generate │ │ └── route.ts └── page.tsx ├── public ├── logo.png ├── screenshot.png ├── vercel.svg ├── github.svg └── star.svg ├── postcss.config.js ├── next.config.js ├── .prettierrc.yaml ├── .gitignore ├── tailwind.config.js ├── tsconfig.json ├── package.json ├── LICENSE ├── README.md └── pnpm-lock.yaml /.env.example: -------------------------------------------------------------------------------- 1 | OPENAI_API_KEY=your_key_here -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chroxify/namegpt/HEAD/app/favicon.ico -------------------------------------------------------------------------------- /public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chroxify/namegpt/HEAD/public/logo.png -------------------------------------------------------------------------------- /app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /public/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chroxify/namegpt/HEAD/public/screenshot.png -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | experimental: { 4 | appDir: true, 5 | }, 6 | } 7 | 8 | module.exports = nextConfig 9 | -------------------------------------------------------------------------------- /.prettierrc.yaml: -------------------------------------------------------------------------------- 1 | trailingComma: "es5" 2 | tabWidth: 2 3 | useTabs: false 4 | semi: false 5 | singleQuote: true 6 | jsxSingleQuote: true 7 | bracketSpacing: true 8 | bracketSameLine: false -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | /node_modules 3 | 4 | # next.js 5 | /.next/ 6 | /out/ 7 | 8 | # production 9 | /build 10 | 11 | # misc 12 | .DS_Store 13 | *.pem 14 | 15 | # debug 16 | npm-debug.log* 17 | yarn-debug.log* 18 | yarn-error.log* 19 | .pnpm-debug.log* 20 | 21 | # local env files 22 | .env*.local 23 | 24 | # vercel 25 | .vercel 26 | 27 | # typescript 28 | *.tsbuildinfo 29 | next-env.d.ts 30 | 31 | # ide 32 | .vscode 33 | 34 | # env 35 | .env -------------------------------------------------------------------------------- /app/layout.tsx: -------------------------------------------------------------------------------- 1 | // These styles apply to every route in the application 2 | import './globals.css' 3 | 4 | export const metadata = { 5 | title: 'NameGPT', 6 | description: 'Generate project names on the fly with GPT-3', 7 | } 8 | 9 | export default function RootLayout({ 10 | children, 11 | }: { 12 | children: React.ReactNode 13 | }) { 14 | return ( 15 | 16 | {children} 17 | 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: [ 4 | "./app/**/*.{js,ts,jsx,tsx}", // Note the addition of the `app` directory. 5 | "./pages/**/*.{js,ts,jsx,tsx}", 6 | "./components/**/*.{js,ts,jsx,tsx}", 7 | 8 | // Or if using `src` directory: 9 | "./src/**/*.{js,ts,jsx,tsx}", 10 | ], 11 | theme: { 12 | extend: { 13 | colors: { 14 | primary: "#AEECDD", 15 | secondary: "#FFFFFF", 16 | background: "#EBF4F1", 17 | text: "#191C1B", 18 | }, 19 | }, 20 | }, 21 | plugins: [], 22 | } 23 | -------------------------------------------------------------------------------- /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 | "plugins": [ 18 | { 19 | "name": "next" 20 | } 21 | ], 22 | "paths": { 23 | "@/*": ["./*"] 24 | } 25 | }, 26 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 27 | "exclude": ["node_modules"] 28 | } 29 | -------------------------------------------------------------------------------- /app/components/buttons/button.tsx: -------------------------------------------------------------------------------- 1 | type ButtonProps = { 2 | title: string 3 | notify: () => void 4 | } 5 | 6 | export default function Button({ title, notify }: ButtonProps) { 7 | const handleClick = () => { 8 | navigator.clipboard.writeText(title) 9 | 10 | // Notify user that value has been copied to clipboard 11 | notify() 12 | } 13 | 14 | return ( 15 | // Button with onClick that copies value to clipboard 16 | 22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "quickdocs", 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 | "@types/node": "18.15.5", 13 | "@types/react": "18.0.28", 14 | "@types/react-dom": "18.0.11", 15 | "@vercel/analytics": "^0.1.11", 16 | "csstype": "^3.1.1", 17 | "eslint": "8.36.0", 18 | "eslint-config-next": "13.2.4", 19 | "next": "13.2.4", 20 | "react": "18.2.0", 21 | "react-dom": "18.2.0", 22 | "react-hot-toast": "^2.4.0", 23 | "typescript": "5.0.2" 24 | }, 25 | "devDependencies": { 26 | "autoprefixer": "^10.4.14", 27 | "postcss": "^8.4.21", 28 | "prettier": "^2.8.7", 29 | "tailwindcss": "^3.2.7" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/components/buttons/imageButton.tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link' 2 | import Image from 'next/image' 3 | 4 | type ButtonProps = { 5 | title: string 6 | link: string 7 | image: string 8 | alt: string 9 | } 10 | 11 | export default function ImageButton({ title, link, image, alt }: ButtonProps) { 12 | return ( 13 | 14 | 24 | 25 | ) 26 | } 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Christo Todorov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /app/components/fields/inputField.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | type InputFieldProps = { 4 | field: string 5 | title: string 6 | placeholder: string 7 | setPrompt: (v: string) => void 8 | } 9 | 10 | export default function InputField({ 11 | field, 12 | title, 13 | placeholder, 14 | setPrompt, 15 | }: InputFieldProps) { 16 | return ( 17 |
18 | {/* Field Title Row */} 19 |
20 |
21 |

22 | {field}. 23 |

24 |
25 |

{title}

26 |
27 | 32 |
33 | ) 34 | } 35 | -------------------------------------------------------------------------------- /app/components/header.tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link' 2 | import Image from 'next/image' 3 | 4 | export default function Header() { 5 | return ( 6 | 38 | ) 39 | } 40 | -------------------------------------------------------------------------------- /app/components/fields/dropDownField.tsx: -------------------------------------------------------------------------------- 1 | type DropDownFieldProps = { 2 | field: string 3 | title: string 4 | options: string[] 5 | setAmount: (v: string) => void 6 | } 7 | 8 | export default function DropDownField({ 9 | field, 10 | title, 11 | options, 12 | setAmount, 13 | }: DropDownFieldProps) { 14 | return ( 15 |
16 | {/* Field Title Row */} 17 |
18 |
19 |

20 | {field}. 21 |

22 |
23 |

{title}

24 |
25 | {/* Dropdown with "Five" as default selection */} 26 | 36 |
37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /public/github.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | 10 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [NameGPT](https://namegpt.chroxify.com) 2 | 3 | **Description**: This project lets you generate unique project names based on a simpel description. 4 | 5 | [![NameGPT](./public/screenshot.png)](https://namegpt.chroxify.com) 6 | 7 | ## How it works? 8 | 9 | NameGPT uses the GPT-3 API to generate unique names on the based user input. It sends the prompt to the GPT-3 API which returns an array of names based on that requested prompt. 10 | 11 | ## Running Locally 12 | #### Clone the Repository 13 | ```bash 14 | git clone https://github.com/chroxify/namegpt.git 15 | ``` 16 | 17 | #### Create an OpenAI API Key 18 | 1. Go to [OpenAI](https://platform.openai.com/login) to create an account. 19 | 2. Visit the [API Keys Page](https://platform.openai.com/account/api-keys) 20 | 3. Click `Create new secret key` and copy the key 21 | 22 | #### Store the API Key in .env 23 | Create a `.env` file at the root of the cloned directory and fill it out as shown in the `.env.example` file. 24 | 25 | #### Run the application 26 | ```bash 27 | npm run dev 28 | ``` 29 | This will run the application in dev mode on `https://localhost:3000`. 30 | 31 | ## One-Click Deployment 32 | Deploy easily with one click using [Vercel](https://vercel.com) 33 | 34 | [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/chroxify/namegpt&env=OPENAI_API_KEY&project-name=NameGPT&repo-name=nameGPT) -------------------------------------------------------------------------------- /app/components/footer.tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link' 2 | import Image from 'next/image' 3 | 4 | export default function Footer() { 5 | return ( 6 | 40 | ) 41 | } 42 | -------------------------------------------------------------------------------- /app/api/generate/route.ts: -------------------------------------------------------------------------------- 1 | // Check if env variable is set 2 | if (!process.env.OPENAI_API_KEY) { 3 | throw new Error('OPENAI_API_KEY is not set') 4 | } 5 | 6 | // Post request taking in a json promt 7 | export async function POST(request: Request) { 8 | const { amount, prompt } = await request.json() 9 | 10 | // Check if amount and prompt is set 11 | if (!amount || !prompt) { 12 | return new Response('Missing amount or prompt', { status: 400 }) 13 | } 14 | 15 | // Check if amount is greater than 5 16 | if (amount > 5) { 17 | return new Response('Amount can not be greater than 5', { status: 400 }) 18 | } 19 | 20 | // Set headers 21 | const headers = { 22 | 'Content-Type': 'application/json', 23 | Authorization: `Bearer ${process.env.OPENAI_API_KEY}`, 24 | } 25 | 26 | // Set payload 27 | const payload = { 28 | model: 'text-davinci-003', 29 | prompt: `Product description: ${prompt}\nAmount to generate: ${amount}\nMAKE NAME BE 1 WORD UNIQUE NON COMBINED WORD (NO CAMELCASE COMBINATIONS)\nRETURN RESULTS AS JSON {"names": []}`, 30 | temperature: 0.8, 31 | max_tokens: 50, 32 | top_p: 1, 33 | frequency_penalty: 0, 34 | presence_penalty: 0, 35 | } 36 | 37 | // Fetch data from openai 38 | const response = await fetch('https://api.openai.com/v1/completions', { 39 | method: 'POST', 40 | headers, 41 | body: JSON.stringify(payload), 42 | }) 43 | 44 | // Get choices from response 45 | const { choices } = await response.json() 46 | 47 | // Convert response to json 48 | const json = JSON.parse(choices[0].text) 49 | 50 | // Return json 51 | return new Response(JSON.stringify(json), { 52 | headers: { 53 | 'Content-Type': 'application/json', 54 | }, 55 | }) 56 | } 57 | -------------------------------------------------------------------------------- /app/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import Link from 'next/link' 3 | import { useState, useEffect } from 'react' 4 | import { Inter } from 'next/font/google' 5 | import toast, { Toaster, useToasterStore } from 'react-hot-toast' 6 | import Header from './components/header' 7 | import Footer from './components/footer' 8 | import { Analytics } from '@vercel/analytics/react'; 9 | import ImageButton from './components/buttons/imageButton' 10 | import Button from './components/buttons/button' 11 | import InputField from './components/fields/inputField' 12 | import DropDownField from './components/fields/dropDownField' 13 | import Script from "next/script"; 14 | 15 | const TOAST_LIMIT = 3 16 | 17 | export default function Home() { 18 | const [names, setNames] = useState([]) 19 | const [loading, setLoading] = useState(false) 20 | const [amount, setAmount] = useState('Three') 21 | const [prompt, setPrompt] = useState( 22 | 'eg. A website that summarizes blog posts for you.' 23 | ) 24 | 25 | const notify = () => 26 | toast.success('Name copied!', { 27 | position: 'bottom-center', 28 | style: { 29 | height: '45px', 30 | border: '2px solid #191C1B', 31 | borderRadius: '8px', 32 | background: '#fff', 33 | color: '#191C1B', 34 | }, 35 | iconTheme: { 36 | primary: '#AEECDD', 37 | secondary: '#000' 38 | }, 39 | className: 'font-semibold', 40 | }) 41 | 42 | const { toasts } = useToasterStore() 43 | 44 | // Enforce Limit 45 | useEffect(() => { 46 | toasts 47 | .filter((t) => t.visible) // Only consider visible toasts 48 | .filter((_, i) => i >= TOAST_LIMIT) // Is toast index over limit 49 | .forEach((t) => toast.dismiss(t.id)) // Dismiss – Use toast.remove(t.id) removal without animation 50 | }, [toasts]) 51 | 52 | async function generateNames() { 53 | // Set loading to true 54 | setLoading(true) 55 | 56 | // Fetch data from API 57 | const res = fetch('/api/generate', { 58 | method: 'POST', 59 | headers: { 60 | 'Content-Type': 'application/json', 61 | 'Cache-Control': 'no-cache', 62 | }, 63 | body: JSON.stringify({ 64 | amount: amount, 65 | prompt: prompt, 66 | }), 67 | }) 68 | .then((res) => res.json()) 69 | .then((data) => { 70 | console.log(JSON.stringify(data)) 71 | setNames(data.names) 72 | setLoading(false) 73 | }) 74 | } 75 | 76 | return ( 77 |
78 | {/* Analytics */} 79 | 80 | {process.env.NODE_ENV === "production" && ( 81 |