|
@@ -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 (
|
|
|
+ <div className="modal-mask">
|
|
|
+ <Modal
|
|
|
+ title={Locale.UI.Edit}
|
|
|
+ onClose={props.onClose}
|
|
|
+ actions={[
|
|
|
+ <IconButton
|
|
|
+ text={Locale.UI.Cancel}
|
|
|
+ icon={<CancelIcon />}
|
|
|
+ key="cancel"
|
|
|
+ onClick={() => {
|
|
|
+ props.onClose();
|
|
|
+ }}
|
|
|
+ />,
|
|
|
+ <IconButton
|
|
|
+ type="primary"
|
|
|
+ text={Locale.UI.Confirm}
|
|
|
+ icon={<ConfirmIcon />}
|
|
|
+ key="ok"
|
|
|
+ onClick={() => {
|
|
|
+ chatStore.updateCurrentSession(
|
|
|
+ (session) => (session.messages = messages),
|
|
|
+ );
|
|
|
+ props.onClose();
|
|
|
+ }}
|
|
|
+ />,
|
|
|
+ ]}
|
|
|
+ >
|
|
|
+ <List>
|
|
|
+ <ListItem
|
|
|
+ title={Locale.Chat.EditMessage.Topic.Title}
|
|
|
+ subTitle={Locale.Chat.EditMessage.Topic.SubTitle}
|
|
|
+ >
|
|
|
+ <input
|
|
|
+ type="text"
|
|
|
+ value={session.topic}
|
|
|
+ onInput={(e) =>
|
|
|
+ chatStore.updateCurrentSession(
|
|
|
+ (session) => (session.topic = e.currentTarget.value),
|
|
|
+ )
|
|
|
+ }
|
|
|
+ ></input>
|
|
|
+ </ListItem>
|
|
|
+ </List>
|
|
|
+ <ContextPrompts
|
|
|
+ context={messages}
|
|
|
+ updateContext={(updater) => {
|
|
|
+ const newMessages = messages.slice();
|
|
|
+ updater(newMessages);
|
|
|
+ setMessages(newMessages);
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ </Modal>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
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 (
|
|
|
<div className={styles.chat} key={session.id}>
|
|
|
<div className="window-header" data-tauri-drag-region>
|
|
@@ -938,7 +980,7 @@ export function Chat() {
|
|
|
<div className={`window-header-title ${styles["chat-body-title"]}`}>
|
|
|
<div
|
|
|
className={`window-header-main-title ${styles["chat-body-main-title"]}`}
|
|
|
- onClickCapture={renameSession}
|
|
|
+ onClickCapture={() => setIsEditingMessage(true)}
|
|
|
>
|
|
|
{!session.topic ? DEFAULT_TOPIC : session.topic}
|
|
|
</div>
|
|
@@ -952,7 +994,7 @@ export function Chat() {
|
|
|
<IconButton
|
|
|
icon={<RenameIcon />}
|
|
|
bordered
|
|
|
- onClick={renameSession}
|
|
|
+ onClick={() => setIsEditingMessage(true)}
|
|
|
/>
|
|
|
</div>
|
|
|
)}
|
|
@@ -1170,6 +1212,14 @@ export function Chat() {
|
|
|
{showExport && (
|
|
|
<ExportMessageModal onClose={() => setShowExport(false)} />
|
|
|
)}
|
|
|
+
|
|
|
+ {isEditingMessage && (
|
|
|
+ <EditMessageModal
|
|
|
+ onClose={() => {
|
|
|
+ setIsEditingMessage(false);
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ )}
|
|
|
</div>
|
|
|
);
|
|
|
}
|