mirror of
https://github.com/Yidadaa/ChatGPT-Next-Web.git
synced 2025-09-08 01:06:58 +08:00
feat: seperate chat page
This commit is contained in:
24
app/components/Search/index.module.scss
Normal file
24
app/components/Search/index.module.scss
Normal file
@@ -0,0 +1,24 @@
|
||||
.search {
|
||||
display: flex;
|
||||
max-width: 460px;
|
||||
height: 50px;
|
||||
padding: 16px;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-shrink: 0;
|
||||
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--Light-Text-Black, #18182A);
|
||||
background: var(--light-opacity-white-70, rgba(255, 255, 255, 0.70));
|
||||
box-shadow: 0px 8px 40px 0px rgba(60, 68, 255, 0.12);
|
||||
|
||||
.icon {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
flex: 0 0;
|
||||
}
|
||||
.input {
|
||||
height: 18px;
|
||||
flex: 1 1;
|
||||
}
|
||||
}
|
30
app/components/Search/index.tsx
Normal file
30
app/components/Search/index.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import styles from "./index.module.scss";
|
||||
import SearchIcon from "@/app/icons/search.svg";
|
||||
|
||||
export interface SearchProps {
|
||||
value?: string;
|
||||
onSearch?: (v: string) => void;
|
||||
placeholder?: string;
|
||||
}
|
||||
|
||||
const Search = (props: SearchProps) => {
|
||||
const { placeholder = "", value, onSearch } = props;
|
||||
return (
|
||||
<div className={styles["search"]}>
|
||||
<div className={styles["icon"]}>
|
||||
<SearchIcon />
|
||||
</div>
|
||||
<input
|
||||
className={styles["input"]}
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
onChange={(e) => {
|
||||
e.preventDefault();
|
||||
onSearch?.(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Search;
|
70
app/components/TabActions/index.tsx
Normal file
70
app/components/TabActions/index.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
import { isValidElement } from "react";
|
||||
|
||||
type IconMap = {
|
||||
active: JSX.Element;
|
||||
inactive: JSX.Element;
|
||||
};
|
||||
interface Action {
|
||||
id: string;
|
||||
icons: JSX.Element | IconMap;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export interface TabActionsProps {
|
||||
actionsShema: Action[];
|
||||
onSelect: (id: string) => void;
|
||||
selected: string;
|
||||
groups: string[][];
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export default function TabActions(props: TabActionsProps) {
|
||||
const { actionsShema, onSelect, selected, groups, className } = props;
|
||||
|
||||
const content = groups.reduce((res, group, ind, arr) => {
|
||||
res.push(
|
||||
...group.map((i) => {
|
||||
const action = actionsShema.find((a) => a.id === i);
|
||||
if (!action) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const { icons } = action;
|
||||
let activeIcon, inactiveIcon;
|
||||
|
||||
if (isValidElement(icons)) {
|
||||
activeIcon = icons;
|
||||
inactiveIcon = icons;
|
||||
} else {
|
||||
activeIcon = (icons as IconMap).active;
|
||||
inactiveIcon = (icons as IconMap).inactive;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
key={action.id}
|
||||
className={` ${
|
||||
selected === action.id ? "bg-blue-900" : "bg-transparent"
|
||||
} p-3 rounded-md items-center ${action.className}`}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
if (selected !== action.id) {
|
||||
onSelect?.(action.id);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{selected === action.id ? activeIcon : inactiveIcon}
|
||||
</div>
|
||||
);
|
||||
}),
|
||||
);
|
||||
if (ind < arr.length - 1) {
|
||||
res.push(<div className=" flex-1"></div>);
|
||||
}
|
||||
return res;
|
||||
}, [] as JSX.Element[]);
|
||||
|
||||
return (
|
||||
<div className={`flex flex-col items-center ${className}`}>{content}</div>
|
||||
);
|
||||
}
|
@@ -9,7 +9,7 @@ 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 { getCSSVar } from "../utils";
|
||||
|
||||
import dynamic from "next/dynamic";
|
||||
import { ModelProvider, Path, SlotID } from "../constant";
|
||||
@@ -23,13 +23,15 @@ import {
|
||||
Route,
|
||||
useLocation,
|
||||
} from "react-router-dom";
|
||||
import { SideBar } from "./sidebar";
|
||||
import { SideBar } from "@/app/containers/Sidebar";
|
||||
import { useAppConfig } from "../store/config";
|
||||
import { AuthPage } from "./auth";
|
||||
import { getClientConfig } from "../config/client";
|
||||
import { ClientApi } from "../client/api";
|
||||
import { useAccessStore } from "../store";
|
||||
import { identifyDefaultClaudeModel } from "../utils/checkers";
|
||||
import useMobileScreen from "@/app/hooks/useMobileScreen";
|
||||
import backgroundUrl from "!url-loader!@/app/icons/background.svg";
|
||||
|
||||
export function Loading(props: { noLogo?: boolean }) {
|
||||
return (
|
||||
@@ -44,7 +46,7 @@ const Settings = dynamic(async () => (await import("./settings")).Settings, {
|
||||
loading: () => <Loading noLogo />,
|
||||
});
|
||||
|
||||
const Chat = dynamic(async () => (await import("./chat")).Chat, {
|
||||
const Chat = dynamic(async () => await import("@/app/containers/Chat"), {
|
||||
loading: () => <Loading noLogo />,
|
||||
});
|
||||
|
||||
@@ -129,8 +131,6 @@ function Screen() {
|
||||
const isHome = location.pathname === Path.Home;
|
||||
const isAuth = location.pathname === Path.Auth;
|
||||
const isMobileScreen = useMobileScreen();
|
||||
const shouldTightBorder =
|
||||
getClientConfig()?.isApp || (config.tightBorder && !isMobileScreen);
|
||||
|
||||
useEffect(() => {
|
||||
loadAsyncGoogleFont();
|
||||
@@ -140,10 +140,11 @@ function Screen() {
|
||||
<div
|
||||
className={
|
||||
styles.container +
|
||||
` ${shouldTightBorder ? styles["tight-container"] : styles.container} ${
|
||||
`${styles["container"]} ${styles["tight-container"]} ${
|
||||
getLang() === "ar" ? styles["rtl-screen"] : ""
|
||||
}`
|
||||
}
|
||||
style={{ background: `url(${backgroundUrl})` }}
|
||||
>
|
||||
{isAuth ? (
|
||||
<>
|
||||
@@ -153,14 +154,19 @@ function Screen() {
|
||||
<>
|
||||
<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
|
||||
className={`flex flex-col h-[100%] w-[--window-content-width`}
|
||||
id={SlotID.AppBody}
|
||||
>
|
||||
<ErrorBoundary>
|
||||
<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>
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
Reference in New Issue
Block a user