mirror of
https://github.com/Yidadaa/ChatGPT-Next-Web.git
synced 2025-09-01 20:56:59 +08:00
Compare commits
1 Commits
feat-redes
...
refactor/n
Author | SHA1 | Date | |
---|---|---|---|
|
0c53579996 |
102
app/(app)/chat/layout.tsx
Normal file
102
app/(app)/chat/layout.tsx
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
"use client";
|
||||||
|
import {
|
||||||
|
DEFAULT_SIDEBAR_WIDTH,
|
||||||
|
MAX_SIDEBAR_WIDTH,
|
||||||
|
MIN_SIDEBAR_WIDTH,
|
||||||
|
Path,
|
||||||
|
} from "@/app/constant";
|
||||||
|
import useDrag from "@/app/hooks/useDrag";
|
||||||
|
import useMobileScreen from "@/app/hooks/useMobileScreen";
|
||||||
|
import { updateGlobalCSSVars } from "@/app/utils/client";
|
||||||
|
import { useRef, useState } from "react";
|
||||||
|
import { useAppConfig } from "@/app/store/config";
|
||||||
|
import React from "react";
|
||||||
|
import { AuthPage } from "@/app/components/auth";
|
||||||
|
import { SideBar } from "@/app/containers/Sidebar";
|
||||||
|
import Screen from "@/app/components/Screen";
|
||||||
|
import { useSwitchTheme } from "@/app/hooks/useSwitchTheme";
|
||||||
|
import Chat from "@/app/containers/Chat/ChatPanel";
|
||||||
|
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||||
|
const [showPanel, setShowPanel] = useState(false);
|
||||||
|
const [externalProps, setExternalProps] = useState({});
|
||||||
|
const config = useAppConfig();
|
||||||
|
useSwitchTheme();
|
||||||
|
const isMobileScreen = useMobileScreen();
|
||||||
|
|
||||||
|
const startDragWidth = useRef(config.sidebarWidth ?? DEFAULT_SIDEBAR_WIDTH);
|
||||||
|
// drag side bar
|
||||||
|
const { onDragStart } = useDrag({
|
||||||
|
customToggle: () => {
|
||||||
|
config.update((config) => {
|
||||||
|
config.sidebarWidth = DEFAULT_SIDEBAR_WIDTH;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
customDragMove: (nextWidth: number) => {
|
||||||
|
const { menuWidth } = updateGlobalCSSVars(nextWidth);
|
||||||
|
|
||||||
|
document.documentElement.style.setProperty(
|
||||||
|
"--menu-width",
|
||||||
|
`${menuWidth}px`,
|
||||||
|
);
|
||||||
|
config.update((config) => {
|
||||||
|
config.sidebarWidth = nextWidth;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
customLimit: (x: number) =>
|
||||||
|
Math.max(
|
||||||
|
MIN_SIDEBAR_WIDTH,
|
||||||
|
Math.min(MAX_SIDEBAR_WIDTH, startDragWidth.current + x),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`
|
||||||
|
w-[100%] relative bg-center
|
||||||
|
max-md:h-[100%]
|
||||||
|
md:flex md:my-2.5
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={`
|
||||||
|
flex flex-col px-6
|
||||||
|
h-[100%]
|
||||||
|
max-md:w-[100%] max-md:px-4 max-md:pb-4 max-md:flex-1
|
||||||
|
md:relative md:basis-sidebar md:pb-6 md:rounded-md md:bg-menu
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
{!isMobileScreen && (
|
||||||
|
<div
|
||||||
|
className={`group/menu-dragger cursor-col-resize w-[0.25rem] flex items-center justify-center`}
|
||||||
|
onPointerDown={(e) => {
|
||||||
|
startDragWidth.current = config.sidebarWidth;
|
||||||
|
onDragStart(e as any);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="w-[2px] opacity-0 group-hover/menu-dragger:opacity-100 bg-menu-dragger h-[100%] rounded-[2px]">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div
|
||||||
|
className={`
|
||||||
|
md:flex-1 md:h-[100%] md:w-page
|
||||||
|
max-md:transition-all max-md:duration-300 max-md:absolute max-md:top-0 max-md:max-h-[100vh] max-md:w-[100%] ${
|
||||||
|
showPanel ? "max-md:left-0" : "max-md:left-[101%]"
|
||||||
|
} max-md:z-10
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
{/* <PanelComponent
|
||||||
|
{...props}
|
||||||
|
{...externalProps}
|
||||||
|
setShowPanel={setShowPanel}
|
||||||
|
setExternalProps={setExternalProps}
|
||||||
|
showPanel={showPanel}
|
||||||
|
/> */}
|
||||||
|
{/* {children} */}
|
||||||
|
<Chat></Chat>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
137
app/(app)/chat/page.tsx
Normal file
137
app/(app)/chat/page.tsx
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
"use client";
|
||||||
|
import {
|
||||||
|
DragDropContext,
|
||||||
|
Droppable,
|
||||||
|
OnDragEndResponder,
|
||||||
|
} from "@hello-pangea/dnd";
|
||||||
|
|
||||||
|
import { useAppConfig, useChatStore } from "@/app/store";
|
||||||
|
|
||||||
|
import Locale from "@/app/locales";
|
||||||
|
// import { useLocation, useNavigate } from "react-router-dom";
|
||||||
|
import { useRouter, usePathname } from "next/navigation";
|
||||||
|
|
||||||
|
import AddIcon from "@/app/icons/addIcon.svg";
|
||||||
|
import NextChatTitle from "@/app/icons/nextchatTitle.svg";
|
||||||
|
|
||||||
|
import Modal from "@/app/components/Modal";
|
||||||
|
import SessionItem from "@/app/containers/Chat/components/SessionItem";
|
||||||
|
|
||||||
|
export default function Page() {
|
||||||
|
const [sessions, selectedIndex, selectSession, moveSession] = useChatStore(
|
||||||
|
(state) => [
|
||||||
|
state.sessions,
|
||||||
|
state.currentSessionIndex,
|
||||||
|
state.selectSession,
|
||||||
|
state.moveSession,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
const config = useAppConfig();
|
||||||
|
|
||||||
|
const { isMobileScreen } = config;
|
||||||
|
|
||||||
|
const chatStore = useChatStore();
|
||||||
|
|
||||||
|
const pathname = usePathname();
|
||||||
|
const onDragEnd: OnDragEndResponder = (result) => {
|
||||||
|
const { destination, source } = result;
|
||||||
|
if (!destination) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
destination.droppableId === source.droppableId &&
|
||||||
|
destination.index === source.index
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
moveSession(source.index, destination.index);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`
|
||||||
|
h-[100%] flex flex-col
|
||||||
|
md:px-0
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
<div data-tauri-drag-region>
|
||||||
|
<div
|
||||||
|
className={`
|
||||||
|
flex items-center justify-between
|
||||||
|
py-6 max-md:box-content max-md:h-0
|
||||||
|
md:py-7
|
||||||
|
`}
|
||||||
|
data-tauri-drag-region
|
||||||
|
>
|
||||||
|
<div className="">
|
||||||
|
<NextChatTitle />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="cursor-pointer "
|
||||||
|
onClick={() => {
|
||||||
|
// if (config.dontShowMaskSplashScreen) {
|
||||||
|
// chatStore.newSession();
|
||||||
|
// navigate(Path.Chat);
|
||||||
|
// } else {
|
||||||
|
// navigate(Path.NewChat);
|
||||||
|
// }
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AddIcon />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={`pb-3 text-sm sm:text-sm-mobile text-text-chat-header-subtitle`}
|
||||||
|
>
|
||||||
|
Build your own AI assistant.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={`flex-1 overflow-y-auto max-md:pb-chat-panel-mobile `}>
|
||||||
|
<DragDropContext onDragEnd={onDragEnd}>
|
||||||
|
<Droppable droppableId="chat-list">
|
||||||
|
{(provided) => (
|
||||||
|
<div
|
||||||
|
ref={provided.innerRef}
|
||||||
|
{...provided.droppableProps}
|
||||||
|
className={`w-[100%]`}
|
||||||
|
>
|
||||||
|
{sessions.map((item, i) => (
|
||||||
|
<SessionItem
|
||||||
|
title={item.topic}
|
||||||
|
time={new Date(item.lastUpdate).toLocaleString()}
|
||||||
|
count={item.messages.length}
|
||||||
|
key={item.id}
|
||||||
|
id={item.id}
|
||||||
|
index={i}
|
||||||
|
selected={i === selectedIndex}
|
||||||
|
onClick={() => {
|
||||||
|
// navigate(Path.Chat);
|
||||||
|
// selectSession(i);
|
||||||
|
}}
|
||||||
|
onDelete={async () => {
|
||||||
|
if (
|
||||||
|
await Modal.warn({
|
||||||
|
okText: Locale.ChatItem.DeleteOkBtn,
|
||||||
|
cancelText: Locale.ChatItem.DeleteCancelBtn,
|
||||||
|
title: Locale.ChatItem.DeleteTitle,
|
||||||
|
content: Locale.ChatItem.DeleteContent,
|
||||||
|
})
|
||||||
|
) {
|
||||||
|
chatStore.deleteSession(i);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
mask={item.mask}
|
||||||
|
isMobileScreen={isMobileScreen}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
{provided.placeholder}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Droppable>
|
||||||
|
</DragDropContext>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
21
app/(app)/layout.tsx
Normal file
21
app/(app)/layout.tsx
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import { AuthPage } from "@/app/components/auth";
|
||||||
|
import { SideBar } from "@/app/containers/Sidebar";
|
||||||
|
import Screen from "@/app/components/Screen";
|
||||||
|
|
||||||
|
export interface MenuWrapperInspectProps {
|
||||||
|
setExternalProps?: (v: Record<string, any>) => void;
|
||||||
|
setShowPanel?: (v: boolean) => void;
|
||||||
|
showPanel?: boolean;
|
||||||
|
[k: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function AppLayout({ children }: { children: React.ReactNode }) {
|
||||||
|
return (
|
||||||
|
<Screen noAuth={<AuthPage />} sidebar={<SideBar />}>
|
||||||
|
{children}
|
||||||
|
</Screen>
|
||||||
|
);
|
||||||
|
}
|
4
app/(app)/settings/layout.tsx
Normal file
4
app/(app)/settings/layout.tsx
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import React from "react";
|
||||||
|
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||||
|
return <>{children}</>;
|
||||||
|
}
|
3
app/(app)/settings/page.tsx
Normal file
3
app/(app)/settings/page.tsx
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export default function Page() {
|
||||||
|
return <></>;
|
||||||
|
}
|
@@ -1,5 +1,6 @@
|
|||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { useSearchParams } from "react-router-dom";
|
// import { useSearchParams } from "react-router-dom";
|
||||||
|
import { useSearchParams } from "next/navigation";
|
||||||
import Locale from "./locales";
|
import Locale from "./locales";
|
||||||
|
|
||||||
type Command = (param: string) => void;
|
type Command = (param: string) => void;
|
||||||
@@ -14,22 +15,23 @@ interface Commands {
|
|||||||
export function useCommand(commands: Commands = {}) {
|
export function useCommand(commands: Commands = {}) {
|
||||||
const [searchParams, setSearchParams] = useSearchParams();
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
|
|
||||||
useEffect(() => {
|
// fixme: update commands
|
||||||
let shouldUpdate = false;
|
// useEffect(() => {
|
||||||
searchParams.forEach((param, name) => {
|
// let shouldUpdate = false;
|
||||||
const commandName = name as keyof Commands;
|
// searchParams.forEach((param, name) => {
|
||||||
if (typeof commands[commandName] === "function") {
|
// const commandName = name as keyof Commands;
|
||||||
commands[commandName]!(param);
|
// if (typeof commands[commandName] === "function") {
|
||||||
searchParams.delete(name);
|
// commands[commandName]!(param);
|
||||||
shouldUpdate = true;
|
// searchParams.delete(name);
|
||||||
}
|
// shouldUpdate = true;
|
||||||
});
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
if (shouldUpdate) {
|
// if (shouldUpdate) {
|
||||||
setSearchParams(searchParams);
|
// setSearchParams(searchParams);
|
||||||
}
|
// }
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// // eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [searchParams, commands]);
|
// }, [searchParams, commands]);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ChatCommands {
|
interface ChatCommands {
|
||||||
|
@@ -53,6 +53,8 @@ export interface TriggerProps
|
|||||||
|
|
||||||
const baseZIndex = 150;
|
const baseZIndex = 150;
|
||||||
|
|
||||||
|
let div: HTMLDivElement | null = null;
|
||||||
|
|
||||||
const Modal = (props: ModalProps) => {
|
const Modal = (props: ModalProps) => {
|
||||||
const {
|
const {
|
||||||
onOk,
|
onOk,
|
||||||
@@ -78,6 +80,16 @@ const Modal = (props: ModalProps) => {
|
|||||||
|
|
||||||
const mergeOpen = visible ?? open;
|
const mergeOpen = visible ?? open;
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
const div: HTMLDivElement = document.createElement("div");
|
||||||
|
div.id = "confirm-root";
|
||||||
|
div.style.height = "0px";
|
||||||
|
document.body.appendChild(div);
|
||||||
|
}, []);
|
||||||
|
const root = createRoot(div);
|
||||||
|
const closeModal = () => {
|
||||||
|
root.unmount();
|
||||||
|
};
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
onCancel?.();
|
onCancel?.();
|
||||||
@@ -121,7 +133,7 @@ const Modal = (props: ModalProps) => {
|
|||||||
<AlertDialog.Root open={mergeOpen} onOpenChange={setOpen}>
|
<AlertDialog.Root open={mergeOpen} onOpenChange={setOpen}>
|
||||||
<AlertDialog.Portal>
|
<AlertDialog.Portal>
|
||||||
<AlertDialog.Overlay
|
<AlertDialog.Overlay
|
||||||
className="bg-modal-mask fixed inset-0 animate-mask "
|
className="fixed inset-0 bg-modal-mask animate-mask "
|
||||||
style={{ zIndex: baseZIndex - 1 }}
|
style={{ zIndex: baseZIndex - 1 }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (maskCloseble) {
|
if (maskCloseble) {
|
||||||
@@ -165,7 +177,7 @@ const Modal = (props: ModalProps) => {
|
|||||||
${titleClassName}
|
${titleClassName}
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<div className="flex gap-3 justify-start flex-1 items-center text-text-modal-title text-chat-header-title">
|
<div className="flex items-center justify-start flex-1 gap-3 text-text-modal-title text-chat-header-title">
|
||||||
{title}
|
{title}
|
||||||
</div>
|
</div>
|
||||||
{closeble && (
|
{closeble && (
|
||||||
@@ -283,11 +295,6 @@ export const Warn = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const div = document.createElement("div");
|
|
||||||
div.id = "confirm-root";
|
|
||||||
div.style.height = "0px";
|
|
||||||
document.body.appendChild(div);
|
|
||||||
|
|
||||||
Modal.warn = (props: Omit<WarnProps, "visible" | "onCancel" | "onOk">) => {
|
Modal.warn = (props: Omit<WarnProps, "visible" | "onCancel" | "onOk">) => {
|
||||||
const root = createRoot(div);
|
const root = createRoot(div);
|
||||||
const closeModal = () => {
|
const closeModal = () => {
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
"use client";
|
||||||
import useRelativePosition from "@/app/hooks/useRelativePosition";
|
import useRelativePosition from "@/app/hooks/useRelativePosition";
|
||||||
import {
|
import {
|
||||||
RefObject,
|
RefObject,
|
||||||
@@ -36,19 +37,6 @@ const ArrowIcon = ({ sibling }: { sibling: RefObject<HTMLDivElement> }) => {
|
|||||||
|
|
||||||
const baseZIndex = 100;
|
const baseZIndex = 100;
|
||||||
const popoverRootName = "popoverRoot";
|
const popoverRootName = "popoverRoot";
|
||||||
let popoverRoot = document.querySelector(
|
|
||||||
`#${popoverRootName}`,
|
|
||||||
) as HTMLDivElement;
|
|
||||||
if (!popoverRoot) {
|
|
||||||
popoverRoot = document.createElement("div");
|
|
||||||
document.body.appendChild(popoverRoot);
|
|
||||||
popoverRoot.style.height = "0px";
|
|
||||||
popoverRoot.style.width = "100%";
|
|
||||||
popoverRoot.style.position = "fixed";
|
|
||||||
popoverRoot.style.bottom = "0";
|
|
||||||
popoverRoot.style.zIndex = "10000";
|
|
||||||
popoverRoot.id = "popover-root";
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PopoverProps {
|
export interface PopoverProps {
|
||||||
content?: JSX.Element | string;
|
content?: JSX.Element | string;
|
||||||
@@ -65,6 +53,8 @@ export interface PopoverProps {
|
|||||||
getPopoverPanelRef?: (ref: RefObject<HTMLDivElement>) => void;
|
getPopoverPanelRef?: (ref: RefObject<HTMLDivElement>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let popoverRoot: HTMLDivElement;
|
||||||
|
|
||||||
export default function Popover(props: PopoverProps) {
|
export default function Popover(props: PopoverProps) {
|
||||||
const {
|
const {
|
||||||
content,
|
content,
|
||||||
@@ -184,6 +174,26 @@ export default function Popover(props: PopoverProps) {
|
|||||||
const popoverRef = useRef<HTMLDivElement>(null);
|
const popoverRef = useRef<HTMLDivElement>(null);
|
||||||
const closeTimer = useRef<number>(0);
|
const closeTimer = useRef<number>(0);
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
if (popoverRoot) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
popoverRoot = document.querySelector(
|
||||||
|
`#${popoverRootName}`,
|
||||||
|
) as HTMLDivElement;
|
||||||
|
if (!popoverRoot) {
|
||||||
|
popoverRoot = document.createElement("div");
|
||||||
|
document.body.appendChild(popoverRoot);
|
||||||
|
popoverRoot.style.height = "0px";
|
||||||
|
popoverRoot.style.width = "100%";
|
||||||
|
popoverRoot.style.position = "fixed";
|
||||||
|
popoverRoot.style.bottom = "0";
|
||||||
|
popoverRoot.style.zIndex = "10000";
|
||||||
|
popoverRoot.id = "popover-root";
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
getPopoverPanelRef?.(popoverRef);
|
getPopoverPanelRef?.(popoverRef);
|
||||||
onShow?.(internalShow);
|
onShow?.(internalShow);
|
||||||
@@ -207,6 +217,10 @@ export default function Popover(props: PopoverProps) {
|
|||||||
window.document.documentElement.style.overflow = "auto";
|
window.document.documentElement.style.overflow = "auto";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (mergedShow) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`relative ${className}`}
|
className={`relative ${className}`}
|
||||||
|
@@ -1,11 +1,12 @@
|
|||||||
import { useLocation } from "react-router-dom";
|
|
||||||
import { useMemo, ReactNode } from "react";
|
import { useMemo, ReactNode } from "react";
|
||||||
import { Path, SIDEBAR_ID, SlotID } from "@/app/constant";
|
import { Path, SIDEBAR_ID, SlotID } from "@/app/constant";
|
||||||
import { getLang } from "@/app/locales";
|
import { getLang } from "@/app/locales";
|
||||||
|
|
||||||
import useMobileScreen from "@/app/hooks/useMobileScreen";
|
import useMobileScreen from "@/app/hooks/useMobileScreen";
|
||||||
import { isIOS } from "@/app/utils";
|
|
||||||
import useListenWinResize from "@/app/hooks/useListenWinResize";
|
import useListenWinResize from "@/app/hooks/useListenWinResize";
|
||||||
|
import { usePathname } from "next/navigation";
|
||||||
|
import { useDeviceInfo } from "@/app/hooks/useDeviceInfo";
|
||||||
|
|
||||||
interface ScreenProps {
|
interface ScreenProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
@@ -14,15 +15,11 @@ interface ScreenProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function Screen(props: ScreenProps) {
|
export default function Screen(props: ScreenProps) {
|
||||||
const location = useLocation();
|
const pathname = usePathname();
|
||||||
const isAuth = location.pathname === Path.Auth;
|
const isAuth = pathname === Path.Auth;
|
||||||
|
|
||||||
const isMobileScreen = useMobileScreen();
|
const isMobileScreen = useMobileScreen();
|
||||||
const isIOSMobile = useMemo(
|
const { deviceType, systemInfo } = useDeviceInfo();
|
||||||
() => isIOS() && isMobileScreen,
|
|
||||||
[isMobileScreen],
|
|
||||||
);
|
|
||||||
|
|
||||||
useListenWinResize();
|
useListenWinResize();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -59,7 +56,10 @@ export default function Screen(props: ScreenProps) {
|
|||||||
id={SlotID.AppBody}
|
id={SlotID.AppBody}
|
||||||
style={{
|
style={{
|
||||||
// #3016 disable transition on ios mobile screen
|
// #3016 disable transition on ios mobile screen
|
||||||
transition: isIOSMobile ? "none" : undefined,
|
transition:
|
||||||
|
systemInfo === "iOS" && deviceType === "mobile"
|
||||||
|
? "none"
|
||||||
|
: undefined,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{props.children}
|
{props.children}
|
||||||
|
@@ -1,7 +1,8 @@
|
|||||||
|
"use client";
|
||||||
import styles from "./auth.module.scss";
|
import styles from "./auth.module.scss";
|
||||||
import { IconButton } from "./button";
|
import { IconButton } from "./button";
|
||||||
|
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useRouter } from "next/navigation";
|
||||||
import { Path } from "../constant";
|
import { Path } from "../constant";
|
||||||
import { useAccessStore } from "../store";
|
import { useAccessStore } from "../store";
|
||||||
import Locale from "../locales";
|
import Locale from "../locales";
|
||||||
@@ -11,11 +12,11 @@ import { useEffect } from "react";
|
|||||||
import { getClientConfig } from "../config/client";
|
import { getClientConfig } from "../config/client";
|
||||||
|
|
||||||
export function AuthPage() {
|
export function AuthPage() {
|
||||||
const navigate = useNavigate();
|
const router = useRouter();
|
||||||
const accessStore = useAccessStore();
|
const accessStore = useAccessStore();
|
||||||
|
|
||||||
const goHome = () => navigate(Path.Home);
|
const goHome = () => router.push(Path.Home);
|
||||||
const goChat = () => navigate(Path.Chat);
|
const goChat = () => router.push(Path.Chat);
|
||||||
const resetAccessCode = () => {
|
const resetAccessCode = () => {
|
||||||
accessStore.update((access) => {
|
accessStore.update((access) => {
|
||||||
access.openaiApiKey = "";
|
access.openaiApiKey = "";
|
||||||
|
@@ -12,13 +12,14 @@ import {
|
|||||||
import { useChatStore } from "../store";
|
import { useChatStore } from "../store";
|
||||||
|
|
||||||
import Locale from "../locales";
|
import Locale from "../locales";
|
||||||
import { Link, useLocation, useNavigate } from "react-router-dom";
|
// import { Link, useLocation, useNavigate } from "react-router-dom";
|
||||||
import { Path } from "../constant";
|
import { Path } from "../constant";
|
||||||
import { MaskAvatar } from "./mask";
|
import { MaskAvatar } from "./mask";
|
||||||
import { Mask } from "../store/mask";
|
import { Mask } from "../store/mask";
|
||||||
import { useRef, useEffect } from "react";
|
import { useRef, useEffect } from "react";
|
||||||
import { showConfirm } from "./ui-lib";
|
import { showConfirm } from "./ui-lib";
|
||||||
import { useMobileScreen } from "../utils";
|
import { useMobileScreen } from "../utils";
|
||||||
|
import { usePathname, useRouter } from "next/navigation";
|
||||||
|
|
||||||
export function ChatItem(props: {
|
export function ChatItem(props: {
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
@@ -41,14 +42,14 @@ export function ChatItem(props: {
|
|||||||
}
|
}
|
||||||
}, [props.selected]);
|
}, [props.selected]);
|
||||||
|
|
||||||
const { pathname: currentPath } = useLocation();
|
const pathname = usePathname();
|
||||||
return (
|
return (
|
||||||
<Draggable draggableId={`${props.id}`} index={props.index}>
|
<Draggable draggableId={`${props.id}`} index={props.index}>
|
||||||
{(provided) => (
|
{(provided) => (
|
||||||
<div
|
<div
|
||||||
className={`${styles["chat-item"]} ${
|
className={`${styles["chat-item"]} ${
|
||||||
props.selected &&
|
props.selected &&
|
||||||
(currentPath === Path.Chat || currentPath === Path.Home) &&
|
(pathname === Path.Chat || pathname === Path.Home) &&
|
||||||
styles["chat-item-selected"]
|
styles["chat-item-selected"]
|
||||||
}`}
|
}`}
|
||||||
onClick={props.onClick}
|
onClick={props.onClick}
|
||||||
@@ -112,8 +113,8 @@ export function ChatList(props: { narrow?: boolean }) {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
const chatStore = useChatStore();
|
const chatStore = useChatStore();
|
||||||
const navigate = useNavigate();
|
|
||||||
const isMobileScreen = useMobileScreen();
|
const isMobileScreen = useMobileScreen();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
const onDragEnd: OnDragEndResponder = (result) => {
|
const onDragEnd: OnDragEndResponder = (result) => {
|
||||||
const { destination, source } = result;
|
const { destination, source } = result;
|
||||||
@@ -150,7 +151,8 @@ export function ChatList(props: { narrow?: boolean }) {
|
|||||||
index={i}
|
index={i}
|
||||||
selected={i === selectedIndex}
|
selected={i === selectedIndex}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
navigate(Path.Chat);
|
// navigate(Path.Chat);
|
||||||
|
router.push(Path.Chat);
|
||||||
selectSession(i);
|
selectSession(i);
|
||||||
}}
|
}}
|
||||||
onDelete={async () => {
|
onDelete={async () => {
|
||||||
|
@@ -97,6 +97,7 @@ import { ExportMessageModal } from "./exporter";
|
|||||||
import { getClientConfig } from "../config/client";
|
import { getClientConfig } from "../config/client";
|
||||||
import { useAllModels } from "../utils/hooks";
|
import { useAllModels } from "../utils/hooks";
|
||||||
import { MultimodalContent } from "../client/api";
|
import { MultimodalContent } from "../client/api";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
|
||||||
const Markdown = dynamic(async () => (await import("./markdown")).Markdown, {
|
const Markdown = dynamic(async () => (await import("./markdown")).Markdown, {
|
||||||
loading: () => <LoadingIcon />,
|
loading: () => <LoadingIcon />,
|
||||||
@@ -428,7 +429,7 @@ export function ChatActions(props: {
|
|||||||
uploading: boolean;
|
uploading: boolean;
|
||||||
}) {
|
}) {
|
||||||
const config = useAppConfig();
|
const config = useAppConfig();
|
||||||
const navigate = useNavigate();
|
const router = useRouter();
|
||||||
const chatStore = useChatStore();
|
const chatStore = useChatStore();
|
||||||
|
|
||||||
// switch themes
|
// switch themes
|
||||||
@@ -543,7 +544,8 @@ export function ChatActions(props: {
|
|||||||
|
|
||||||
<ChatAction
|
<ChatAction
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
navigate(Path.Masks);
|
// navigate(Path.Masks);
|
||||||
|
router.push(Path.Masks);
|
||||||
}}
|
}}
|
||||||
text={Locale.Chat.InputActions.Masks}
|
text={Locale.Chat.InputActions.Masks}
|
||||||
icon={<MaskIcon />}
|
icon={<MaskIcon />}
|
||||||
|
@@ -27,9 +27,9 @@ import {
|
|||||||
} from "../constant";
|
} from "../constant";
|
||||||
|
|
||||||
import { Link, useNavigate } from "react-router-dom";
|
import { Link, useNavigate } from "react-router-dom";
|
||||||
import { isIOS, useMobileScreen } from "../utils";
|
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import { showConfirm, showToast } from "./ui-lib";
|
import { showConfirm, showToast } from "./ui-lib";
|
||||||
|
import { useDeviceInfo } from "../hooks/useDeviceInfo";
|
||||||
|
|
||||||
const ChatList = dynamic(async () => (await import("./chat-list")).ChatList, {
|
const ChatList = dynamic(async () => (await import("./chat-list")).ChatList, {
|
||||||
loading: () => null,
|
loading: () => null,
|
||||||
@@ -130,16 +130,11 @@ function useDragSideBar() {
|
|||||||
|
|
||||||
export function SideBar(props: { className?: string }) {
|
export function SideBar(props: { className?: string }) {
|
||||||
const chatStore = useChatStore();
|
const chatStore = useChatStore();
|
||||||
|
const { deviceType, systemInfo } = useDeviceInfo();
|
||||||
// drag side bar
|
// drag side bar
|
||||||
const { onDragStart, shouldNarrow } = useDragSideBar();
|
const { onDragStart, shouldNarrow } = useDragSideBar();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const config = useAppConfig();
|
const config = useAppConfig();
|
||||||
const isMobileScreen = useMobileScreen();
|
|
||||||
const isIOSMobile = useMemo(
|
|
||||||
() => isIOS() && isMobileScreen,
|
|
||||||
[isMobileScreen],
|
|
||||||
);
|
|
||||||
|
|
||||||
useHotKey();
|
useHotKey();
|
||||||
|
|
||||||
@@ -150,7 +145,8 @@ export function SideBar(props: { className?: string }) {
|
|||||||
}`}
|
}`}
|
||||||
style={{
|
style={{
|
||||||
// #3016 disable transition on ios mobile screen
|
// #3016 disable transition on ios mobile screen
|
||||||
transition: isMobileScreen && isIOSMobile ? "none" : undefined,
|
transition:
|
||||||
|
deviceType === "mobile" && systemInfo === "iOS" ? "none" : undefined,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className={styles["sidebar-header"]} data-tauri-drag-region>
|
<div className={styles["sidebar-header"]} data-tauri-drag-region>
|
||||||
|
@@ -3,7 +3,12 @@ import { BuildConfig, getBuildConfig } from "./build";
|
|||||||
export function getClientConfig() {
|
export function getClientConfig() {
|
||||||
if (typeof document !== "undefined") {
|
if (typeof document !== "undefined") {
|
||||||
// client side
|
// client side
|
||||||
return JSON.parse(queryMeta("config")) as BuildConfig;
|
try {
|
||||||
|
const config = JSON.parse(queryMeta("config")) as BuildConfig;
|
||||||
|
return config;
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof process !== "undefined") {
|
if (typeof process !== "undefined") {
|
||||||
|
@@ -1,5 +1,3 @@
|
|||||||
import { useNavigate } from "react-router-dom";
|
|
||||||
|
|
||||||
import { ModelType, Theme, useAppConfig } from "@/app/store/config";
|
import { ModelType, Theme, useAppConfig } from "@/app/store/config";
|
||||||
import { useChatStore } from "@/app/store/chat";
|
import { useChatStore } from "@/app/store/chat";
|
||||||
import { ChatControllerPool } from "@/app/client/controller";
|
import { ChatControllerPool } from "@/app/client/controller";
|
||||||
@@ -22,6 +20,7 @@ import AddCircleIcon from "@/app/icons/addCircle.svg";
|
|||||||
|
|
||||||
import Popover from "@/app/components/Popover";
|
import Popover from "@/app/components/Popover";
|
||||||
import ModelSelect from "./ModelSelect";
|
import ModelSelect from "./ModelSelect";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
|
||||||
export interface Action {
|
export interface Action {
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
@@ -46,7 +45,7 @@ export function ChatActions(props: {
|
|||||||
className?: string;
|
className?: string;
|
||||||
}) {
|
}) {
|
||||||
const config = useAppConfig();
|
const config = useAppConfig();
|
||||||
const navigate = useNavigate();
|
const router = useRouter();
|
||||||
const chatStore = useChatStore();
|
const chatStore = useChatStore();
|
||||||
|
|
||||||
// switch themes
|
// switch themes
|
||||||
@@ -146,7 +145,7 @@ export function ChatActions(props: {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
navigate(Path.Masks);
|
router.push(Path.Masks);
|
||||||
},
|
},
|
||||||
text: Locale.Chat.InputActions.Masks,
|
text: Locale.Chat.InputActions.Masks,
|
||||||
isShow: true,
|
isShow: true,
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { useNavigate } from "react-router-dom";
|
import { useRouter } from "next/navigation";
|
||||||
import Locale from "@/app/locales";
|
import Locale from "@/app/locales";
|
||||||
import { Path } from "@/app/constant";
|
import { Path } from "@/app/constant";
|
||||||
import { DEFAULT_TOPIC, useChatStore } from "@/app/store/chat";
|
import { DEFAULT_TOPIC, useChatStore } from "@/app/store/chat";
|
||||||
@@ -17,8 +17,8 @@ export interface ChatHeaderProps {
|
|||||||
export default function ChatHeader(props: ChatHeaderProps) {
|
export default function ChatHeader(props: ChatHeaderProps) {
|
||||||
const { isMobileScreen, setIsEditingMessage, setShowExport } = props;
|
const { isMobileScreen, setIsEditingMessage, setShowExport } = props;
|
||||||
|
|
||||||
const navigate = useNavigate();
|
// const navigate = useNavigate();
|
||||||
|
const router = useRouter();
|
||||||
const chatStore = useChatStore();
|
const chatStore = useChatStore();
|
||||||
const session = chatStore.currentSession();
|
const session = chatStore.currentSession();
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ export default function ChatHeader(props: ChatHeaderProps) {
|
|||||||
{isMobileScreen ? (
|
{isMobileScreen ? (
|
||||||
<div
|
<div
|
||||||
className="cursor-pointer follow-parent-svg default-icon-color"
|
className="cursor-pointer follow-parent-svg default-icon-color"
|
||||||
onClick={() => navigate(Path.Home)}
|
onClick={() => router.push(Path.Home)}
|
||||||
>
|
>
|
||||||
<GobackIcon />
|
<GobackIcon />
|
||||||
</div>
|
</div>
|
||||||
|
@@ -10,6 +10,7 @@ import { ChatCommandPrefix, useChatCommand } from "@/app/command";
|
|||||||
import { useChatStore } from "@/app/store/chat";
|
import { useChatStore } from "@/app/store/chat";
|
||||||
import { usePromptStore } from "@/app/store/prompt";
|
import { usePromptStore } from "@/app/store/prompt";
|
||||||
import { useAppConfig } from "@/app/store/config";
|
import { useAppConfig } from "@/app/store/config";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
import usePaste from "@/app/hooks/usePaste";
|
import usePaste from "@/app/hooks/usePaste";
|
||||||
|
|
||||||
import { ChatActions } from "./ChatActions";
|
import { ChatActions } from "./ChatActions";
|
||||||
@@ -71,7 +72,7 @@ export default forwardRef<ChatInputPanelInstance, ChatInputPanelProps>(
|
|||||||
const [promptHints, setPromptHints] = useState<RenderPompt[]>([]);
|
const [promptHints, setPromptHints] = useState<RenderPompt[]>([]);
|
||||||
|
|
||||||
const chatStore = useChatStore();
|
const chatStore = useChatStore();
|
||||||
const navigate = useNavigate();
|
const router = useRouter();
|
||||||
const config = useAppConfig();
|
const config = useAppConfig();
|
||||||
|
|
||||||
const { uploadImage } = useUploadImage(attachImages, {
|
const { uploadImage } = useUploadImage(attachImages, {
|
||||||
@@ -85,7 +86,7 @@ export default forwardRef<ChatInputPanelInstance, ChatInputPanelProps>(
|
|||||||
// chat commands shortcuts
|
// chat commands shortcuts
|
||||||
const chatCommands = useChatCommand({
|
const chatCommands = useChatCommand({
|
||||||
new: () => chatStore.newSession(),
|
new: () => chatStore.newSession(),
|
||||||
newm: () => navigate(Path.NewChat),
|
newm: () => router.push(Path.NewChat),
|
||||||
prev: () => chatStore.nextSession(-1),
|
prev: () => chatStore.nextSession(-1),
|
||||||
next: () => chatStore.nextSession(1),
|
next: () => chatStore.nextSession(1),
|
||||||
clear: () =>
|
clear: () =>
|
||||||
@@ -299,7 +300,7 @@ export default forwardRef<ChatInputPanelInstance, ChatInputPanelProps>(
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{!isMobileScreen && (
|
{!isMobileScreen && (
|
||||||
<div className="flex items-center justify-center text-sm gap-3">
|
<div className="flex items-center justify-center gap-3 text-sm">
|
||||||
<div className="flex-1"> </div>
|
<div className="flex-1"> </div>
|
||||||
<div className="text-text-chat-input-placeholder font-common line-clamp-1">
|
<div className="text-text-chat-input-placeholder font-common line-clamp-1">
|
||||||
{Locale.Chat.Input(submitKey)}
|
{Locale.Chat.Input(submitKey)}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { Fragment, useMemo } from "react";
|
import { Fragment, useEffect, useMemo } from "react";
|
||||||
import { ChatMessage, useChatStore } from "@/app/store/chat";
|
import { ChatMessage, useChatStore } from "@/app/store/chat";
|
||||||
import { CHAT_PAGE_SIZE } from "@/app/constant";
|
import { CHAT_PAGE_SIZE } from "@/app/constant";
|
||||||
import Locale from "@/app/locales";
|
import Locale from "@/app/locales";
|
||||||
@@ -88,11 +88,13 @@ export default function ChatMessagePanel(props: ChatMessagePanelProps) {
|
|||||||
? session.clearContextIndex! + context.length - msgRenderIndex
|
? session.clearContextIndex! + context.length - msgRenderIndex
|
||||||
: -1;
|
: -1;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
if (!MarkdownLoadedCallback) {
|
if (!MarkdownLoadedCallback) {
|
||||||
MarkdownLoadedCallback = () => {
|
MarkdownLoadedCallback = () => {
|
||||||
window.setTimeout(scrollDomToBottom, 100);
|
window.setTimeout(scrollDomToBottom, 100);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
}, [scrollDomToBottom]);
|
||||||
|
|
||||||
const messages = useMemo(() => {
|
const messages = useMemo(() => {
|
||||||
const endRenderIndex = Math.min(
|
const endRenderIndex = Math.min(
|
||||||
|
@@ -1,11 +1,10 @@
|
|||||||
import { Draggable } from "@hello-pangea/dnd";
|
import { Draggable } from "@hello-pangea/dnd";
|
||||||
|
|
||||||
import Locale from "@/app/locales";
|
import Locale from "@/app/locales";
|
||||||
import { useLocation } from "react-router-dom";
|
|
||||||
import { Path } from "@/app/constant";
|
import { Path } from "@/app/constant";
|
||||||
import { Mask } from "@/app/store/mask";
|
import { Mask } from "@/app/store/mask";
|
||||||
import { useRef, useEffect } from "react";
|
import { useRef, useEffect } from "react";
|
||||||
|
import { usePathname } from "next/navigation";
|
||||||
import DeleteChatIcon from "@/app/icons/deleteChatIcon.svg";
|
import DeleteChatIcon from "@/app/icons/deleteChatIcon.svg";
|
||||||
|
|
||||||
import { getTime } from "@/app/utils";
|
import { getTime } from "@/app/utils";
|
||||||
@@ -36,8 +35,7 @@ export default function SessionItem(props: {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [props.selected]);
|
}, [props.selected]);
|
||||||
|
const pathname = usePathname();
|
||||||
const { pathname: currentPath } = useLocation();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Draggable draggableId={`${props.id}`} index={props.index}>
|
<Draggable draggableId={`${props.id}`} index={props.index}>
|
||||||
@@ -51,7 +49,7 @@ export default function SessionItem(props: {
|
|||||||
md:bg-chat-menu-session-unselected md:border-chat-menu-session-unselected
|
md:bg-chat-menu-session-unselected md:border-chat-menu-session-unselected
|
||||||
${
|
${
|
||||||
props.selected &&
|
props.selected &&
|
||||||
(currentPath === Path.Chat || currentPath === Path.Home)
|
(pathname === Path.Chat || pathname === Path.Home)
|
||||||
? `
|
? `
|
||||||
md:!bg-chat-menu-session-selected md:!border-chat-menu-session-selected
|
md:!bg-chat-menu-session-selected md:!border-chat-menu-session-selected
|
||||||
!bg-chat-menu-session-selected-mobile !border-chat-menu-session-selected-mobile
|
!bg-chat-menu-session-selected-mobile !border-chat-menu-session-selected-mobile
|
||||||
|
@@ -7,7 +7,6 @@ import {
|
|||||||
import { useAppConfig, useChatStore } from "@/app/store";
|
import { useAppConfig, useChatStore } from "@/app/store";
|
||||||
|
|
||||||
import Locale from "@/app/locales";
|
import Locale from "@/app/locales";
|
||||||
import { useLocation, useNavigate } from "react-router-dom";
|
|
||||||
import { Path } from "@/app/constant";
|
import { Path } from "@/app/constant";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
|
|
||||||
@@ -18,6 +17,7 @@ import MenuLayout from "@/app/components/MenuLayout";
|
|||||||
import Panel from "./ChatPanel";
|
import Panel from "./ChatPanel";
|
||||||
import Modal from "@/app/components/Modal";
|
import Modal from "@/app/components/Modal";
|
||||||
import SessionItem from "./components/SessionItem";
|
import SessionItem from "./components/SessionItem";
|
||||||
|
import { usePathname, useRouter } from "next/navigation";
|
||||||
|
|
||||||
export default MenuLayout(function SessionList(props) {
|
export default MenuLayout(function SessionList(props) {
|
||||||
const { setShowPanel } = props;
|
const { setShowPanel } = props;
|
||||||
@@ -30,17 +30,16 @@ export default MenuLayout(function SessionList(props) {
|
|||||||
state.moveSession,
|
state.moveSession,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
const navigate = useNavigate();
|
|
||||||
const config = useAppConfig();
|
const config = useAppConfig();
|
||||||
|
|
||||||
const { isMobileScreen } = config;
|
const { isMobileScreen } = config;
|
||||||
|
|
||||||
const chatStore = useChatStore();
|
const chatStore = useChatStore();
|
||||||
const { pathname: currentPath } = useLocation();
|
const router = useRouter();
|
||||||
|
const pathname = usePathname();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setShowPanel?.(currentPath === Path.Chat);
|
setShowPanel?.(pathname === Path.Chat);
|
||||||
}, [currentPath]);
|
}, [pathname]);
|
||||||
|
|
||||||
const onDragEnd: OnDragEndResponder = (result) => {
|
const onDragEnd: OnDragEndResponder = (result) => {
|
||||||
const { destination, source } = result;
|
const { destination, source } = result;
|
||||||
@@ -81,9 +80,11 @@ export default MenuLayout(function SessionList(props) {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (config.dontShowMaskSplashScreen) {
|
if (config.dontShowMaskSplashScreen) {
|
||||||
chatStore.newSession();
|
chatStore.newSession();
|
||||||
navigate(Path.Chat);
|
// navigate(Path.Chat);
|
||||||
|
router.push(Path.Chat);
|
||||||
} else {
|
} else {
|
||||||
navigate(Path.NewChat);
|
// navigate(Path.NewChat);
|
||||||
|
router.push(Path.NewChat);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -116,8 +117,9 @@ export default MenuLayout(function SessionList(props) {
|
|||||||
index={i}
|
index={i}
|
||||||
selected={i === selectedIndex}
|
selected={i === selectedIndex}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
navigate(Path.Chat);
|
// navigate(Path.Chat);
|
||||||
selectSession(i);
|
selectSession(i);
|
||||||
|
router.push(Path.Chat);
|
||||||
}}
|
}}
|
||||||
onDelete={async () => {
|
onDelete={async () => {
|
||||||
if (
|
if (
|
||||||
|
@@ -14,13 +14,14 @@ import AssistantMobileInactive from "@/app/icons/assistantMobileInactive.svg";
|
|||||||
|
|
||||||
import { useAppConfig } from "@/app/store";
|
import { useAppConfig } from "@/app/store";
|
||||||
import { Path, REPO_URL } from "@/app/constant";
|
import { Path, REPO_URL } from "@/app/constant";
|
||||||
import { useNavigate, useLocation } from "react-router-dom";
|
|
||||||
import useHotKey from "@/app/hooks/useHotKey";
|
import useHotKey from "@/app/hooks/useHotKey";
|
||||||
import ActionsBar from "@/app/components/ActionsBar";
|
import ActionsBar from "@/app/components/ActionsBar";
|
||||||
|
import { usePathname, useRouter } from "next/navigation";
|
||||||
|
|
||||||
export function SideBar(props: { className?: string }) {
|
export function SideBar(props: { className?: string }) {
|
||||||
const navigate = useNavigate();
|
// const navigate = useNavigate();
|
||||||
const loc = useLocation();
|
const pathname = usePathname();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
const config = useAppConfig();
|
const config = useAppConfig();
|
||||||
const { isMobileScreen } = config;
|
const { isMobileScreen } = config;
|
||||||
@@ -28,8 +29,7 @@ export function SideBar(props: { className?: string }) {
|
|||||||
useHotKey();
|
useHotKey();
|
||||||
|
|
||||||
let selectedTab: string;
|
let selectedTab: string;
|
||||||
|
switch (pathname) {
|
||||||
switch (loc.pathname) {
|
|
||||||
case Path.Masks:
|
case Path.Masks:
|
||||||
case Path.NewChat:
|
case Path.NewChat:
|
||||||
selectedTab = Path.Masks;
|
selectedTab = Path.Masks;
|
||||||
@@ -40,6 +40,7 @@ export function SideBar(props: { className?: string }) {
|
|||||||
default:
|
default:
|
||||||
selectedTab = Path.Home;
|
selectedTab = Path.Home;
|
||||||
}
|
}
|
||||||
|
console.log("======", selectedTab);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -98,12 +99,17 @@ export function SideBar(props: { className?: string }) {
|
|||||||
return window.open(REPO_URL, "noopener noreferrer");
|
return window.open(REPO_URL, "noopener noreferrer");
|
||||||
}
|
}
|
||||||
if (id !== Path.Masks) {
|
if (id !== Path.Masks) {
|
||||||
return navigate(id);
|
router.push(id);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (config.dontShowMaskSplashScreen !== true) {
|
if (config.dontShowMaskSplashScreen !== true) {
|
||||||
navigate(Path.NewChat, { state: { fromHome: true } });
|
// navigate(Path.NewChat, { state: { fromHome: true } });
|
||||||
|
router.push(Path.NewChat);
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
navigate(Path.Masks, { state: { fromHome: true } });
|
// navigate(Path.Masks, { state: { fromHome: true } });
|
||||||
|
router.push(Path.Masks);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
groups={{
|
groups={{
|
||||||
|
44
app/hooks/useDeviceInfo.ts
Normal file
44
app/hooks/useDeviceInfo.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
// retur user device info
|
||||||
|
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
export function useDeviceInfo() {
|
||||||
|
const [deviceInfo, setDeviceInfo] = useState({});
|
||||||
|
|
||||||
|
const [systemInfo, setSystemInfo] = useState<string | null>(null);
|
||||||
|
const [deviceType, setDeviceType] = useState<string | null>(null);
|
||||||
|
useEffect(() => {
|
||||||
|
const userAgent = navigator.userAgent.toLowerCase();
|
||||||
|
|
||||||
|
if (/iphone|ipad|ipod/.test(userAgent)) {
|
||||||
|
setSystemInfo("iOS");
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const onResize = () => {
|
||||||
|
setDeviceInfo({
|
||||||
|
width: window.innerWidth,
|
||||||
|
height: window.innerHeight,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (window.innerWidth < 600) {
|
||||||
|
setDeviceType("mobile");
|
||||||
|
} else {
|
||||||
|
setDeviceType("desktop");
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("resize", onResize);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("resize", onResize);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return {
|
||||||
|
windowSize: deviceInfo,
|
||||||
|
systemInfo,
|
||||||
|
deviceType,
|
||||||
|
};
|
||||||
|
}
|
@@ -32,7 +32,7 @@ interface Position {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function useRelativePosition({
|
export default function useRelativePosition({
|
||||||
containerRef = { current: window.document.body },
|
containerRef = { current: null },
|
||||||
delay = 100,
|
delay = 100,
|
||||||
offsetDistance = 0,
|
offsetDistance = 0,
|
||||||
}: Options) {
|
}: Options) {
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { useLayoutEffect, useRef, useState } from "react";
|
import { useEffect, useLayoutEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
type Size = {
|
type Size = {
|
||||||
width: number;
|
width: number;
|
||||||
@@ -10,10 +10,14 @@ export function useWindowSize(callback?: (size: Size) => void) {
|
|||||||
|
|
||||||
callbackRef.current = callback;
|
callbackRef.current = callback;
|
||||||
|
|
||||||
const [size, setSize] = useState({
|
const [size, setSize] = useState({});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setSize({
|
||||||
width: window.innerWidth,
|
width: window.innerWidth,
|
||||||
height: window.innerHeight,
|
height: window.innerHeight,
|
||||||
});
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
const onResize = () => {
|
const onResize = () => {
|
||||||
|
14
app/utils.ts
14
app/utils.ts
@@ -291,18 +291,16 @@ export function getMessageImages(message: RequestMessage): string[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function isVisionModel(model: string) {
|
export function isVisionModel(model: string) {
|
||||||
|
|
||||||
// Note: This is a better way using the TypeScript feature instead of `&&` or `||` (ts v5.5.0-dev.20240314 I've been using)
|
// Note: This is a better way using the TypeScript feature instead of `&&` or `||` (ts v5.5.0-dev.20240314 I've been using)
|
||||||
|
|
||||||
const visionKeywords = [
|
const visionKeywords = ["vision", "claude-3", "gemini-1.5-pro"];
|
||||||
"vision",
|
|
||||||
"claude-3",
|
|
||||||
"gemini-1.5-pro",
|
|
||||||
];
|
|
||||||
|
|
||||||
const isGpt4Turbo = model.includes("gpt-4-turbo") && !model.includes("preview");
|
const isGpt4Turbo =
|
||||||
|
model.includes("gpt-4-turbo") && !model.includes("preview");
|
||||||
|
|
||||||
return visionKeywords.some((keyword) => model.includes(keyword)) || isGpt4Turbo;
|
return (
|
||||||
|
visionKeywords.some((keyword) => model.includes(keyword)) || isGpt4Turbo
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTime(dateTime: string) {
|
export function getTime(dateTime: string) {
|
||||||
|
Reference in New Issue
Block a user