update code struct

This commit is contained in:
Zhang Minghan 2023-10-23 23:39:03 +08:00
parent 435308de41
commit f9949a86f7
18 changed files with 224 additions and 162 deletions

View File

@ -25,6 +25,7 @@
"@radix-ui/react-tooltip": "^1.0.6", "@radix-ui/react-tooltip": "^1.0.6",
"@reduxjs/toolkit": "^1.9.5", "@reduxjs/toolkit": "^1.9.5",
"axios": "^1.5.0", "axios": "^1.5.0",
"chart.js": "^4.4.0",
"class-variance-authority": "^0.7.0", "class-variance-authority": "^0.7.0",
"clsx": "^2.0.0", "clsx": "^2.0.0",
"i18next": "^23.4.6", "i18next": "^23.4.6",
@ -32,11 +33,12 @@
"lucide-react": "^0.274.0", "lucide-react": "^0.274.0",
"match-sorter": "^6.3.1", "match-sorter": "^6.3.1",
"react": "^18.2.0", "react": "^18.2.0",
"react-chartjs-2": "^5.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-i18next": "^13.2.2", "react-i18next": "^13.2.2",
"react-markdown": "^8.0.7", "react-markdown": "^8.0.7",
"react-redux": "^8.1.2", "react-redux": "^8.1.2",
"react-router-dom": "^6.15.0", "react-router-dom": "^5.1.2",
"react-syntax-highlighter": "^15.5.0", "react-syntax-highlighter": "^15.5.0",
"rehype-katex": "^6.0.3", "rehype-katex": "^6.0.3",
"remark-gfm": "^3.0.1", "remark-gfm": "^3.0.1",

27
app/pnpm-lock.yaml generated
View File

@ -47,6 +47,9 @@ dependencies:
axios: axios:
specifier: ^1.5.0 specifier: ^1.5.0
version: 1.5.0 version: 1.5.0
chart.js:
specifier: ^4.4.0
version: 4.4.0
class-variance-authority: class-variance-authority:
specifier: ^0.7.0 specifier: ^0.7.0
version: 0.7.0 version: 0.7.0
@ -68,6 +71,9 @@ dependencies:
react: react:
specifier: ^18.2.0 specifier: ^18.2.0
version: 18.2.0 version: 18.2.0
react-chartjs-2:
specifier: ^5.2.0
version: 5.2.0(chart.js@4.4.0)(react@18.2.0)
react-dom: react-dom:
specifier: ^18.2.0 specifier: ^18.2.0
version: 18.2.0(react@18.2.0) version: 18.2.0(react@18.2.0)
@ -1669,6 +1675,10 @@ packages:
'@jridgewell/resolve-uri': 3.1.1 '@jridgewell/resolve-uri': 3.1.1
'@jridgewell/sourcemap-codec': 1.4.15 '@jridgewell/sourcemap-codec': 1.4.15
/@kurkle/color@0.3.2:
resolution: {integrity: sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==}
dev: false
/@nodelib/fs.scandir@2.1.5: /@nodelib/fs.scandir@2.1.5:
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'} engines: {node: '>= 8'}
@ -3375,6 +3385,13 @@ packages:
resolution: {integrity: sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==} resolution: {integrity: sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==}
dev: false dev: false
/chart.js@4.4.0:
resolution: {integrity: sha512-vQEj6d+z0dcsKLlQvbKIMYFHd3t8W/7L2vfJIbYcfyPcRx92CsHqECpueN8qVGNlKyDcr5wBrYAYKnfu/9Q1hQ==}
engines: {pnpm: '>=7'}
dependencies:
'@kurkle/color': 0.3.2
dev: false
/chokidar@3.5.3: /chokidar@3.5.3:
resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
engines: {node: '>= 8.10.0'} engines: {node: '>= 8.10.0'}
@ -5742,6 +5759,16 @@ packages:
safe-buffer: 5.2.1 safe-buffer: 5.2.1
dev: true dev: true
/react-chartjs-2@5.2.0(chart.js@4.4.0)(react@18.2.0):
resolution: {integrity: sha512-98iN5aguJyVSxp5U3CblRLH67J8gkfyGNbiK3c+l1QI/G4irHMPQw44aEPmjVag+YKTyQ260NcF82GTQ3bdscA==}
peerDependencies:
chart.js: ^4.1.1
react: ^16.8.0 || ^17.0.0 || ^18.0.0
dependencies:
chart.js: 4.4.0
react: 18.2.0
dev: false
/react-dom@18.2.0(react@18.2.0): /react-dom@18.2.0(react@18.2.0):
resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==}
peerDependencies: peerDependencies:

View File

@ -1,162 +1,13 @@
import { RouterProvider } from "react-router-dom"; import { Provider } from "react-redux";
import "./assets/navbar.less";
import ModeToggle, { ThemeProvider } from "./components/ThemeProvider.tsx";
import { Button } from "./components/ui/button.tsx";
import router from "./router.ts";
import I18nProvider from "./components/I18nProvider.tsx";
import ProjectLink from "./components/ProjectLink.tsx";
import {
BadgeCent,
Boxes,
CalendarPlus,
Cloud,
ListStart,
Menu,
Plug,
} from "lucide-react";
import { Provider, useDispatch, useSelector } from "react-redux";
import { toggleMenu } from "./store/menu.ts";
import store from "./store/index.ts"; import store from "./store/index.ts";
import { import AppProvider from "./components/app/AppProvider.tsx";
logout, import { AppRouter } from "./router.tsx";
selectAuthenticated,
selectUsername,
validateToken,
} from "./store/auth.ts";
import { useEffect } from "react";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "./components/ui/dropdown-menu.tsx";
import { Toaster } from "./components/ui/toaster.tsx";
import { login, tokenField } from "./conf.ts";
import { useTranslation } from "react-i18next";
import Quota from "./routes/Quota.tsx";
import { openDialog as openQuotaDialog, quotaSelector } from "./store/quota.ts";
import { openDialog as openPackageDialog } from "./store/package.ts";
import { openDialog as openSub } from "./store/subscription.ts";
import { openDialog as openApiDialog } from "./store/api.ts";
import { openDialog as openSharingDialog } from "./store/sharing.ts";
import Package from "./routes/Package.tsx";
import Subscription from "./routes/Subscription.tsx";
import ApiKey from "./routes/ApiKey.tsx";
import ShareManagement from "./routes/ShareManagement.tsx";
function Settings() {
const { t } = useTranslation();
const dispatch = useDispatch();
const username = useSelector(selectUsername);
const quota = useSelector(quotaSelector);
return (
<div className={`avatar`}>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant={`ghost`} size={`icon`}>
<img src={`https://api.deeptrain.net/avatar/${username}`} alt="" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align={`end`}>
<DropdownMenuLabel className={`username`}>
{username}
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={() => dispatch(openQuotaDialog())}>
<Cloud className={`h-4 w-4 mr-1`} />
{quota}
</DropdownMenuItem>
<DropdownMenuItem onClick={() => dispatch(openQuotaDialog())}>
<BadgeCent className={`h-4 w-4 mr-1`} />
{t("quota")}
</DropdownMenuItem>
<DropdownMenuItem onClick={() => dispatch(openSub())}>
<CalendarPlus className={`h-4 w-4 mr-1`} />
{t("sub.title")}
</DropdownMenuItem>
<DropdownMenuItem onClick={() => dispatch(openPackageDialog())}>
<Boxes className={`h-4 w-4 mr-1`} />
{t("pkg.title")}
</DropdownMenuItem>
<DropdownMenuItem onClick={() => dispatch(openSharingDialog())}>
<ListStart className={`h-4 w-4 mr-1`} />
{t("share.manage")}
</DropdownMenuItem>
<DropdownMenuItem onClick={() => dispatch(openApiDialog())}>
<Plug className={`h-4 w-4 mr-1`} />
{t("api.title")}
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem asChild>
<Button
size={`sm`}
className={`action-button`}
onClick={() => dispatch(logout())}
>
{t("logout")}
</Button>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
);
}
function NavBar() {
const { t } = useTranslation();
const dispatch = useDispatch();
useEffect(() => {
validateToken(dispatch, localStorage.getItem(tokenField) ?? "");
}, []);
const auth = useSelector(selectAuthenticated);
return (
<nav className={`navbar`}>
<div className={`items`}>
<Button
size={`icon`}
variant={`ghost`}
onClick={() => dispatch(toggleMenu())}
>
<Menu />
</Button>
<img
className={`logo`}
src="/favicon.ico"
alt=""
onClick={() => router.navigate("/")}
/>
<div className={`grow`} />
<ProjectLink />
<ModeToggle />
<I18nProvider />
{auth ? (
<Settings />
) : (
<Button size={`sm`} onClick={login}>
{t("login")}
</Button>
)}
</div>
</nav>
);
}
function App() { function App() {
return ( return (
<Provider store={store}> <Provider store={store}>
<NavBar /> <AppProvider />
<ThemeProvider /> <AppRouter />
<RouterProvider router={router} />
<Toaster />
<Quota />
<ApiKey />
<Package />
<Subscription />
<ShareManagement />
</Provider> </Provider>
); );
} }

View File

@ -0,0 +1,15 @@
import NavBar from "./NavBar.tsx";
import { ThemeProvider } from "../ThemeProvider.tsx";
import DialogManager from "../../dialogs";
function AppProvider() {
return (
<>
<NavBar />
<ThemeProvider />
<DialogManager />
</>
);
}
export default AppProvider;

View File

@ -0,0 +1,142 @@
import "@/assets/navbar.less";
import { useTranslation } from "react-i18next";
import { useDispatch, useSelector } from "react-redux";
import {
logout,
selectAuthenticated,
selectUsername,
validateToken,
} from "../../store/auth.ts";
import {
openDialog as openQuotaDialog,
quotaSelector,
} from "../../store/quota.ts";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "../ui/dropdown-menu.tsx";
import { Button } from "../ui/button.tsx";
import {
BadgeCent,
Boxes,
CalendarPlus,
Cloud,
ListStart,
Plug,
} from "lucide-react";
import { openDialog as openSub } from "../../store/subscription.ts";
import { openDialog as openPackageDialog } from "../../store/package.ts";
import { openDialog as openSharingDialog } from "../../store/sharing.ts";
import { openDialog as openApiDialog } from "../../store/api.ts";
import { useEffect } from "react";
import { login, tokenField } from "../../conf.ts";
import { toggleMenu } from "../../store/menu.ts";
import ProjectLink from "../ProjectLink.tsx";
import ModeToggle from "../ThemeProvider.tsx";
import I18nProvider from "../I18nProvider.tsx";
import router from "../../router.tsx";
function Menu() {
const { t } = useTranslation();
const dispatch = useDispatch();
const username = useSelector(selectUsername);
const quota = useSelector(quotaSelector);
return (
<div className={`avatar`}>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant={`ghost`} size={`icon`}>
<img src={`https://api.deeptrain.net/avatar/${username}`} alt="" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align={`end`}>
<DropdownMenuLabel className={`username`}>
{username}
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={() => dispatch(openQuotaDialog())}>
<Cloud className={`h-4 w-4 mr-1`} />
{quota}
</DropdownMenuItem>
<DropdownMenuItem onClick={() => dispatch(openQuotaDialog())}>
<BadgeCent className={`h-4 w-4 mr-1`} />
{t("quota")}
</DropdownMenuItem>
<DropdownMenuItem onClick={() => dispatch(openSub())}>
<CalendarPlus className={`h-4 w-4 mr-1`} />
{t("sub.title")}
</DropdownMenuItem>
<DropdownMenuItem onClick={() => dispatch(openPackageDialog())}>
<Boxes className={`h-4 w-4 mr-1`} />
{t("pkg.title")}
</DropdownMenuItem>
<DropdownMenuItem onClick={() => dispatch(openSharingDialog())}>
<ListStart className={`h-4 w-4 mr-1`} />
{t("share.manage")}
</DropdownMenuItem>
<DropdownMenuItem onClick={() => dispatch(openApiDialog())}>
<Plug className={`h-4 w-4 mr-1`} />
{t("api.title")}
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem asChild>
<Button
size={`sm`}
className={`action-button`}
onClick={() => dispatch(logout())}
>
{t("logout")}
</Button>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
);
}
function NavBar() {
const { t } = useTranslation();
const dispatch = useDispatch();
useEffect(() => {
validateToken(dispatch, localStorage.getItem(tokenField) ?? "");
}, []);
const auth = useSelector(selectAuthenticated);
return (
<nav className={`navbar`}>
<div className={`items`}>
<Button
size={`icon`}
variant={`ghost`}
onClick={() => dispatch(toggleMenu())}
>
<Menu />
</Button>
<img
className={`logo`}
src="/favicon.ico"
alt=""
onClick={() => router.navigate("/")}
/>
<div className={`grow`} />
<ProjectLink />
<ModeToggle />
<I18nProvider />
{auth ? (
<Menu />
) : (
<Button size={`sm`} onClick={login}>
{t("login")}
</Button>
)}
</div>
</nav>
);
}
export default NavBar;

View File

@ -13,7 +13,7 @@ import { manager } from "../../conversation/manager.ts";
import { formatMessage } from "../../utils.ts"; import { formatMessage } from "../../utils.ts";
import ChatInterface from "./ChatInterface.tsx"; import ChatInterface from "./ChatInterface.tsx";
import { Button } from "../ui/button.tsx"; import { Button } from "../ui/button.tsx";
import router from "../../router.ts"; import router from "../../router.tsx";
import { BookMarked, ChevronRight, FolderKanban, Globe } from "lucide-react"; import { BookMarked, ChevronRight, FolderKanban, Globe } from "lucide-react";
import { import {
Tooltip, Tooltip,

View File

@ -1,7 +1,7 @@
import axios from "axios"; import axios from "axios";
import { Model } from "./conversation/types.ts"; import { Model } from "./conversation/types.ts";
export const version = "3.5.0"; export const version = "3.5.1";
export const dev: boolean = window.location.hostname === "localhost"; export const dev: boolean = window.location.hostname === "localhost";
export const deploy: boolean = true; export const deploy: boolean = true;
export let rest_api: string = "http://localhost:8094"; export let rest_api: string = "http://localhost:8094";

21
app/src/dialogs/index.tsx Normal file
View File

@ -0,0 +1,21 @@
import { Toaster } from "../components/ui/toaster.tsx";
import Quota from "./Quota.tsx";
import ApiKey from "./ApiKey.tsx";
import Package from "./Package.tsx";
import Subscription from "./Subscription.tsx";
import ShareManagement from "./ShareManagement.tsx";
function DialogManager() {
return (
<>
<Toaster />
<Quota />
<ApiKey />
<Package />
<Subscription />
<ShareManagement />
</>
);
}
export default DialogManager;

View File

@ -1,4 +1,4 @@
import { createBrowserRouter } from "react-router-dom"; import { createBrowserRouter, RouterProvider } from "react-router-dom";
import Home from "./routes/Home.tsx"; import Home from "./routes/Home.tsx";
import NotFound from "./routes/NotFound.tsx"; import NotFound from "./routes/NotFound.tsx";
import Auth from "./routes/Auth.tsx"; import Auth from "./routes/Auth.tsx";
@ -30,4 +30,8 @@ const router = createBrowserRouter([
}, },
]); ]);
export function AppRouter() {
return <RouterProvider router={router} />;
}
export default router; export default router;

View File

@ -8,7 +8,7 @@ import "../assets/auth.less";
import axios from "axios"; import axios from "axios";
import { validateToken } from "../store/auth.ts"; import { validateToken } from "../store/auth.ts";
import { useDispatch } from "react-redux"; import { useDispatch } from "react-redux";
import router from "../router.ts"; import router from "../router.tsx";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
function Auth() { function Auth() {

View File

@ -4,7 +4,7 @@ import { useTranslation } from "react-i18next";
import { Button } from "../components/ui/button.tsx"; import { Button } from "../components/ui/button.tsx";
import { ChevronLeft, Cloud, FileDown, Send } from "lucide-react"; import { ChevronLeft, Cloud, FileDown, Send } from "lucide-react";
import { rest_api } from "../conf.ts"; import { rest_api } from "../conf.ts";
import router from "../router.ts"; import router from "../router.tsx";
import { Input } from "../components/ui/input.tsx"; import { Input } from "../components/ui/input.tsx";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { manager } from "../conversation/generation.ts"; import { manager } from "../conversation/generation.ts";

View File

@ -1,7 +1,7 @@
import "../assets/404.less"; import "../assets/404.less";
import { Button } from "../components/ui/button.tsx"; import { Button } from "../components/ui/button.tsx";
import { HelpCircle } from "lucide-react"; import { HelpCircle } from "lucide-react";
import router from "../router.ts"; import router from "../router.tsx";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
function NotFound() { function NotFound() {

View File

@ -11,7 +11,7 @@ import { Copy, File, HelpCircle, Loader2, MessagesSquare } from "lucide-react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import MessageSegment from "../components/Message.tsx"; import MessageSegment from "../components/Message.tsx";
import { Button } from "../components/ui/button.tsx"; import { Button } from "../components/ui/button.tsx";
import router from "../router.ts"; import router from "../router.tsx";
import { useToast } from "../components/ui/use-toast.ts"; import { useToast } from "../components/ui/use-toast.ts";
import { sharingEvent } from "../events/sharing.ts"; import { sharingEvent } from "../events/sharing.ts";
import { Message } from "../conversation/types.ts"; import { Message } from "../conversation/types.ts";