"use client"; require("../polyfill"); import { useState, useEffect, useRef } from "react"; import { IconButton } from "./button"; import styles from "./home.module.scss"; import SettingsIcon from "../icons/settings.svg"; import GithubIcon from "../icons/github.svg"; import ChatGptIcon from "../icons/chatgpt.svg"; import BotIcon from "../icons/bot.svg"; import AddIcon from "../icons/add.svg"; import LoadingIcon from "../icons/three-dots.svg"; import CloseIcon from "../icons/close.svg"; import LeftIcon from "../icons/left.svg"; import RightIcon from "../icons/right.svg"; import { useChatStore } from "../store"; import { getCSSVar, isMobileScreen } from "../utils"; import Locale from "../locales"; import { Chat } from "./chat"; import dynamic from "next/dynamic"; import { REPO_URL } from "../constant"; import { ErrorBoundary } from "./error"; export function Loading(props: { noLogo?: boolean }) { return (
{!props.noLogo && }
); } const Settings = dynamic(async () => (await import("./settings")).Settings, { loading: () => , }); const ChatList = dynamic(async () => (await import("./chat-list")).ChatList, { loading: () => , }); function useSwitchTheme() { const config = useChatStore((state) => state.config); 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]', ); const metaDescriptionLight = document.querySelector( 'meta[name="theme-color"]:not([media])', ); if (config.theme === "auto") { metaDescriptionDark?.setAttribute("content", "#151515"); metaDescriptionLight?.setAttribute("content", "#fafafa"); } else { const themeColor = getCSSVar("--themeColor"); metaDescriptionDark?.setAttribute("content", themeColor); metaDescriptionLight?.setAttribute("content", themeColor); } }, [config.theme]); } function useDragSideBar() { const limit = (x: number) => Math.min(500, Math.max(220, x)); const chatStore = useChatStore(); const startX = useRef(0); const startDragWidth = useRef(chatStore.config.sidebarWidth ?? 300); const lastUpdateTime = useRef(Date.now()); const handleMouseMove = useRef((e: MouseEvent) => { if (Date.now() < lastUpdateTime.current + 100) { return; } lastUpdateTime.current = Date.now(); const d = e.clientX - startX.current; const nextWidth = limit(startDragWidth.current + d); chatStore.updateConfig((config) => (config.sidebarWidth = nextWidth)); }); const handleMouseUp = useRef(() => { startDragWidth.current = chatStore.config.sidebarWidth ?? 300; window.removeEventListener("mousemove", handleMouseMove.current); window.removeEventListener("mouseup", handleMouseUp.current); }); const onDragMouseDown = (e: MouseEvent) => { startX.current = e.clientX; window.addEventListener("mousemove", handleMouseMove.current); window.addEventListener("mouseup", handleMouseUp.current); }; useEffect(() => { if (isMobileScreen()) { return; } document.documentElement.style.setProperty( "--sidebar-width", `${limit(chatStore.config.sidebarWidth ?? 300)}px`, ); }, [chatStore.config.sidebarWidth]); return { onDragMouseDown, }; } const useHasHydrated = () => { const [hasHydrated, setHasHydrated] = useState(false); useEffect(() => { setHasHydrated(true); }, []); return hasHydrated; }; function _Home() { const [ sidebarCollapse, setSideBarCollapse, createNewSession, currentIndex, removeSession, ] = useChatStore((state) => [ state.sidebarCollapse, state.setSidebarCollapse, state.newSession, state.currentSessionIndex, state.removeSession, ]); const chatStore = useChatStore(); const loading = !useHasHydrated(); // setting const [openSettings, setOpenSettings] = useState(false); const config = useChatStore((state) => state.config); // drag side bar const { onDragMouseDown } = useDragSideBar(); useSwitchTheme(); // the two useEffects are for enable animation of the sidebar collapse in mobile screen const [width, setWidth] = useState(window.innerWidth); useEffect(() => { const handleResize = () => { setWidth(window.innerWidth); }; window.addEventListener("resize", handleResize); return () => { window.removeEventListener("resize", handleResize); }; }, []); const [sidebarStyle, setSidebarStyles] = useState({}); useEffect(() => { if (sidebarCollapse && isMobileScreen()) { setTimeout(() => { setSidebarStyles({ display: "none", }); }, 100); } else { setTimeout(() => { setSidebarStyles({ display: "flex", }); }, 100); } }, [sidebarCollapse, width]); if (loading) { return ; } return (
{!sidebarCollapse && (
ChatGPT Next
Build your own AI assistant.
)}
{ setOpenSettings(false); if (window.innerWidth < 768) { setSideBarCollapse(true); } }} >
{sidebarCollapse ? (
} onClick={() => { setSideBarCollapse(false); }} />
} onClick={chatStore.deleteSession} />
} onClick={() => { setOpenSettings(true); setSideBarCollapse(true); }} shadow />
} text={""} onClick={() => { createNewSession(); setSideBarCollapse(true); }} shadow />
) : (
{!isMobileScreen() ? ( sidebarCollapse ? ( } onClick={() => { setSideBarCollapse(false); }} /> ) : ( } onClick={() => { setSideBarCollapse(true); }} /> ) ) : null}
} onClick={chatStore.deleteSession} />
} onClick={() => { setOpenSettings(true); setSideBarCollapse(true); }} shadow />
} // Auto hide Text 'Next Chat' when sidebar width shrinks text={ !isMobileScreen() ? chatStore.config.sidebarWidth <= 340 ? "" : Locale.Home.NewChat : Locale.Home.NewChat } onClick={() => { createNewSession(); setSideBarCollapse(true); }} shadow />
)}
onDragMouseDown(e as any)} >
{openSettings ? ( { setOpenSettings(false); setSideBarCollapse(false); }} /> ) : ( )}
); } export function Home() { return ( <_Home> ); }