├── .nvmrc ├── .env.example ├── .npmrc ├── src ├── styles │ └── globals.css ├── lib │ └── utils.ts ├── app │ ├── page.tsx │ ├── head.tsx │ └── layout.tsx ├── pages │ └── api │ │ └── openai.ts └── components │ ├── footer │ └── index.tsx │ └── openai │ └── colortext.tsx ├── .prettierignore ├── public ├── og.jpg └── favicon.ico ├── postcss.config.js ├── .vscode └── settings.json ├── .editorconfig ├── prettier.config.js ├── next.config.js ├── README.md ├── .gitignore ├── tailwind.config.js ├── tsconfig.json ├── LICENSE.md ├── .eslintrc └── package.json /.nvmrc: -------------------------------------------------------------------------------- 1 | 18 -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | OPENAI_API_KEY= -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | save-exact = true 2 | legacy-peer-deps=true 3 | strict-peer-dependencies=false -------------------------------------------------------------------------------- /src/styles/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .cache 2 | public 3 | .idea 4 | .vscode 5 | .DS_Store 6 | node_modules 7 | .github -------------------------------------------------------------------------------- /public/og.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/masonyekta/nextjs-openai-text-to-color/HEAD/public/og.jpg -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/masonyekta/nextjs-openai-text-to-color/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib", 3 | "typescript.enablePromptUseWorkspaceTsdk": true 4 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | indent_size = 4 6 | charset = utf-8 7 | end_of_line = lf 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true -------------------------------------------------------------------------------- /src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { twMerge } from 'tailwind-merge' 2 | import { ClassValue, clsx } from 'clsx' 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)) 6 | } 7 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | semi: false, 3 | singleQuote: true, 4 | printWidth: 100, 5 | tabWidth: 4, 6 | useTabs: true, 7 | trailingComma: 'es5', 8 | bracketSpacing: true, 9 | } 10 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | swcMinify: true, 5 | experimental: { appDir: true }, 6 | } 7 | 8 | module.exports = nextConfig 9 | -------------------------------------------------------------------------------- /src/app/page.tsx: -------------------------------------------------------------------------------- 1 | import { ColorText } from '@/components/openai/colortext' 2 | 3 | export default function Page() { 4 | return ( 5 | <> 6 |
7 |
8 |

9 | Turn a text description into a color. 10 |

11 | 12 |
13 |
14 | 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Next.js OpenAI Text to Color 2 | 3 | This is a simple fun project which uses OpenAI API to turn a text description into a color. 4 | 5 | ![Preview](https://i.imgur.com/J7CJ2Et.jpg) 6 | 7 | ## Getting Started 8 | 9 | Copy `.env.example` to `.env` and set your own OpenAI API. 10 | 11 | Run the development server: 12 | 13 | ```bash 14 | npm install // install packages 15 | npm run dev // run development server 16 | ``` 17 | 18 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env 30 | .env.development.local 31 | .env.test.local 32 | .env.production.local 33 | .env.local 34 | 35 | # vercel 36 | .vercel 37 | 38 | # typescript 39 | *.tsbuildinfo 40 | next-env.d.ts 41 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | const { colors } = require('tailwindcss/colors') 2 | const { fontFamily } = require('tailwindcss/defaultTheme') 3 | 4 | /** @type {import('tailwindcss').Config} */ 5 | module.exports = { 6 | content: ['./src/app/**/*.{ts,tsx}', './src/components/**/*.{ts,tsx}'], 7 | theme: { 8 | letterSpacing: { 9 | tighter: '-.055em', 10 | }, 11 | container: { 12 | center: true, 13 | padding: '1.5rem', 14 | screens: { 15 | '2xl': '1440px', 16 | }, 17 | }, 18 | extend: { 19 | fontFamily: { 20 | sans: ['var(--font-inter)', ...fontFamily.sans], 21 | }, 22 | 23 | colors: { 24 | ...colors, 25 | }, 26 | }, 27 | }, 28 | plugins: [require('@tailwindcss/typography')], 29 | } 30 | -------------------------------------------------------------------------------- /src/pages/api/openai.ts: -------------------------------------------------------------------------------- 1 | import type { NextApiRequest, NextApiResponse } from 'next' 2 | import { Configuration, OpenAIApi } from 'openai' 3 | 4 | const configuration = new Configuration({ 5 | apiKey: process.env.OPENAI_API_KEY, 6 | }) 7 | const openai = new OpenAIApi(configuration) 8 | 9 | export default async function handler(req: NextApiRequest, res: NextApiResponse) { 10 | const completion = await openai.createCompletion({ 11 | model: 'text-davinci-003', 12 | prompt: `The CSS code for a color like ${req.body.text}:\n\nbackground-color: #`, 13 | temperature: 0, 14 | max_tokens: 64, 15 | top_p: 1.0, 16 | frequency_penalty: 0.0, 17 | presence_penalty: 0.0, 18 | stop: [';'], 19 | }) 20 | res.status(200).json({ result: completion.data }) 21 | } 22 | -------------------------------------------------------------------------------- /src/app/head.tsx: -------------------------------------------------------------------------------- 1 | export default function Head() { 2 | return ( 3 | <> 4 | Turn a text description into a color. 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /src/components/footer/index.tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link' 2 | 3 | export function Footer() { 4 | return ( 5 |
6 |

7 | Made using{' '} 8 | 9 | OpenAI 10 | {' '} 11 | API by{' '} 12 | 13 | @masonyekta 14 | 15 |

16 |

17 | View source code on{' '} 18 | 23 | GitHub 24 | 25 |

26 |
27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /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 | "baseUrl": ".", 18 | "paths": { 19 | "@/components/*": ["./src/components/*"], 20 | "@/lib/*": ["./src/lib/*"], 21 | "@/styles/*": ["./src/styles/*"] 22 | }, 23 | "plugins": [ 24 | { 25 | "name": "next" 26 | } 27 | ] 28 | }, 29 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 30 | "exclude": ["node_modules"] 31 | } 32 | -------------------------------------------------------------------------------- /src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import { Inter as FontSans } from '@next/font/google' 2 | import React from 'react' 3 | import '@/styles/globals.css' 4 | import { cn } from '@/lib/utils' 5 | import { Footer } from '@/components/footer' 6 | 7 | const fontSans = FontSans({ 8 | subsets: ['latin'], 9 | variable: '--font-inter', 10 | }) 11 | 12 | interface RootLayoutProps { 13 | children: React.ReactNode 14 | } 15 | 16 | export default function RootLayout({ children }: RootLayoutProps) { 17 | return ( 18 | 19 | 20 | 21 |
22 | {children} 23 |
25 | 26 | 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 masonyekta 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 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "parserOptions": { 4 | "ecmaFeatures": { 5 | "jsx": true 6 | }, 7 | "ecmaVersion": "latest", 8 | "sourceType": "module" 9 | }, 10 | "env": { 11 | "browser": true, 12 | "es6": true, 13 | "node": true 14 | }, 15 | "plugins": ["react", "jsx-a11y", "prettier", "@typescript-eslint"], 16 | "extends": [ 17 | "plugin:react/recommended", 18 | "plugin:react/jsx-runtime", 19 | "plugin:react-hooks/recommended", 20 | "standard", 21 | "plugin:prettier/recommended", 22 | "next", 23 | "next/core-web-vitals" 24 | ], 25 | "rules": { 26 | "prettier/prettier": "error", 27 | "react/prop-types": "off", 28 | "react/jsx-filename-extension": "off", 29 | "react/jsx-props-no-spreading": "off", 30 | "import/prefer-default-export": "off", 31 | "import/extensions": [ 32 | "error", 33 | "ignorePackages", 34 | { 35 | "": "never", 36 | "ts": "never", 37 | "tsx": "never", 38 | "js": "never", 39 | "jsx": "never" 40 | } 41 | ], 42 | "@typescript-eslint/explicit-function-return-type": "off", 43 | "@typescript-eslint/no-explicit-any": "off", 44 | "@typescript-eslint/no-var-requires": "off", 45 | "quotes": "off", 46 | "@typescript-eslint/no-unused-vars": [2, { "argsIgnorePattern": "^_" }] 47 | }, 48 | "settings": { 49 | "import/resolver": { 50 | "typescript": { 51 | "project": "." 52 | } 53 | }, 54 | "react": { 55 | "version": "detect" 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "next-js-openai-text-to-color", 3 | "version": "0.1.0", 4 | "private": true, 5 | "author": { 6 | "name": "masonyekta", 7 | "url": "https://github.com/masonyekta/nextjs-openai-text-to-color" 8 | }, 9 | "scripts": { 10 | "build": "next build", 11 | "dev": "next dev", 12 | "fix": "eslint --ignore-path .gitignore \"src/**/*.+(ts|js|tsx)\" --fix", 13 | "format": "prettier --ignore-path .gitignore \"src/**/*.+(ts|js|tsx)\" --write", 14 | "lint": "eslint --ignore-path .gitignore \"src/**/*.+(ts|js|tsx)\"", 15 | "start": "next start" 16 | }, 17 | "dependencies": { 18 | "@next/font": "13.0.5", 19 | "next": "13.0.5", 20 | "openai": "3.1.0", 21 | "prettier": "2.8.0", 22 | "react": "18.2.0", 23 | "react-dom": "18.2.0", 24 | "react-hook-form": "7.40.0", 25 | "server-only": "0.0.1", 26 | "typescript": "4.9.3" 27 | }, 28 | "devDependencies": { 29 | "@tailwindcss/typography": "0.5.8", 30 | "@types/node": "18.11.9", 31 | "@types/react": "18.0.25", 32 | "@types/react-dom": "18.0.9", 33 | "@typescript-eslint/eslint-plugin": "5.45.0", 34 | "@typescript-eslint/parser": "5.45.0", 35 | "autoprefixer": "10.4.13", 36 | "clsx": "1.2.1", 37 | "eslint": "8.28.0", 38 | "eslint-config-next": "13.0.5", 39 | "eslint-config-prettier": "8.5.0", 40 | "eslint-config-standard": "17.0.0", 41 | "eslint-plugin-import": "2.26.0", 42 | "eslint-plugin-jsx-a11y": "6.6.1", 43 | "eslint-plugin-n": "15.5.1", 44 | "eslint-plugin-prettier": "4.2.1", 45 | "eslint-plugin-promise": "6.1.1", 46 | "eslint-plugin-react": "7.31.11", 47 | "eslint-plugin-react-hooks": "4.6.0", 48 | "postcss": "8.4.19", 49 | "tailwind-merge": "1.8.0", 50 | "tailwindcss": "3.2.4" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/components/openai/colortext.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import React from 'react' 4 | 5 | export const ColorText = () => { 6 | const [value, setValue] = React.useState('') 7 | const [prompt, setPrompt] = React.useState('') 8 | const [color, setColor] = React.useState('') 9 | 10 | const handleInput = React.useCallback((e: React.ChangeEvent) => { 11 | setValue(e.target.value) 12 | }, []) 13 | 14 | const handleKeyDown = React.useCallback( 15 | async (e: React.KeyboardEvent) => { 16 | if (e.key === 'Enter') { 17 | setPrompt(value) 18 | setColor('Loading...') 19 | const response = await fetch('/api/openai', { 20 | method: 'POST', 21 | headers: { 22 | 'Content-Type': 'application/json', 23 | }, 24 | body: JSON.stringify({ text: value }), 25 | }) 26 | const data = await response.json() 27 | setValue('') 28 | setColor(`#${data.result.choices[0].text}`) 29 | } 30 | }, 31 | [value] 32 | ) 33 | 34 | React.useEffect(() => { 35 | // change background color 36 | if (color) { 37 | document.body.style.background = color 38 | } 39 | }) 40 | 41 | return ( 42 | <> 43 |
44 |
45 |
46 | 60 |
61 | 62 | ↵ Return 63 | 64 |
65 |
66 |
67 |
68 | {prompt && ( 69 |
70 |

Description:

71 | {prompt} 72 |
73 | )} 74 | {color && ( 75 |
76 |

Color Code:

77 | {color} 78 |
79 | )} 80 |
81 |
82 | 83 | ) 84 | } 85 | 86 | export default ColorText 87 | --------------------------------------------------------------------------------