├── .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 | 
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 |
21 |
22 | {children}
23 |
24 |
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 |
--------------------------------------------------------------------------------