diff --git a/app/package.json b/app/package.json
index 211462c..c826935 100644
--- a/app/package.json
+++ b/app/package.json
@@ -25,6 +25,7 @@
"@radix-ui/react-tooltip": "^1.0.6",
"@reduxjs/toolkit": "^1.9.5",
"axios": "^1.5.0",
+ "chart.js": "^4.4.0",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"i18next": "^23.4.6",
@@ -32,11 +33,12 @@
"lucide-react": "^0.274.0",
"match-sorter": "^6.3.1",
"react": "^18.2.0",
+ "react-chartjs-2": "^5.2.0",
"react-dom": "^18.2.0",
"react-i18next": "^13.2.2",
"react-markdown": "^8.0.7",
"react-redux": "^8.1.2",
- "react-router-dom": "^6.15.0",
+ "react-router-dom": "^5.1.2",
"react-syntax-highlighter": "^15.5.0",
"rehype-katex": "^6.0.3",
"remark-gfm": "^3.0.1",
diff --git a/app/pnpm-lock.yaml b/app/pnpm-lock.yaml
index 0aff528..ac14310 100644
--- a/app/pnpm-lock.yaml
+++ b/app/pnpm-lock.yaml
@@ -47,6 +47,9 @@ dependencies:
axios:
specifier: ^1.5.0
version: 1.5.0
+ chart.js:
+ specifier: ^4.4.0
+ version: 4.4.0
class-variance-authority:
specifier: ^0.7.0
version: 0.7.0
@@ -68,6 +71,9 @@ dependencies:
react:
specifier: ^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:
specifier: ^18.2.0
version: 18.2.0(react@18.2.0)
@@ -1669,6 +1675,10 @@ packages:
'@jridgewell/resolve-uri': 3.1.1
'@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:
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'}
@@ -3375,6 +3385,13 @@ packages:
resolution: {integrity: sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==}
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:
resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
engines: {node: '>= 8.10.0'}
@@ -5742,6 +5759,16 @@ packages:
safe-buffer: 5.2.1
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):
resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==}
peerDependencies:
diff --git a/app/src/App.tsx b/app/src/App.tsx
index 525273f..e3709e1 100644
--- a/app/src/App.tsx
+++ b/app/src/App.tsx
@@ -1,162 +1,13 @@
-import { RouterProvider } from "react-router-dom";
-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 { Provider } from "react-redux";
import store from "./store/index.ts";
-import {
- logout,
- 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 (
-
-
-
-
-
-
-
- {username}
-
-
- dispatch(openQuotaDialog())}>
-
- {quota}
-
- dispatch(openQuotaDialog())}>
-
- {t("quota")}
-
- dispatch(openSub())}>
-
- {t("sub.title")}
-
- dispatch(openPackageDialog())}>
-
- {t("pkg.title")}
-
- dispatch(openSharingDialog())}>
-
- {t("share.manage")}
-
- dispatch(openApiDialog())}>
-
- {t("api.title")}
-
-
-
-
-
-
-
-
- );
-}
-
-function NavBar() {
- const { t } = useTranslation();
- const dispatch = useDispatch();
- useEffect(() => {
- validateToken(dispatch, localStorage.getItem(tokenField) ?? "");
- }, []);
- const auth = useSelector(selectAuthenticated);
-
- return (
-
- );
-}
+import AppProvider from "./components/app/AppProvider.tsx";
+import { AppRouter } from "./router.tsx";
function App() {
return (
-
-
-
-
-
-
-
-
-
+
+
);
}
diff --git a/app/src/components/app/AppProvider.tsx b/app/src/components/app/AppProvider.tsx
new file mode 100644
index 0000000..4b1ac1e
--- /dev/null
+++ b/app/src/components/app/AppProvider.tsx
@@ -0,0 +1,15 @@
+import NavBar from "./NavBar.tsx";
+import { ThemeProvider } from "../ThemeProvider.tsx";
+import DialogManager from "../../dialogs";
+
+function AppProvider() {
+ return (
+ <>
+
+
+
+ >
+ );
+}
+
+export default AppProvider;
diff --git a/app/src/components/app/NavBar.tsx b/app/src/components/app/NavBar.tsx
new file mode 100644
index 0000000..796b10a
--- /dev/null
+++ b/app/src/components/app/NavBar.tsx
@@ -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 (
+
+
+
+
+
+
+
+ {username}
+
+
+ dispatch(openQuotaDialog())}>
+
+ {quota}
+
+ dispatch(openQuotaDialog())}>
+
+ {t("quota")}
+
+ dispatch(openSub())}>
+
+ {t("sub.title")}
+
+ dispatch(openPackageDialog())}>
+
+ {t("pkg.title")}
+
+ dispatch(openSharingDialog())}>
+
+ {t("share.manage")}
+
+ dispatch(openApiDialog())}>
+
+ {t("api.title")}
+
+
+
+
+
+
+
+
+ );
+}
+
+function NavBar() {
+ const { t } = useTranslation();
+ const dispatch = useDispatch();
+ useEffect(() => {
+ validateToken(dispatch, localStorage.getItem(tokenField) ?? "");
+ }, []);
+ const auth = useSelector(selectAuthenticated);
+
+ return (
+
+ );
+}
+
+export default NavBar;
diff --git a/app/src/components/home/ChatWrapper.tsx b/app/src/components/home/ChatWrapper.tsx
index 8c269e5..10be32d 100644
--- a/app/src/components/home/ChatWrapper.tsx
+++ b/app/src/components/home/ChatWrapper.tsx
@@ -13,7 +13,7 @@ import { manager } from "../../conversation/manager.ts";
import { formatMessage } from "../../utils.ts";
import ChatInterface from "./ChatInterface.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 {
Tooltip,
diff --git a/app/src/conf.ts b/app/src/conf.ts
index 1d5ddc6..87fa061 100644
--- a/app/src/conf.ts
+++ b/app/src/conf.ts
@@ -1,7 +1,7 @@
import axios from "axios";
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 deploy: boolean = true;
export let rest_api: string = "http://localhost:8094";
diff --git a/app/src/routes/ApiKey.tsx b/app/src/dialogs/ApiKey.tsx
similarity index 100%
rename from app/src/routes/ApiKey.tsx
rename to app/src/dialogs/ApiKey.tsx
diff --git a/app/src/routes/Package.tsx b/app/src/dialogs/Package.tsx
similarity index 100%
rename from app/src/routes/Package.tsx
rename to app/src/dialogs/Package.tsx
diff --git a/app/src/routes/Quota.tsx b/app/src/dialogs/Quota.tsx
similarity index 100%
rename from app/src/routes/Quota.tsx
rename to app/src/dialogs/Quota.tsx
diff --git a/app/src/routes/ShareManagement.tsx b/app/src/dialogs/ShareManagement.tsx
similarity index 100%
rename from app/src/routes/ShareManagement.tsx
rename to app/src/dialogs/ShareManagement.tsx
diff --git a/app/src/routes/Subscription.tsx b/app/src/dialogs/Subscription.tsx
similarity index 100%
rename from app/src/routes/Subscription.tsx
rename to app/src/dialogs/Subscription.tsx
diff --git a/app/src/dialogs/index.tsx b/app/src/dialogs/index.tsx
new file mode 100644
index 0000000..d819508
--- /dev/null
+++ b/app/src/dialogs/index.tsx
@@ -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 (
+ <>
+
+
+
+
+
+
+ >
+ );
+}
+
+export default DialogManager;
diff --git a/app/src/router.ts b/app/src/router.tsx
similarity index 80%
rename from app/src/router.ts
rename to app/src/router.tsx
index dd3a58b..7655a37 100644
--- a/app/src/router.ts
+++ b/app/src/router.tsx
@@ -1,4 +1,4 @@
-import { createBrowserRouter } from "react-router-dom";
+import { createBrowserRouter, RouterProvider } from "react-router-dom";
import Home from "./routes/Home.tsx";
import NotFound from "./routes/NotFound.tsx";
import Auth from "./routes/Auth.tsx";
@@ -30,4 +30,8 @@ const router = createBrowserRouter([
},
]);
+export function AppRouter() {
+ return ;
+}
+
export default router;
diff --git a/app/src/routes/Auth.tsx b/app/src/routes/Auth.tsx
index b0f45ee..a13c553 100644
--- a/app/src/routes/Auth.tsx
+++ b/app/src/routes/Auth.tsx
@@ -8,7 +8,7 @@ import "../assets/auth.less";
import axios from "axios";
import { validateToken } from "../store/auth.ts";
import { useDispatch } from "react-redux";
-import router from "../router.ts";
+import router from "../router.tsx";
import { useTranslation } from "react-i18next";
function Auth() {
diff --git a/app/src/routes/Generation.tsx b/app/src/routes/Generation.tsx
index 8f9e332..54245f3 100644
--- a/app/src/routes/Generation.tsx
+++ b/app/src/routes/Generation.tsx
@@ -4,7 +4,7 @@ import { useTranslation } from "react-i18next";
import { Button } from "../components/ui/button.tsx";
import { ChevronLeft, Cloud, FileDown, Send } from "lucide-react";
import { rest_api } from "../conf.ts";
-import router from "../router.ts";
+import router from "../router.tsx";
import { Input } from "../components/ui/input.tsx";
import { useEffect, useRef, useState } from "react";
import { manager } from "../conversation/generation.ts";
diff --git a/app/src/routes/NotFound.tsx b/app/src/routes/NotFound.tsx
index 508b257..32f523f 100644
--- a/app/src/routes/NotFound.tsx
+++ b/app/src/routes/NotFound.tsx
@@ -1,7 +1,7 @@
import "../assets/404.less";
import { Button } from "../components/ui/button.tsx";
import { HelpCircle } from "lucide-react";
-import router from "../router.ts";
+import router from "../router.tsx";
import { useTranslation } from "react-i18next";
function NotFound() {
diff --git a/app/src/routes/Sharing.tsx b/app/src/routes/Sharing.tsx
index 97187c4..af15125 100644
--- a/app/src/routes/Sharing.tsx
+++ b/app/src/routes/Sharing.tsx
@@ -11,7 +11,7 @@ import { Copy, File, HelpCircle, Loader2, MessagesSquare } from "lucide-react";
import { useTranslation } from "react-i18next";
import MessageSegment from "../components/Message.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 { sharingEvent } from "../events/sharing.ts";
import { Message } from "../conversation/types.ts";