├── README.md ├── public ├── favicon.ico ├── vercel.svg ├── thirteen.svg └── next.svg ├── postcss.config.js ├── .prettierrc.json ├── jsconfig.json ├── next.config.js ├── src ├── pages │ ├── _app.js │ ├── _document.js │ ├── index.js │ └── api │ │ └── chat.js ├── components │ ├── UserAvatar.jsx │ ├── Avatar.jsx │ ├── ErrorMessage.jsx │ ├── Layout.jsx │ ├── ScrollToBottom.jsx │ ├── Message.jsx │ ├── Twitch.jsx │ ├── Banner.jsx │ ├── TypingEffect.jsx │ ├── Welcome.jsx │ ├── CreatePrompt.jsx │ ├── Aside.jsx │ └── Icons.jsx ├── styles │ ├── globals.css │ └── Home.module.css └── store │ └── conversations.js ├── .eslintrc.json ├── .gitignore ├── tailwind.config.js └── package.json /README.md: -------------------------------------------------------------------------------- 1 | Clon de ChatGPT desde cero con React y Tailwind 2 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/midudev/midu-chatgpt-clone/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {} 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "jsxSingleQuote": true, 5 | "trailingComma": "none" 6 | } -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "@/*": ["./src/*"] 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | } 5 | 6 | module.exports = nextConfig 7 | -------------------------------------------------------------------------------- /src/pages/_app.js: -------------------------------------------------------------------------------- 1 | import '@/styles/globals.css' 2 | 3 | export default function App({ Component, pageProps }) { 4 | return 5 | } 6 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "next/core-web-vitals", 4 | "./node_modules/standard/eslintrc.json" 5 | ], 6 | "rules": { 7 | "space-before-function-paren": "off" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/components/UserAvatar.jsx: -------------------------------------------------------------------------------- 1 | export function UserAvatar() { 2 | return ( 3 | Foto de midu 7 | ) 8 | } 9 | -------------------------------------------------------------------------------- /src/components/Avatar.jsx: -------------------------------------------------------------------------------- 1 | export function Avatar({ children }) { 2 | return ( 3 |
4 | {children} 5 |
6 | ) 7 | } 8 | -------------------------------------------------------------------------------- /src/pages/_document.js: -------------------------------------------------------------------------------- 1 | import { Html, Head, Main, NextScript } from 'next/document' 2 | 3 | export default function Document() { 4 | return ( 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /src/components/ErrorMessage.jsx: -------------------------------------------------------------------------------- 1 | export function ErrorMessage ({ message }) { 2 | return ( 3 |
An error occurred. Either the engine you requested does not exist or there was another issue processing your request. Please, try again later.
4 | ) 5 | } 6 | -------------------------------------------------------------------------------- /src/components/Layout.jsx: -------------------------------------------------------------------------------- 1 | import Head from 'next/head' 2 | import { Aside } from '@/components/Aside' 3 | 4 | export function Layout({ children }) { 5 | return ( 6 | <> 7 | 8 | midu Chat GPT 9 | 10 |
11 |
14 | 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /.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*.local 30 | 31 | # vercel 32 | .vercel 33 | -------------------------------------------------------------------------------- /src/components/ScrollToBottom.jsx: -------------------------------------------------------------------------------- 1 | export function ScrollToBottom () { 2 | return ( 3 | 6 | ) 7 | } 8 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ['./src/**/*.{js,ts,jsx,tsx}'], 4 | theme: { 5 | extend: { 6 | animation: { 7 | typing: 'blink 1s steps(5, start) infinite' 8 | }, 9 | backgroundImage: { 10 | gradient: 'linear-gradient(180deg,rgba(53,55,64,0),#353740 58.85%)' 11 | }, 12 | keyframes: { 13 | blink: { 14 | to: { visibility: 'hidden' } 15 | } 16 | }, 17 | colors: { 18 | gptlogo: '#10a37f', 19 | gptdarkgray: '#202123', 20 | gptgray: '#343541', 21 | gptlightgray: '#444654' 22 | } 23 | } 24 | }, 25 | plugins: [] 26 | } 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "midu-chat-gpt", 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 | "@formkit/auto-animate": "1.0.0-beta.6", 13 | "@next/font": "13.1.6", 14 | "eslint": "8.34.0", 15 | "eslint-config-next": "13.1.6", 16 | "highlight.js": "11.7.0", 17 | "lz-string": "1.4.4", 18 | "lz-ts": "1.1.2", 19 | "next": "13.1.6", 20 | "react": "18.2.0", 21 | "react-dom": "18.2.0", 22 | "snarkdown": "2.0.0", 23 | "zustand": "4.3.3" 24 | }, 25 | "devDependencies": { 26 | "autoprefixer": "10.4.13", 27 | "postcss": "8.4.21", 28 | "prettier": "2.8.4", 29 | "simple-zustand-devtools": "1.1.0", 30 | "standard": "17.0.0", 31 | "tailwindcss": "3.2.6" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /public/thirteen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/Message.jsx: -------------------------------------------------------------------------------- 1 | import snarkdown from 'snarkdown' 2 | import { Avatar } from '@/components/Avatar.jsx' 3 | import { TypingEffect } from '@/components/TypingEffect.jsx' 4 | import { UserAvatar } from '@/components/UserAvatar' 5 | import { ChatGPTLogo } from '@/components/Icons.jsx' 6 | import { ErrorMessage } from './ErrorMessage' 7 | 8 | export function Message({ ia, message, error }) { 9 | const avatar = ia ? : 10 | const textElement = ia 11 | ? 12 | : ( 13 |
17 | ) 18 | 19 | const renderContent = () => { 20 | if (error) return 21 | return textElement 22 | } 23 | 24 | return ( 25 |
26 |
27 | {avatar} 28 |
29 |
30 | {renderContent()} 31 |
32 |
33 |
34 |
35 | ) 36 | } 37 | -------------------------------------------------------------------------------- /public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/Twitch.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react' 2 | 3 | const useTwitchOnline = () => { 4 | const [online, setOnline] = useState(false) 5 | 6 | useEffect(() => { 7 | fetch('https://midudev-apis.midudev.workers.dev/uptime/midudev') 8 | .then(res => res.json()) 9 | .then(({ online }) => { 10 | setOnline(online) 11 | }) 12 | .catch(err => { 13 | console.error(err) 14 | }) 15 | }, []) 16 | 17 | return online 18 | } 19 | 20 | export default function TwitchStream () { 21 | const [open, setOpen] = useState(true) 22 | const online = useTwitchOnline() 23 | 24 | if (!online) return
25 | 26 | const { hostname } = document.location 27 | 28 | if (!open) return null 29 | 30 | return ( 31 |
32 | 33 |