feat: add mask page

This commit is contained in:
Yidadaa
2023-04-25 00:49:27 +08:00
parent 708c6829f7
commit ffa7302571
22 changed files with 460 additions and 232 deletions

View File

@@ -57,6 +57,8 @@ import { useNavigate } from "react-router-dom";
import { Path } from "../constant";
import { ModelConfigList } from "./model-config";
import { Avatar, AvatarPicker } from "./emoji";
import { MaskConfig } from "./mask";
import { DEFAULT_MASK_ID } from "../store/mask";
const Markdown = dynamic(
async () => memo((await import("./markdown")).Markdown),
@@ -103,103 +105,10 @@ function exportMessages(messages: Message[], topic: string) {
});
}
function ContextPrompts() {
const chatStore = useChatStore();
const session = chatStore.currentSession();
const context = session.context;
const addContextPrompt = (prompt: Message) => {
chatStore.updateCurrentSession((session) => {
session.context.push(prompt);
});
};
const removeContextPrompt = (i: number) => {
chatStore.updateCurrentSession((session) => {
session.context.splice(i, 1);
});
};
const updateContextPrompt = (i: number, prompt: Message) => {
chatStore.updateCurrentSession((session) => {
session.context[i] = prompt;
});
};
return (
<>
<div className={chatStyle["context-prompt"]} style={{ marginBottom: 20 }}>
{context.map((c, i) => (
<div className={chatStyle["context-prompt-row"]} key={i}>
<select
value={c.role}
className={chatStyle["context-role"]}
onChange={(e) =>
updateContextPrompt(i, {
...c,
role: e.target.value as any,
})
}
>
{ROLES.map((r) => (
<option key={r} value={r}>
{r}
</option>
))}
</select>
<Input
value={c.content}
type="text"
className={chatStyle["context-content"]}
rows={1}
onInput={(e) =>
updateContextPrompt(i, {
...c,
content: e.currentTarget.value as any,
})
}
/>
<IconButton
icon={<DeleteIcon />}
className={chatStyle["context-delete-button"]}
onClick={() => removeContextPrompt(i)}
bordered
/>
</div>
))}
<div className={chatStyle["context-prompt-row"]}>
<IconButton
icon={<AddIcon />}
text={Locale.Context.Add}
bordered
className={chatStyle["context-prompt-button"]}
onClick={() =>
addContextPrompt({
role: "system",
content: "",
date: "",
})
}
/>
</div>
</div>
</>
);
}
export function SessionConfigModel(props: { onClose: () => void }) {
const chatStore = useChatStore();
const session = chatStore.currentSession();
const [showPicker, setShowPicker] = useState(false);
const updateConfig = (updater: (config: ModelConfig) => void) => {
const config = { ...session.modelConfig };
updater(config);
chatStore.updateCurrentSession((session) => (session.modelConfig = config));
};
return (
<div className="modal-mask">
<Modal
@@ -210,7 +119,7 @@ export function SessionConfigModel(props: { onClose: () => void }) {
key="reset"
icon={<CopyIcon />}
bordered
text="重置预设"
text="重置"
onClick={() =>
confirm(Locale.Memory.ResetConfirm) && chatStore.resetSession()
}
@@ -219,69 +128,29 @@ export function SessionConfigModel(props: { onClose: () => void }) {
key="copy"
icon={<CopyIcon />}
bordered
text="保存预设"
text="保存为面具"
onClick={() => copyToClipboard(session.memoryPrompt)}
/>,
]}
>
<ContextPrompts />
<List>
<ListItem title={"角色头像"}>
<Popover
content={
<AvatarPicker
onEmojiClick={(emoji) =>
chatStore.updateCurrentSession(
(session) => (session.avatar = emoji),
)
}
></AvatarPicker>
}
open={showPicker}
onClose={() => setShowPicker(false)}
>
<div
onClick={() => setShowPicker(true)}
style={{ cursor: "pointer" }}
>
{session.avatar ? (
<Avatar avatar={session.avatar} />
) : (
<Avatar model={session.modelConfig.model} />
)}
</div>
</Popover>
</ListItem>
<ListItem title={"对话标题"}>
<input
type="text"
value={session.topic}
onInput={(e) =>
chatStore.updateCurrentSession(
(session) => (session.topic = e.currentTarget.value),
)
}
></input>
</ListItem>
</List>
<List>
<ModelConfigList
modelConfig={session.modelConfig}
updateConfig={updateConfig}
/>
{session.modelConfig.sendMemory ? (
<ListItem
title={`${Locale.Memory.Title} (${session.lastSummarizeIndex} of
${session.messages.length})`}
subTitle={session.memoryPrompt || Locale.Memory.EmptyContent}
></ListItem>
) : (
<></>
)}
</List>
<MaskConfig
mask={session.mask}
updateMask={(updater) => {
const mask = { ...session.mask };
updater(mask);
chatStore.updateCurrentSession((session) => (session.mask = mask));
}}
extraListItems={
session.mask.modelConfig.sendMemory ? (
<ListItem
title={`${Locale.Memory.Title} (${session.lastSummarizeIndex} of ${session.messages.length})`}
subTitle={session.memoryPrompt || Locale.Memory.EmptyContent}
></ListItem>
) : (
<></>
)
}
></MaskConfig>
</Modal>
</div>
);
@@ -294,7 +163,7 @@ function PromptToast(props: {
}) {
const chatStore = useChatStore();
const session = chatStore.currentSession();
const context = session.context;
const context = session.mask.context;
return (
<div className={chatStyle["prompt-toast"]} key="prompt-toast">
@@ -617,7 +486,7 @@ export function Chat() {
inputRef.current?.focus();
};
const context: RenderMessage[] = session.context.slice();
const context: RenderMessage[] = session.mask.context.slice();
const accessStore = useAccessStore();
@@ -680,20 +549,20 @@ export function Chat() {
return (
<div className={styles.chat} key={session.id}>
<div className={styles["window-header"]}>
<div className={styles["window-header-title"]}>
<div className="window-header">
<div className="window-header-title">
<div
className={`${styles["window-header-main-title"]} ${styles["chat-body-title"]}`}
className={`window-header-main-title " ${styles["chat-body-title"]}`}
onClickCapture={renameSession}
>
{!session.topic ? DEFAULT_TOPIC : session.topic}
</div>
<div className={styles["window-header-sub-title"]}>
<div className="window-header-sub-title">
{Locale.Chat.SubTitle(session.messages.length)}
</div>
</div>
<div className={styles["window-actions"]}>
<div className={styles["window-action-button"] + " " + styles.mobile}>
<div className="window-actions">
<div className={"window-action-button" + " " + styles.mobile}>
<IconButton
icon={<ReturnIcon />}
bordered
@@ -701,14 +570,14 @@ export function Chat() {
onClick={() => navigate(Path.Home)}
/>
</div>
<div className={styles["window-action-button"]}>
<div className="window-action-button">
<IconButton
icon={<RenameIcon />}
bordered
onClick={renameSession}
/>
</div>
<div className={styles["window-action-button"]}>
<div className="window-action-button">
<IconButton
icon={<ExportIcon />}
bordered
@@ -722,7 +591,7 @@ export function Chat() {
/>
</div>
{!isMobileScreen && (
<div className={styles["window-action-button"]}>
<div className="window-action-button">
<IconButton
icon={config.tightBorder ? <MinIcon /> : <MaxIcon />}
bordered
@@ -773,10 +642,10 @@ export function Chat() {
<div className={styles["chat-message-avatar"]}>
{message.role === "user" ? (
<Avatar avatar={config.avatar} />
) : session.avatar ? (
<Avatar avatar={session.avatar} />
) : (
) : session.mask.id === DEFAULT_MASK_ID ? (
<Avatar model={message.model ?? "gpt-3.5-turbo"} />
) : (
<Avatar avatar={session.mask.avatar} />
)}
</div>
{showTyping && (