From 7c2fa9f8a4c9b04d534e9bea946fa3e909369240 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Fri, 21 Jul 2023 00:24:26 +0800 Subject: [PATCH] feat: close #2449 edit / insert / delete messages modal --- app/components/chat.module.scss | 19 +++- app/components/chat.tsx | 108 ++++++++++++++----- app/components/mask.tsx | 182 ++++++++++++++++++-------------- app/locales/cn.ts | 6 ++ app/locales/en.ts | 6 ++ 5 files changed, 211 insertions(+), 110 deletions(-) diff --git a/app/components/chat.module.scss b/app/components/chat.module.scss index 6f443200..a3ab5606 100644 --- a/app/components/chat.module.scss +++ b/app/components/chat.module.scss @@ -95,11 +95,28 @@ } .context-prompt { + .context-prompt-insert { + display: flex; + justify-content: center; + padding: 4px; + opacity: 0.2; + transition: all ease 0.3s; + background-color: rgba(0, 0, 0, 0); + cursor: pointer; + border-radius: 4px; + margin-top: 4px; + margin-bottom: 4px; + + &:hover { + opacity: 1; + background-color: rgba(0, 0, 0, 0.05); + } + } + .context-prompt-row { display: flex; justify-content: center; width: 100%; - margin-bottom: 10px; &:hover { .context-drag { diff --git a/app/components/chat.tsx b/app/components/chat.tsx index db9f8448..7f54a7dd 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -25,6 +25,8 @@ import SettingsIcon from "../icons/chat-settings.svg"; import DeleteIcon from "../icons/clear.svg"; import PinIcon from "../icons/pin.svg"; import EditIcon from "../icons/rename.svg"; +import ConfirmIcon from "../icons/confirm.svg"; +import CancelIcon from "../icons/cancel.svg"; import LightIcon from "../icons/light.svg"; import DarkIcon from "../icons/dark.svg"; @@ -63,6 +65,7 @@ import { IconButton } from "./button"; import styles from "./chat.module.scss"; import { + List, ListItem, Modal, Selector, @@ -73,7 +76,7 @@ import { import { useLocation, useNavigate } from "react-router-dom"; import { LAST_INPUT_KEY, Path, REQUEST_TIMEOUT_MS } from "../constant"; import { Avatar } from "./emoji"; -import { MaskAvatar, MaskConfig } from "./mask"; +import { ContextPrompts, MaskAvatar, MaskConfig } from "./mask"; import { useMaskStore } from "../store/mask"; import { ChatCommandPrefix, useChatCommand, useCommand } from "../command"; import { prettyObject } from "../utils/format"; @@ -520,6 +523,68 @@ export function ChatActions(props: { ); } +export function EditMessageModal(props: { onClose: () => void }) { + const chatStore = useChatStore(); + const session = chatStore.currentSession(); + const [messages, setMessages] = useState(session.messages.slice()); + + return ( +
+ } + key="cancel" + onClick={() => { + props.onClose(); + }} + />, + } + key="ok" + onClick={() => { + chatStore.updateCurrentSession( + (session) => (session.messages = messages), + ); + props.onClose(); + }} + />, + ]} + > + + + + chatStore.updateCurrentSession( + (session) => (session.topic = e.currentTarget.value), + ) + } + > + + + { + const newMessages = messages.slice(); + updater(newMessages); + setMessages(newMessages); + }} + /> + +
+ ); +} + export function Chat() { type RenderMessage = ChatMessage & { preview?: boolean }; @@ -710,22 +775,6 @@ export function Chat() { } }; - const findLastUserIndex = (messageId: string) => { - // find last user input message - let lastUserMessageIndex: number | null = null; - for (let i = 0; i < session.messages.length; i += 1) { - const message = session.messages[i]; - if (message.role === "user") { - lastUserMessageIndex = i; - } - if (message.id === messageId) { - break; - } - } - - return lastUserMessageIndex; - }; - const deleteMessage = (msgId?: string) => { chatStore.updateCurrentSession( (session) => @@ -859,16 +908,6 @@ export function Chat() { const [showPromptModal, setShowPromptModal] = useState(false); - const renameSession = () => { - showPrompt(Locale.Chat.Rename, session.topic).then((newTopic) => { - if (newTopic && newTopic !== session.topic) { - chatStore.updateCurrentSession( - (session) => (session.topic = newTopic!), - ); - } - }); - }; - const clientConfig = useMemo(() => getClientConfig(), []); const location = useLocation(); @@ -919,6 +958,9 @@ export function Chat() { }, }); + // edit / insert message modal + const [isEditingMessage, setIsEditingMessage] = useState(false); + return (
@@ -938,7 +980,7 @@ export function Chat() {
setIsEditingMessage(true)} > {!session.topic ? DEFAULT_TOPIC : session.topic}
@@ -952,7 +994,7 @@ export function Chat() { } bordered - onClick={renameSession} + onClick={() => setIsEditingMessage(true)} />
)} @@ -1170,6 +1212,14 @@ export function Chat() { {showExport && ( setShowExport(false)} /> )} + + {isEditingMessage && ( + { + setIsEditingMessage(false); + }} + /> + )}
); } diff --git a/app/components/mask.tsx b/app/components/mask.tsx index b9072213..3d8ce3a2 100644 --- a/app/components/mask.tsx +++ b/app/components/mask.tsx @@ -215,67 +215,58 @@ function ContextPromptItem(props: { const [focusingInput, setFocusingInput] = useState(false); return ( - - {(provided) => ( -
- {!focusingInput && ( - <> -
- -
- - - )} - setFocusingInput(true)} - onBlur={() => { - setFocusingInput(false); - // If the selection is not removed when the user loses focus, some - // extensions like "Translate" will always display a floating bar - window?.getSelection()?.removeAllRanges(); - }} - onInput={(e) => +
+ {!focusingInput && ( + <> +
+ +
+ + )} - + setFocusingInput(true)} + onBlur={() => { + setFocusingInput(false); + // If the selection is not removed when the user loses focus, some + // extensions like "Translate" will always display a floating bar + window?.getSelection()?.removeAllRanges(); + }} + onInput={(e) => + props.update({ + ...props.prompt, + content: e.currentTarget.value as any, + }) + } + /> + {!focusingInput && ( + } + className={chatStyle["context-delete-button"]} + onClick={() => props.remove()} + bordered + /> + )} +
); } @@ -285,8 +276,8 @@ export function ContextPrompts(props: { }) { const context = props.context; - const addContextPrompt = (prompt: ChatMessage) => { - props.updateContext((context) => context.push(prompt)); + const addContextPrompt = (prompt: ChatMessage, i: number) => { + props.updateContext((context) => context.splice(i, 0, prompt)); }; const removeContextPrompt = (i: number) => { @@ -319,13 +310,41 @@ export function ContextPrompts(props: { {(provided) => (
{context.map((c, i) => ( - updateContextPrompt(i, prompt)} - remove={() => removeContextPrompt(i)} - /> + > + {(provided) => ( +
+ updateContextPrompt(i, prompt)} + remove={() => removeContextPrompt(i)} + /> +
{ + addContextPrompt( + createMessage({ + role: "user", + content: "", + date: new Date().toLocaleString(), + }), + i + 1, + ); + }} + > + +
+
+ )} + ))} {provided.placeholder}
@@ -333,23 +352,26 @@ export function ContextPrompts(props: { -
- } - text={Locale.Context.Add} - bordered - className={chatStyle["context-prompt-button"]} - onClick={() => - addContextPrompt( - createMessage({ - role: "user", - content: "", - date: "", - }), - ) - } - /> -
+ {props.context.length === 0 && ( +
+ } + text={Locale.Context.Add} + bordered + className={chatStyle["context-prompt-button"]} + onClick={() => + addContextPrompt( + createMessage({ + role: "user", + content: "", + date: "", + }), + props.context.length, + ) + } + /> +
+ )}
); diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 54225e31..656cd5fe 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -18,6 +18,12 @@ const cn = { }, Chat: { SubTitle: (count: number) => `共 ${count} 条对话`, + EditMessage: { + Topic: { + Title: "聊天主题", + SubTitle: "更改当前聊天主题", + }, + }, Actions: { ChatList: "查看消息列表", CompressedHistory: "查看压缩后的历史 Prompt", diff --git a/app/locales/en.ts b/app/locales/en.ts index ebc19f07..2d83de92 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -20,6 +20,12 @@ const en: LocaleType = { }, Chat: { SubTitle: (count: number) => `${count} messages`, + EditMessage: { + Topic: { + Title: "Topic", + SubTitle: "Change the current topic", + }, + }, Actions: { ChatList: "Go To Chat List", CompressedHistory: "Compressed History Memory Prompt",