123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206 |
- "use client";
- require("../polyfill");
- import { useState, useEffect } from "react";
- import styles from "./home.module.scss";
- import BotIcon from "../icons/bot.svg";
- import LoadingIcon from "../icons/three-dots.svg";
- import { getCSSVar, useMobileScreen } from "../utils";
- import dynamic from "next/dynamic";
- import { Path, SlotID } from "../constant";
- import { ErrorBoundary } from "./error";
- import { getISOLang, getLang } from "../locales";
- import { EnsureToken } from "./oauth/index";
- import {
- HashRouter as Router,
- Routes,
- Route,
- useLocation,
- } from "react-router-dom";
- import { SideBar } from "./sidebar";
- import { useAppConfig } from "../store/config";
- import { AuthPage } from "./auth";
- import { getClientConfig } from "../config/client";
- import { api } from "../client/api";
- import { useAccessStore } from "../store";
- export function Loading(props: { noLogo?: boolean }) {
- return (
- <div className={styles["loading-content"] + " no-dark"}>
- {!props.noLogo && <BotIcon />}
- <LoadingIcon />
- </div>
- );
- }
- const Settings = dynamic(async () => (await import("./settings")).Settings, {
- loading: () => <Loading noLogo />,
- });
- const Chat = dynamic(async () => (await import("./chat")).Chat, {
- loading: () => <Loading noLogo />,
- });
- const NewChat = dynamic(async () => (await import("./new-chat")).NewChat, {
- loading: () => <Loading noLogo />,
- });
- const MaskPage = dynamic(async () => (await import("./mask")).MaskPage, {
- loading: () => <Loading noLogo />,
- });
- export function useSwitchTheme() {
- const config = useAppConfig();
- useEffect(() => {
- document.body.classList.remove("light");
- document.body.classList.remove("dark");
- if (config.theme === "dark") {
- document.body.classList.add("dark");
- } else if (config.theme === "light") {
- document.body.classList.add("light");
- }
- const metaDescriptionDark = document.querySelector(
- 'meta[name="theme-color"][media*="dark"]',
- );
- const metaDescriptionLight = document.querySelector(
- 'meta[name="theme-color"][media*="light"]',
- );
- if (config.theme === "auto") {
- metaDescriptionDark?.setAttribute("content", "#151515");
- metaDescriptionLight?.setAttribute("content", "#fafafa");
- } else {
- const themeColor = getCSSVar("--theme-color");
- metaDescriptionDark?.setAttribute("content", themeColor);
- metaDescriptionLight?.setAttribute("content", themeColor);
- }
- }, [config.theme]);
- }
- function useHtmlLang() {
- useEffect(() => {
- const lang = getISOLang();
- const htmlLang = document.documentElement.lang;
- if (lang !== htmlLang) {
- document.documentElement.lang = lang;
- }
- }, []);
- }
- const useHasHydrated = () => {
- const [hasHydrated, setHasHydrated] = useState<boolean>(false);
- useEffect(() => {
- setHasHydrated(true);
- }, []);
- return hasHydrated;
- };
- const loadAsyncGoogleFont = () => {
- const linkEl = document.createElement("link");
- const proxyFontUrl = "/google-fonts";
- const remoteFontUrl = "https://fonts.googleapis.com";
- const googleFontUrl =
- getClientConfig()?.buildMode === "export" ? remoteFontUrl : proxyFontUrl;
- linkEl.rel = "stylesheet";
- linkEl.href =
- googleFontUrl +
- "/css2?family=" +
- encodeURIComponent("Noto Sans:wght@300;400;700;900") +
- "&display=swap";
- document.head.appendChild(linkEl);
- };
- function Screen() {
- const config = useAppConfig();
- const location = useLocation();
- const isHome = location.pathname === Path.Home;
- const isAuth = location.pathname === Path.Auth;
- const isMobileScreen = useMobileScreen();
- const shouldTightBorder =
- getClientConfig()?.isApp || (config.tightBorder && !isMobileScreen);
- useEffect(() => {
- loadAsyncGoogleFont();
- }, []);
- return (
- <div
- className={
- styles.container +
- ` ${shouldTightBorder ? styles["tight-container"] : styles.container} ${
- getLang() === "ar" ? styles["rtl-screen"] : ""
- }`
- }
- >
- {isAuth ? (
- <>
- <AuthPage />
- </>
- ) : (
- <>
- <SideBar className={isHome ? styles["sidebar-show"] : ""} />
- <div className={styles["window-content"]} id={SlotID.AppBody}>
- <Routes>
- <Route path={Path.Home} element={<Chat />} />
- <Route path={Path.NewChat} element={<NewChat />} />
- <Route path={Path.Masks} element={<MaskPage />} />
- <Route path={Path.Chat} element={<Chat />} />
- <Route path={Path.Settings} element={<Settings />} />
- </Routes>
- </div>
- </>
- )}
- </div>
- );
- }
- export function useLoadData() {
- const config = useAppConfig();
- useEffect(() => {
- (async () => {
- const models = await api.llm.models();
- config.mergeModels(models);
- })();
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, []);
- }
- export function Home() {
- useSwitchTheme();
- useLoadData();
- useHtmlLang();
- useEffect(() => {
- console.log("[Config] got config from build time", getClientConfig());
- useAccessStore.getState().fetch();
- }, []);
- if (!useHasHydrated()) {
- return <Loading />;
- }
- return (
- <ErrorBoundary>
- <EnsureToken>
- <Router>
- <Screen />
- </Router>
- </EnsureToken>
- </ErrorBoundary>
- );
- }
|