From 98264f65cf50d236e365dfdd44d1d07d18e01c0f Mon Sep 17 00:00:00 2001 From: Nestor Qin Date: Sun, 19 May 2024 05:06:50 -0400 Subject: [PATCH] feat: Retain model selection upon starting a new chat session --- app/components/new-chat.module.scss | 125 -------------------- app/components/new-chat.tsx | 171 ---------------------------- app/store/chat.ts | 3 + app/store/mask.ts | 6 +- 4 files changed, 7 insertions(+), 298 deletions(-) delete mode 100644 app/components/new-chat.module.scss delete mode 100644 app/components/new-chat.tsx diff --git a/app/components/new-chat.module.scss b/app/components/new-chat.module.scss deleted file mode 100644 index b291a236..00000000 --- a/app/components/new-chat.module.scss +++ /dev/null @@ -1,125 +0,0 @@ -@import "../styles/animation.scss"; - -.new-chat { - height: 100%; - width: 100%; - display: flex; - align-items: center; - justify-content: center; - flex-direction: column; - - .mask-header { - display: flex; - justify-content: space-between; - width: 100%; - padding: 10px; - box-sizing: border-box; - animation: slide-in-from-top ease 0.3s; - } - - .mask-cards { - display: flex; - margin-top: 5vh; - margin-bottom: 20px; - animation: slide-in ease 0.3s; - - .mask-card { - padding: 20px 10px; - border: var(--border-in-light); - box-shadow: var(--card-shadow); - border-radius: 14px; - background-color: var(--white); - transform: scale(1); - - &:first-child { - transform: rotate(-15deg) translateY(5px); - } - - &:last-child { - transform: rotate(15deg) translateY(5px); - } - } - } - - .title { - font-size: 32px; - font-weight: bolder; - margin-bottom: 1vh; - animation: slide-in ease 0.35s; - } - - .sub-title { - animation: slide-in ease 0.4s; - } - - .actions { - margin-top: 5vh; - margin-bottom: 2vh; - animation: slide-in ease 0.45s; - display: flex; - justify-content: center; - font-size: 12px; - - .skip { - margin-left: 10px; - } - } - - .masks { - flex-grow: 1; - width: 100%; - overflow: auto; - align-items: center; - padding-top: 20px; - - $linear: linear-gradient( - to bottom, - rgba(0, 0, 0, 0), - rgba(0, 0, 0, 1), - rgba(0, 0, 0, 0) - ); - - -webkit-mask-image: $linear; - mask-image: $linear; - - animation: slide-in ease 0.5s; - - .mask-row { - display: flex; - // justify-content: center; - margin-bottom: 10px; - - @for $i from 1 to 10 { - &:nth-child(#{$i * 2}) { - margin-left: 50px; - } - } - - .mask { - display: flex; - align-items: center; - padding: 10px 14px; - border: var(--border-in-light); - box-shadow: var(--card-shadow); - background-color: var(--white); - border-radius: 10px; - margin-right: 10px; - max-width: 8em; - transform: scale(1); - cursor: pointer; - transition: all ease 0.3s; - - &:hover { - transform: translateY(-5px) scale(1.1); - z-index: 999; - border-color: var(--primary); - } - - .mask-name { - margin-left: 10px; - font-size: 14px; - } - } - } - } -} diff --git a/app/components/new-chat.tsx b/app/components/new-chat.tsx deleted file mode 100644 index 7f9ca563..00000000 --- a/app/components/new-chat.tsx +++ /dev/null @@ -1,171 +0,0 @@ -import { useEffect, useRef, useState } from "react"; -import { Path, SlotID } from "../constant"; -import { IconButton } from "./button"; -import { EmojiAvatar } from "./emoji"; -import styles from "./new-chat.module.scss"; - -import LeftIcon from "../icons/left.svg"; -import LightningIcon from "../icons/lightning.svg"; -import EyeIcon from "../icons/eye.svg"; - -import { useLocation, useNavigate } from "react-router-dom"; -import { Mask, useMaskStore } from "../store/mask"; -import Locale from "../locales"; -import { useAppConfig, useChatStore } from "../store"; -import { MaskAvatar } from "./mask"; -import { useCommand } from "../command"; -import { showConfirm } from "./ui-lib"; -import { BUILTIN_MASK_STORE } from "../masks"; - -function MaskItem(props: { mask: Mask; onClick?: () => void }) { - return ( -
- -
{props.mask.name}
-
- ); -} - -function useMaskGroup(masks: Mask[]) { - const [groups, setGroups] = useState([]); - - useEffect(() => { - const computeGroup = () => { - const appBody = document.getElementById(SlotID.AppBody); - if (!appBody || masks.length === 0) return; - - const rect = appBody.getBoundingClientRect(); - const maxWidth = rect.width; - const maxHeight = rect.height * 0.6; - const maskItemWidth = 120; - const maskItemHeight = 50; - - const randomMask = () => masks[Math.floor(Math.random() * masks.length)]; - let maskIndex = 0; - const nextMask = () => masks[maskIndex++ % masks.length]; - - const rows = Math.ceil(maxHeight / maskItemHeight); - const cols = Math.ceil(maxWidth / maskItemWidth); - - const newGroups = new Array(rows) - .fill(0) - .map((_, _i) => - new Array(cols) - .fill(0) - .map((_, j) => (j < 1 || j > cols - 2 ? randomMask() : nextMask())), - ); - - setGroups(newGroups); - }; - - computeGroup(); - - window.addEventListener("resize", computeGroup); - return () => window.removeEventListener("resize", computeGroup); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - return groups; -} - -export function NewChat() { - const chatStore = useChatStore(); - const maskStore = useMaskStore(); - - const masks = maskStore.getAll(); - const groups = useMaskGroup(masks); - - const navigate = useNavigate(); - const config = useAppConfig(); - - const maskRef = useRef(null); - - const { state } = useLocation(); - - const startChat = (mask?: Mask) => { - setTimeout(() => { - chatStore.newSession(mask); - navigate(Path.Chat); - }, 10); - }; - - useCommand({ - mask: (id) => { - try { - const mask = maskStore.get(id) ?? BUILTIN_MASK_STORE.get(id); - startChat(mask ?? undefined); - } catch { - console.error("[New Chat] failed to create chat from mask id=", id); - } - }, - }); - - useEffect(() => { - if (maskRef.current) { - maskRef.current.scrollLeft = - (maskRef.current.scrollWidth - maskRef.current.clientWidth) / 2; - } - }, [groups]); - - return ( -
-
- } - text={Locale.NewChat.Return} - onClick={() => navigate(Path.Home)} - > -
-
-
- -
-
- -
-
- -
-
- -
{Locale.NewChat.Title}
-
{Locale.NewChat.SubTitle}
- -
- navigate(Path.Masks)} - icon={} - bordered - shadow - /> - - startChat()} - icon={} - type="primary" - shadow - className={styles["skip"]} - /> -
- -
- {groups.map((masks, i) => ( -
- {masks.map((mask, index) => ( - startChat(mask)} - /> - ))} -
- ))} -
-
- ); -} diff --git a/app/store/chat.ts b/app/store/chat.ts index 8561316e..b7bb2e2d 100644 --- a/app/store/chat.ts +++ b/app/store/chat.ts @@ -191,6 +191,9 @@ export const useChatStore = createPersistStore( }, }; session.topic = mask.name; + } else { + // inherit current model config + session.mask.modelConfig = this.currentSession().mask.modelConfig; } set((state) => ({ diff --git a/app/store/mask.ts b/app/store/mask.ts index 909b4134..4fde7558 100644 --- a/app/store/mask.ts +++ b/app/store/mask.ts @@ -1,6 +1,6 @@ import { BUILTIN_MASKS } from "../masks"; import { getLang, Lang } from "../locales"; -import { DEFAULT_TOPIC, ChatMessage } from "./chat"; +import { DEFAULT_TOPIC, ChatMessage, useChatStore } from "./chat"; import { ModelConfig, useAppConfig } from "./config"; import { StoreKey } from "../constant"; import { nanoid } from "nanoid"; @@ -33,7 +33,9 @@ export const createEmptyMask = () => name: DEFAULT_TOPIC, context: [], syncGlobalConfig: true, // use global config as default - modelConfig: { ...useAppConfig.getState().modelConfig }, + modelConfig: { + ...useAppConfig.getState().modelConfig, + }, lang: getLang(), builtin: false, createdAt: Date.now(),