From a6ebbb28022987a056b2897f3b8e66cbd7a62eee Mon Sep 17 00:00:00 2001 From: Zhang Minghan Date: Mon, 18 Sep 2023 21:26:27 +0800 Subject: [PATCH] add code generation feature --- app/src/assets/generation.less | 88 +++++++++++++++++++++++++++ app/src/assets/main.less | 1 + app/src/assets/ui.less | 27 +++++++++ app/src/components/SelectGroup.tsx | 25 ++++++++ app/src/i18n.ts | 3 + app/src/router.ts | 6 ++ app/src/routes/Generation.tsx | 97 ++++++++++++++++++++++++++++++ 7 files changed, 247 insertions(+) create mode 100644 app/src/assets/generation.less create mode 100644 app/src/assets/ui.less create mode 100644 app/src/components/SelectGroup.tsx create mode 100644 app/src/routes/Generation.tsx diff --git a/app/src/assets/generation.less b/app/src/assets/generation.less new file mode 100644 index 0000000..341ff1d --- /dev/null +++ b/app/src/assets/generation.less @@ -0,0 +1,88 @@ +.generation-page { + position: relative; + display: flex; + width: 100%; + height: calc(100vh - 56px); + + .login-action { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + margin: auto; + transform: translateY(-28px); + + .tip { + display: flex; + flex-direction: row; + align-items: center; + margin-bottom: max(6vh, 24px); + user-select: none; + } + + .text { + font-size: 1rem; + } + } +} + +.generation-container { + display: flex; + flex-direction: column; + padding: 12px 16px; + gap: 6px; + width: 100%; + height: 100%; + + .action { + flex-shrink: 0; + } + + .generation-wrapper { + display: flex; + flex-direction: column; + align-items: center; + flex-grow: 1; + padding: 15vh 0; + gap: 2rem; + + .product { + display: flex; + flex-direction: row; + align-items: center; + text-align: center; + font-size: 2rem; + gap: 12px; + user-select: none; + + img { + width: 3rem; + height: 3rem; + } + } + } +} + +.input-box { + display: flex; + flex-direction: row; + width: 80%; + gap: 8px; + margin: 0 auto; + max-width: 680px; + + .input { + flex-grow: 1; + text-align: center; + font-size: 1.25rem; + height: 46px; + border-radius: var(--radius); + border: 1px solid hsl(var(--border-hover)); + letter-spacing: 1px; + } + + .action { + width: 46px; + height: 46px; + } +} diff --git a/app/src/assets/main.less b/app/src/assets/main.less index 2f18a61..4ac19bb 100644 --- a/app/src/assets/main.less +++ b/app/src/assets/main.less @@ -1,3 +1,4 @@ +@import "ui"; @font-family: Andika,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji"; @line-height: 1.5; @font-weight: 400; diff --git a/app/src/assets/ui.less b/app/src/assets/ui.less new file mode 100644 index 0000000..6cd02f1 --- /dev/null +++ b/app/src/assets/ui.less @@ -0,0 +1,27 @@ +.select-group { + display: flex; + flex-direction: row; + flex-wrap: wrap; + gap: 8px; + padding: 6px 8px; + border-radius: 4px; + user-select: none; + + .select-group-item { + padding: 0.35rem 0.5rem; + border-radius: 4px; + transition: .2s; + cursor: pointer; + font-size: 16px; + background: hsl(var(--accent-secondary)); + + &:hover { + background: hsl(var(--accent)); + } + + &.active { + background: hsl(var(--text)); + color: hsl(var(--background)); + } + } +} diff --git a/app/src/components/SelectGroup.tsx b/app/src/components/SelectGroup.tsx new file mode 100644 index 0000000..9873b9f --- /dev/null +++ b/app/src/components/SelectGroup.tsx @@ -0,0 +1,25 @@ +type SelectGroupProps = { + current: string, + list: string[], + onChange?: (select: string) => void, +} + +function SelectGroup(props: SelectGroupProps) { + return ( +
+ { + props.list.map((select: string, idx: number) => ( +
props.onChange?.(select)} + className={`select-group-item ${select == props.current ? 'active' : ''}`} + > + { select } +
+ )) + } +
+ ) +} + +export default SelectGroup; diff --git a/app/src/i18n.ts b/app/src/i18n.ts index 19b4e9f..eb14ddc 100644 --- a/app/src/i18n.ts +++ b/app/src/i18n.ts @@ -11,6 +11,7 @@ const resources = { "not-found": "Page not found", home: "Home", login: "Login", + "login-require": "You need to login to use this feature", logout: "Logout", quota: "Quota", "try-again": "Try again", @@ -151,6 +152,7 @@ const resources = { "not-found": "页面未找到", home: "首页", login: "登录", + "login-require": "您需要登录才能使用此功能", logout: "登出", quota: "配额", "try-again": "重试", @@ -281,6 +283,7 @@ const resources = { "not-found": "Страница не найдена", home: "Главная", login: "Войти", + "login-require": "Вам нужно войти, чтобы использовать эту функцию", logout: "Выйти", quota: "Квота", "try-again": "Попробуйте еще раз", diff --git a/app/src/router.ts b/app/src/router.ts index 0ee0df5..5c2af1a 100644 --- a/app/src/router.ts +++ b/app/src/router.ts @@ -2,6 +2,7 @@ import { createBrowserRouter } from "react-router-dom"; import Home from "./routes/Home.tsx"; import NotFound from "./routes/NotFound.tsx"; import Auth from "./routes/Auth.tsx"; +import Generation from "./routes/Generation.tsx"; const router = createBrowserRouter([ { @@ -16,6 +17,11 @@ const router = createBrowserRouter([ Component: Auth, ErrorBoundary: NotFound, }, + { + id: "generation", + path: "/generate", + Component: Generation, + } ]); export default router; diff --git a/app/src/routes/Generation.tsx b/app/src/routes/Generation.tsx new file mode 100644 index 0000000..dac7e7c --- /dev/null +++ b/app/src/routes/Generation.tsx @@ -0,0 +1,97 @@ +import "../assets/generation.less"; +import {useSelector} from "react-redux"; +import {selectAuthenticated} from "../store/auth.ts"; +import {useTranslation} from "react-i18next"; +import {Button} from "../components/ui/button.tsx"; +import {ChevronLeft, Info, LogIn, Send} from "lucide-react"; +import {login} from "../conf.ts"; +import router from "../router.ts"; +import {Input} from "../components/ui/input.tsx"; +import {useEffect, useRef, useState} from "react"; +import SelectGroup from "../components/SelectGroup.tsx"; + +type WrapperProps = { + onSend?: (value: string) => boolean, +} + +function Wrapper(props: WrapperProps) { + const ref = useRef(null); + const [ model, setModel ] = useState('GPT-3.5'); + + function handleSend() { + const target = ref.current as HTMLInputElement | null; + if (!target) return; + + const value = target.value.trim(); + if (!value.length) return; + + if (props.onSend?.(value)) { + target.value = ''; + } + } + + useEffect(() => { + ref.current && (ref.current as HTMLInputElement).focus(); + ref.current && (ref.current as HTMLInputElement).addEventListener('keydown', (e) => { + if (e.key === 'Enter') { + handleSend(); + } + }); + }) + return ( +
+
+ {""} + AI Code Generator +
+
+ + +
+
+ +
+
+ ) +} +function Generation() { + const { t } = useTranslation(); + const auth = useSelector(selectAuthenticated); + + return ( +
+ { + auth ? +
+ + { + console.log(value); + return true; + }} /> +
: +
+
+ + { t('login-require') } +
+ +
+ } +
+ ) +} + +export default Generation;