├── .dockerignore ├── .eslintrc.json ├── .github ├── ISSUE_TEMPLATE │ └── enhancement.md └── workflows │ ├── CODEOWNERS │ ├── PULL_REQUEST_TEMPLATE.md │ ├── release.yml │ └── test.yml ├── .gitignore ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── data └── me.json ├── next.config.js ├── package.json ├── postcss.config.js ├── prettier.config.js ├── public └── favicon.ico ├── src ├── components │ ├── auth │ │ └── AddTokenModal.tsx │ ├── chat │ │ ├── AssistantMessageContent.tsx │ │ ├── ChatHeader.tsx │ │ ├── ChatInput.tsx │ │ ├── ChatMessage.tsx │ │ ├── ChatMessages.tsx │ │ ├── ChatPlaceholder.tsx │ │ ├── UserMessageContent.tsx │ │ └── sidebar │ │ │ ├── ChatSidebar.tsx │ │ │ ├── buttons │ │ │ ├── ApiKey.tsx │ │ │ ├── ButtonContainer.tsx │ │ │ ├── CurrentModel.tsx │ │ │ └── ThemeButton.tsx │ │ │ └── conversation │ │ │ ├── Conversation.tsx │ │ │ └── Conversations.tsx │ ├── hooks │ │ └── useModels.tsx │ ├── input │ │ ├── Dropdown.tsx │ │ ├── Slider.tsx │ │ └── TextArea.tsx │ ├── misc │ │ ├── Github.tsx │ │ └── GithubStar.tsx │ └── playground │ │ ├── AddMessage.tsx │ │ ├── ConfigSidebar.tsx │ │ ├── PlaygroundHeader.tsx │ │ ├── PlaygroundMessage.tsx │ │ ├── PlaygroundMessages.tsx │ │ ├── SystemMessage.tsx │ │ └── conversations │ │ ├── Conversation.tsx │ │ └── PlaygroundConversations.tsx ├── context │ ├── AuthProvider.tsx │ ├── OpenAIProvider.tsx │ └── PlaygroundProvider.tsx ├── pages │ ├── _app.tsx │ ├── _document.tsx │ ├── api │ │ ├── completion.ts │ │ └── models.ts │ ├── chat │ │ └── [id].tsx │ ├── index.tsx │ └── playground.tsx ├── styles │ └── globals.css └── utils │ ├── History.ts │ ├── OpenAI │ ├── OpenAI.constants.ts │ ├── OpenAI.ts │ ├── OpenAI.types.ts │ └── index.ts │ └── utils.ts ├── tailwind.config.js ├── tsconfig.json └── yarn.lock /.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | .dockerignore 3 | node_modules 4 | npm-debug.log 5 | README.md 6 | .next 7 | docker 8 | .git 9 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals", 3 | "rules": { 4 | "react-hooks/exhaustive-deps": "off" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/enhancement.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Enhancement Issue 3 | about: Create an enhancement issue 4 | title: Enhancement Issue 5 | labels: feature 6 | assignees: ks6088ts 7 | 8 | --- 9 | 10 | ## What would you like to be added? 11 | 12 | ## Why is this needed? 13 | -------------------------------------------------------------------------------- /.github/workflows/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @ks6088ts 2 | -------------------------------------------------------------------------------- /.github/workflows/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | #### What this PR does / why we need it: 4 | 5 | #### Which issue(s) this PR fixes: 6 | 7 | 11 | 12 | Fixes # 13 | 14 | #### Special notes for your reviewer: 15 | 16 | None 17 | 18 | #### Does this PR introduce a user-facing change? 19 | 20 | No 21 | 22 | #### Additional documentation 23 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | jobs: 9 | release: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Check out the repo 13 | uses: actions/checkout@v3 14 | - name: Log in to Docker Hub 15 | uses: docker/login-action@v2.0.0 16 | with: 17 | username: ${{ secrets.DOCKERHUB_USERNAME }} 18 | password: ${{ secrets.DOCKERHUB_TOKEN }} 19 | # https://github.com/docker/build-push-action/issues/42#issuecomment-915323168 20 | - name: Set Versions 21 | uses: actions/github-script@v4 22 | id: set_version 23 | with: 24 | script: | 25 | const tag = context.ref.substring(10) 26 | const no_v = tag.replace('v', '') 27 | const dash_index = no_v.lastIndexOf('-') 28 | const no_dash = (dash_index > -1) ? no_v.substring(0, dash_index) : no_v 29 | core.setOutput('tag', tag) 30 | core.setOutput('no-v', no_v) 31 | core.setOutput('no-dash', no_dash) 32 | - name: Build and push tag ${{steps.set_version.outputs.no-dash}} 33 | uses: docker/build-push-action@v4 34 | with: 35 | context: . 36 | push: true 37 | tags: | 38 | ${{ secrets.DOCKERHUB_USERNAME }}/azure-openai-playground:${{steps.set_version.outputs.no-dash}} 39 | ${{ secrets.DOCKERHUB_USERNAME }}/azure-openai-playground:latest 40 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: push 4 | 5 | jobs: 6 | test: 7 | name: Run CI test 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v3 11 | - uses: actions/setup-node@v3 12 | with: 13 | node-version: 18 14 | cache: yarn 15 | - name: Run CI test 16 | run: make ci-test 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 | .env 31 | 32 | # vercel 33 | .vercel 34 | 35 | # typescript 36 | *.tsbuildinfo 37 | next-env.d.ts 38 | node_modules 39 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18.16.0-bullseye-slim 2 | 3 | WORKDIR /usr/src/app 4 | 5 | COPY ./ ./ 6 | 7 | RUN yarn install --frozen-lockfile && yarn cache clean 8 | 9 | RUN yarn build 10 | 11 | EXPOSE 3000 12 | CMD ["yarn", "start"] 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Josh Nakka 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 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | GIT_REVISION ?= $(shell git rev-parse --short HEAD) 2 | GIT_TAG ?= $(shell git describe --tags --abbrev=0 | sed -e s/v//g) 3 | 4 | DOCKERHUB_USERNAME ?= ks6088ts 5 | DOCKER ?= docker 6 | DOCKER_IMAGE_NAME ?= azure-openai-playground 7 | DOCKER_COMMAND ?= 8 | DOCKER_TAG_NAME ?= $(DOCKERHUB_USERNAME)/$(DOCKER_IMAGE_NAME):$(GIT_TAG) 9 | 10 | AZURE_OPENAI_API_KEY ?= 11 | AZURE_OPENAI_NAME ?= 12 | AZURE_OPENAI_DEPLOYMENT_NAME ?= 13 | AZURE_OPENAI_API_VERSION ?= "2023-05-15" 14 | AZURE_OPENAI_API_URL ?= 15 | AZURE_OPENAI_API_STREAM_ENABLED ?= # Enabled by default. If set to `false`, stream option disabled 16 | 17 | PLATFORM ?= linux/amd64 18 | 19 | .PHONY: help 20 | help: 21 | @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' 22 | .DEFAULT_GOAL := help 23 | 24 | .PHONY: install-deps-dev 25 | install-deps-dev: ## install dependencies for development 26 | yarn install --frozen-lockfile 27 | 28 | .PHONY: ci-test 29 | ci-test: install-deps-dev ## ci-test 30 | yarn lint 31 | yarn build 32 | 33 | .PHONY: server 34 | server: ## server 35 | yarn dev 36 | 37 | .PHONY: docker-build 38 | docker-build: ## docker build 39 | $(DOCKER) build --platform=$(PLATFORM) -t $(DOCKER_TAG_NAME) . 40 | 41 | .PHONY: docker-run 42 | docker-run: ## docker run 43 | $(DOCKER) run --platform=$(PLATFORM) --rm \ 44 | -p "3000:3000" \ 45 | --env "AZURE_OPENAI_API_KEY=$(AZURE_OPENAI_API_KEY)" \ 46 | --env "AZURE_OPENAI_NAME=$(AZURE_OPENAI_NAME)" \ 47 | --env "AZURE_OPENAI_DEPLOYMENT_NAME=$(AZURE_OPENAI_DEPLOYMENT_NAME)" \ 48 | --env "AZURE_OPENAI_API_VERSION=$(AZURE_OPENAI_API_VERSION)" \ 49 | --env "AZURE_OPENAI_API_URL=$(AZURE_OPENAI_API_URL)" \ 50 | --env "AZURE_OPENAI_API_STREAM_ENABLED=$(AZURE_OPENAI_API_STREAM_ENABLED)" \ 51 | $(DOCKER_TAG_NAME) 52 | 53 | .PHONY: docker-push 54 | docker-push: ## docker push 55 | $(DOCKER) push $(DOCKER_TAG_NAME) 56 | 57 | AZURE_CONTAINER_APP_NAME ?= azure-openai-playground-$(GIT_REVISION) 58 | 59 | .PHONY: azure-deploy-aca 60 | azure-deploy-aca: ## deploy to azure container apps 61 | az containerapp up \ 62 | --name $(AZURE_CONTAINER_APP_NAME) \ 63 | --image $(DOCKER_TAG_NAME) \ 64 | --env-vars "AZURE_OPENAI_API_KEY=$(AZURE_OPENAI_API_KEY)" "AZURE_OPENAI_NAME=$(AZURE_OPENAI_NAME)" "AZURE_OPENAI_DEPLOYMENT_NAME=$(AZURE_OPENAI_DEPLOYMENT_NAME)" \ 65 | --environment "production" \ 66 | --ingress external \ 67 | --target-port 3000 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![test](https://github.com/ks6088ts-labs/azure-openai-playground/actions/workflows/test.yml/badge.svg)](https://github.com/ks6088ts-labs/azure-openai-playground/actions/workflows/test.yml) 2 | [![release](https://github.com/ks6088ts-labs/azure-openai-playground/actions/workflows/release.yml/badge.svg)](https://github.com/ks6088ts-labs/azure-openai-playground/actions/workflows/release.yml) 3 | 4 | # GPT-4 Playground 5 | 6 | Just got your GPT-4 API Key and want to give it a spin? Look not further! This project is mainly targeted to allow you to test out your Open AI API keys. The current OpenAI Playground still only allows 4096 tokens for 8k or 32k models like GPT-4 and if you would like to test out you key in a rendered chat environment you would have to purchase ChatCPT Plus. This project should fix both of those issues without comprimising on either experience. The project aims to preserve as much of the vanilla experience as possible while also providing a link between the the playground and ChatGPT to enable a better developer experience. 7 | 8 | As a side note, all API keys are encrypted and stored in your browser's local storage, so you can use this project without having to worry about your API key being stolen. 9 | 10 | ## Demo 11 | 12 | ### Mock ChatGPT Environment 13 | This environment has most of the critical features like conversation history (which is stored locally), prompting, and multiple conversations. This environment is a great way to test out your API key and see how it works! 14 | ![ChatGpt-4 ChatGPT](https://i.imgur.com/DfTbV9d.png) 15 | 16 | ### Playground Environment 17 | ![ChatGpt-4 Playground](https://i.imgur.com/DS6NPH2.png) 18 | 19 | ## Running Locally 20 | To run this project locally, you will need to have [Node.js](https://nodejs.org/en/) installed. Once you have Node.js installed, you can clone this repository and run the following commands: 21 | 22 | ```bash 23 | yarn install 24 | yarn dev 25 | ``` 26 | 27 | This will start a local server on port 3000. You can then navigate to `localhost:3000` to view the project! 28 | 29 | ## Contributing 30 | 31 | **This project is still in development! Contributions are very much appreciated!** 32 | 33 | If you would like to contribute to this project, please feel free to open a pull request or an issue, I hashed this project out in a few hours so there are bound to be some bugs! 34 | -------------------------------------------------------------------------------- /data/me.json: -------------------------------------------------------------------------------- 1 | { 2 | "me": { 3 | "access_token": "access_token", 4 | "expires_on": "2023-06-13T08:52:48.7389101Z", 5 | "provider_name": "aad", 6 | "command": "json-server --watch data/me.json -p 8888" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | env: { 5 | AZURE_OPENAI_API_KEY: process.env.AZURE_OPENAI_API_KEY, 6 | AZURE_OPENAI_NAME: process.env.AZURE_OPENAI_NAME, 7 | AZURE_OPENAI_DEPLOYMENT_NAME: process.env.AZURE_OPENAI_DEPLOYMENT_NAME, 8 | AZURE_OPENAI_API_VERSION: process.env.AZURE_OPENAI_API_VERSION, 9 | AZURE_OPENAI_API_URL: process.env.AZURE_OPENAI_API_URL, 10 | AZURE_OPENAI_API_STREAM_ENABLED: process.env.AZURE_OPENAI_API_STREAM_ENABLED, 11 | AZURE_OPENAI_SITE_BRANDING: process.env.AZURE_OPENAI_SITE_BRANDING, 12 | }, 13 | } 14 | 15 | module.exports = nextConfig 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gpt4-playground", 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 | "format": "prettier --write src/**/*.{js,jsx,ts,tsx}" 11 | }, 12 | "dependencies": { 13 | "@headlessui/react": "^1.7.13", 14 | "@types/node": "18.15.10", 15 | "@types/react": "18.0.30", 16 | "@types/react-dom": "18.0.11", 17 | "@vercel/analytics": "^0.1.11", 18 | "eslint": "8.36.0", 19 | "eslint-config-next": "13.2.4", 20 | "eventsource-parser": "^1.0.0", 21 | "next": "13.2.4", 22 | "openai": "^3.2.1", 23 | "parse-numeric-range": "^1.3.0", 24 | "react": "18.2.0", 25 | "react-dom": "18.2.0", 26 | "react-icons": "^4.8.0", 27 | "react-markdown": "^8.0.6", 28 | "react-mathjax": "^1.0.1", 29 | "react-secure-storage": "^1.2.0", 30 | "react-slider": "^2.0.4", 31 | "react-syntax-highlighter": "^15.5.0", 32 | "rehype-katex": "^6.0.2", 33 | "remark-math": "^5.1.1", 34 | "typescript": "5.0.2", 35 | "uuid": "^9.0.0" 36 | }, 37 | "devDependencies": { 38 | "@tailwindcss/typography": "^0.5.9", 39 | "@types/react-mathjax": "^1.0.1", 40 | "@types/react-slider": "^1.3.1", 41 | "@types/react-syntax-highlighter": "^15.5.6", 42 | "@types/uuid": "^9.0.1", 43 | "autoprefixer": "^10.4.14", 44 | "postcss": "^8.4.21", 45 | "prettier": "^2.8.7", 46 | "prettier-plugin-tailwindcss": "^0.2.5", 47 | "tailwind-scrollbar": "^3.0.0", 48 | "tailwindcss": "^3.3.0" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [require('prettier-plugin-tailwindcss')], 3 | } -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ks6088ts-labs/azure-openai-playground/2ab3f58117f30e4f049a50854a290eb151b1570b/public/favicon.ico -------------------------------------------------------------------------------- /src/components/auth/AddTokenModal.tsx: -------------------------------------------------------------------------------- 1 | import { useAuth } from "@/context/AuthProvider"; 2 | import React from "react"; 3 | import { MdClose, MdToken } from "react-icons/md"; 4 | 5 | type Props = { 6 | className?: string; 7 | }; 8 | 9 | export default function AddTokenModal({ className }: Props) { 10 | const { token, addToken, clearToken } = useAuth(); 11 | const [open, setOpen] = React.useState(false); 12 | const [input, setInput] = React.useState(token); 13 | 14 | const handleInput = (e: React.ChangeEvent) => { 15 | setInput(e.target.value); 16 | }; 17 | 18 | const handleClear = () => { 19 | clearToken(); 20 | setOpen(false); 21 | }; 22 | 23 | const handleSubmit = (e: React.FormEvent) => { 24 | e.preventDefault(); 25 | addToken(input); 26 | setOpen(false); 27 | }; 28 | 29 | return ( 30 | <> 31 | 37 | 43 | {open && ( 44 |
45 |
46 |
47 | 53 |
54 |

55 | Your API token 56 |

57 |

58 | You can get your API token from the{" "} 59 | 65 | OpenAI dashboard 66 | 67 | . All requests are made on the client side, so your token is never 68 | sent to the server. If you would like more information look at the{" "} 69 | 75 | Github Repository 76 | 77 | ! 78 |

79 |
80 | 87 |
88 | 95 | 101 |
102 |
103 |
104 |
105 | )} 106 | 107 | ); 108 | } 109 | -------------------------------------------------------------------------------- /src/components/chat/AssistantMessageContent.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactMarkdown from "react-markdown"; 3 | import rangeParser from "parse-numeric-range"; 4 | import { oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism"; 5 | 6 | import { PrismLight as SyntaxHighlighter } from "react-syntax-highlighter"; 7 | import tsx from "react-syntax-highlighter/dist/cjs/languages/prism/tsx"; 8 | import typescript from "react-syntax-highlighter/dist/cjs/languages/prism/typescript"; 9 | import scss from "react-syntax-highlighter/dist/cjs/languages/prism/scss"; 10 | import bash from "react-syntax-highlighter/dist/cjs/languages/prism/bash"; 11 | import markdown from "react-syntax-highlighter/dist/cjs/languages/prism/markdown"; 12 | import python from "react-syntax-highlighter/dist/cjs/languages/prism/python"; 13 | import cpp from "react-syntax-highlighter/dist/cjs/languages/prism/cpp"; 14 | import json from "react-syntax-highlighter/dist/cjs/languages/prism/json"; 15 | import MathJax from "react-mathjax"; 16 | import remarkMath from "remark-math"; 17 | import rehypeKatex from "rehype-katex"; 18 | 19 | import "katex/dist/katex.min.css"; // `rehype-katex` does not import the CSS for you 20 | 21 | SyntaxHighlighter.registerLanguage("tsx", tsx); 22 | SyntaxHighlighter.registerLanguage("typescript", typescript); 23 | SyntaxHighlighter.registerLanguage("scss", scss); 24 | SyntaxHighlighter.registerLanguage("bash", bash); 25 | SyntaxHighlighter.registerLanguage("markdown", markdown); 26 | SyntaxHighlighter.registerLanguage("python", python); 27 | SyntaxHighlighter.registerLanguage("cpp", cpp); 28 | SyntaxHighlighter.registerLanguage("json", json); 29 | SyntaxHighlighter.registerLanguage("json", json); 30 | 31 | const syntaxTheme = oneDark; 32 | 33 | type Props = { 34 | content: string; 35 | }; 36 | 37 | export default function AssistantMessageContent({ content, ...props }: Props) { 38 | const MarkdownComponents: any = { 39 | // Work around for not rending and tags 40 | em: ({ node, inline, className, children, ...props }: any) => { 41 | return ( 42 | 43 | _{children}_ 44 | 45 | ); 46 | }, 47 | strong: ({ node, inline, className, children, ...props }: any) => { 48 | return ( 49 | 50 | __{children}__ 51 | 52 | ); 53 | }, 54 | 55 | pre: ({ node, inline, className, children, ...props }: any) => { 56 | return ( 57 |
 58 |           {children}
 59 |         
60 | ); 61 | }, 62 | 63 | math: (props: any) => , 64 | inlineMath: (props: any) => , 65 | 66 | code({ node, inline, className, ...props }: any) { 67 | const hasLang = /language-(\w+)/.exec(className || ""); 68 | const hasMeta = node?.data?.meta; 69 | 70 | const applyHighlights: object = (applyHighlights: number) => { 71 | if (hasMeta) { 72 | const RE = /{([\d,-]+)}/; 73 | const metadata = node.data.meta?.replace(/\s/g, ""); 74 | const strlineNumbers = RE?.test(metadata) 75 | ? RE?.exec(metadata)?.[1] 76 | : "0"; 77 | const highlightLines = rangeParser(strlineNumbers || "0"); 78 | const highlight = highlightLines; 79 | const data: string = highlight.includes(applyHighlights) 80 | ? "highlight" 81 | : ""; 82 | return { data }; 83 | } else { 84 | return {}; 85 | } 86 | }; 87 | 88 | return hasLang ? ( 89 | 99 | {props.children} 100 | 101 | ) : ( 102 | 103 | ); 104 | }, 105 | }; 106 | 107 | return ( 108 | 114 | {content} 115 | 116 | ); 117 | } 118 | -------------------------------------------------------------------------------- /src/components/chat/ChatHeader.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { MdClose, MdMenu } from "react-icons/md"; 3 | import ChatSidebar from "@/components/chat/sidebar/ChatSidebar"; 4 | import { Transition } from "@headlessui/react"; 5 | import AddTokenModal from "./../auth/AddTokenModal"; 6 | 7 | type Props = {}; 8 | 9 | export default function ChatHeader({}: Props) { 10 | const [isOpen, setIsOpen] = React.useState(false); 11 | 12 | return ( 13 | <> 14 |
15 | 18 | 19 | 20 |
21 | {/* Animate slide in from left */} 22 | 32 |
33 | 34 |
35 | 41 |
42 | 43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /src/components/chat/ChatInput.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useOpenAI } from "@/context/OpenAIProvider"; 3 | import { MdSend } from "react-icons/md"; 4 | 5 | type Props = {}; 6 | 7 | export default function ChatInput({}: Props) { 8 | const { addMessage, loading } = useOpenAI(); 9 | const textAreaRef = React.useRef(null); 10 | 11 | const [input, setInput] = React.useState(""); 12 | 13 | const handleChange = (e: React.ChangeEvent) => { 14 | setInput(e.target.value); 15 | }; 16 | 17 | const handleSubmit = (e: React.FormEvent) => { 18 | if (loading) return; 19 | e.preventDefault(); 20 | addMessage(input, true, "user"); 21 | setInput(""); 22 | }; 23 | 24 | React.useEffect(() => { 25 | const resize = () => { 26 | if (textAreaRef.current) { 27 | textAreaRef.current.style.height = "40px"; 28 | textAreaRef.current.style.height = `${textAreaRef.current.scrollHeight}px`; 29 | } 30 | }; 31 | 32 | resize(); 33 | }, [input]); 34 | 35 | // Handle submitting with enter 36 | React.useEffect(() => { 37 | const handleKeyDown = (e: KeyboardEvent) => { 38 | if (e.key === "Enter" && !e.shiftKey) { 39 | e.preventDefault(); 40 | handleSubmit(e as any); 41 | } 42 | }; 43 | 44 | document.addEventListener("keydown", handleKeyDown); 45 | 46 | return () => { 47 | document.removeEventListener("keydown", handleKeyDown); 48 | }; 49 | }, [handleSubmit]); 50 | 51 | return ( 52 |
53 |
57 |
58 |