import { useEffect, useRef } from "react"; import styles from "./home.module.scss"; import { IconButton } from "./button"; import SettingsIcon from "../icons/settings.svg"; import GithubIcon from "../icons/github.svg"; import ChatGptIcon from "../icons/chatgpt.svg"; import AddIcon from "../icons/add.svg"; import CloseIcon from "../icons/close.svg"; import MaskIcon from "../icons/mask.svg"; import PluginIcon from "../icons/plugin.svg"; import Locale from "../locales"; import { useAppConfig, useChatStore } from "../store"; import { MAX_SIDEBAR_WIDTH, MIN_SIDEBAR_WIDTH, NARROW_SIDEBAR_WIDTH, Path, REPO_URL, } from "../constant"; import { Link, useNavigate } from "react-router-dom"; import { useMobileScreen } from "../utils"; import dynamic from "next/dynamic"; import { showConfirm, showToast } from "./ui-lib"; const ChatList = dynamic(async () => (await import("./chat-list")).ChatList, { loading: () => null, }); function useHotKey() { const chatStore = useChatStore(); useEffect(() => { const onKeyDown = (e: KeyboardEvent) => { if (e.altKey || e.ctrlKey) { if (e.key === "ArrowUp") { chatStore.nextSession(-1); } else if (e.key === "ArrowDown") { chatStore.nextSession(1); } } }; window.addEventListener("keydown", onKeyDown); return () => window.removeEventListener("keydown", onKeyDown); }); } function useDragSideBar() { const limit = (x: number) => Math.min(MAX_SIDEBAR_WIDTH, x); const config = useAppConfig(); const startX = useRef(0); const startDragWidth = useRef(config.sidebarWidth ?? 300); const lastUpdateTime = useRef(Date.now()); const handleMouseMove = useRef((e: MouseEvent) => { if (Date.now() < lastUpdateTime.current + 50) { return; } lastUpdateTime.current = Date.now(); const d = e.clientX - startX.current; const nextWidth = limit(startDragWidth.current + d); config.update((config) => (config.sidebarWidth = nextWidth)); }); const handleMouseUp = useRef(() => { startDragWidth.current = 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); }; const isMobileScreen = useMobileScreen(); const shouldNarrow = !isMobileScreen && config.sidebarWidth < MIN_SIDEBAR_WIDTH; useEffect(() => { const barWidth = shouldNarrow ? NARROW_SIDEBAR_WIDTH : limit(config.sidebarWidth ?? 300); const sideBarWidth = isMobileScreen ? "100vw" : `${barWidth}px`; document.documentElement.style.setProperty("--sidebar-width", sideBarWidth); }, [config.sidebarWidth, isMobileScreen, shouldNarrow]); return { onDragMouseDown, shouldNarrow, }; } export function SideBar(props: { className?: string }) { const chatStore = useChatStore(); // drag side bar const { onDragMouseDown, shouldNarrow } = useDragSideBar(); const navigate = useNavigate(); const config = useAppConfig(); useHotKey(); return (
ChatGPT Next
Build your own AI assistant.
} text={shouldNarrow ? undefined : Locale.Mask.Name} className={styles["sidebar-bar-button"]} onClick={() => navigate(Path.NewChat, { state: { fromHome: true } })} shadow /> } text={shouldNarrow ? undefined : Locale.Plugin.Name} className={styles["sidebar-bar-button"]} onClick={() => showToast(Locale.WIP)} shadow />
{ if (e.target === e.currentTarget) { navigate(Path.Home); } }} >
} onClick={async () => { if (await showConfirm(Locale.Home.DeleteChat)) { chatStore.deleteSession(chatStore.currentSessionIndex); } }} />
} shadow />
} shadow />
} text={shouldNarrow ? undefined : Locale.Home.NewChat} onClick={() => { if (config.dontShowMaskSplashScreen) { chatStore.newSession(); navigate(Path.Chat); } else { navigate(Path.NewChat); } }} shadow />
onDragMouseDown(e as any)} >
); }