From 2beddab11406653d245d5b1a2258eaf04f035f6d Mon Sep 17 00:00:00 2001 From: josc146 Date: Fri, 16 Jun 2023 00:12:13 +0800 Subject: [PATCH] save conversation button --- backend-golang/file.go | 22 ++++ frontend/src/_locales/zh-hans/main.json | 5 +- frontend/src/pages/Chat.tsx | 125 ++++++++++++-------- frontend/src/stores/commonStore.ts | 14 +-- frontend/wailsjs/go/backend_golang/App.d.ts | 2 + frontend/wailsjs/go/backend_golang/App.js | 4 + 6 files changed, 112 insertions(+), 60 deletions(-) diff --git a/backend-golang/file.go b/backend-golang/file.go index 18858d1..5d62695 100644 --- a/backend-golang/file.go +++ b/backend-golang/file.go @@ -10,6 +10,8 @@ import ( "runtime" "strings" "time" + + wruntime "github.com/wailsapp/wails/v2/pkg/runtime" ) func (a *App) SaveJson(fileName string, jsonData any) error { @@ -119,6 +121,26 @@ func (a *App) CopyFile(src string, dst string) error { return nil } +func (a *App) OpenSaveFileDialog(filterPattern string, defaultFileName string, savedContent string) (string, error) { + path, err := wruntime.SaveFileDialog(a.ctx, wruntime.SaveDialogOptions{ + DefaultFilename: defaultFileName, + Filters: []wruntime.FileFilter{{ + Pattern: filterPattern, + }}, + CanCreateDirectories: true, + }) + if err != nil { + return "", err + } + if path == "" { + return "", nil + } + if err := os.WriteFile(path, []byte(savedContent), 0644); err != nil { + return "", err + } + return path, nil +} + func (a *App) OpenFileFolder(path string, relative bool) error { var absPath string var err error diff --git a/frontend/src/_locales/zh-hans/main.json b/frontend/src/_locales/zh-hans/main.json index 103000d..5648875 100644 --- a/frontend/src/_locales/zh-hans/main.json +++ b/frontend/src/_locales/zh-hans/main.json @@ -146,5 +146,8 @@ "Are you sure you want to reset this page? It cannot be undone.": "你确定要重置本页吗?这无法撤销", "Model file download is not complete": "模型文件下载未完成", "Error": "错误", - "Are you sure you want to clear the conversation? It cannot be undone.": "你确定要清空对话吗?这无法撤销" + "Are you sure you want to clear the conversation? It cannot be undone.": "你确定要清空对话吗?这无法撤销", + "Save": "保存", + "Conversation Saved": "对话已保存", + "Open": "打开" } \ No newline at end of file diff --git a/frontend/src/pages/Chat.tsx b/frontend/src/pages/Chat.tsx index 414fdbc..58b993f 100644 --- a/frontend/src/pages/Chat.tsx +++ b/frontend/src/pages/Chat.tsx @@ -10,12 +10,14 @@ import { ConversationPair, getConversationPairs, Record } from '../utils/get-con import logo from '../assets/images/logo.jpg'; import MarkdownRender from '../components/MarkdownRender'; import { ToolTipButton } from '../components/ToolTipButton'; -import { ArrowCircleUp28Regular, Delete28Regular, RecordStop28Regular } from '@fluentui/react-icons'; +import { ArrowCircleUp28Regular, Delete28Regular, RecordStop28Regular, Save28Regular } from '@fluentui/react-icons'; import { CopyButton } from '../components/CopyButton'; import { ReadButton } from '../components/ReadButton'; import { toast } from 'react-toastify'; import { WorkHeader } from '../components/WorkHeader'; import { DialogButton } from '../components/DialogButton'; +import { OpenFileFolder, OpenSaveFileDialog } from '../../wailsjs/go/backend_golang/App'; +import { toastWithButton } from '../utils'; export const userName = 'M E'; export const botName = 'A I'; @@ -40,7 +42,7 @@ export type MessageItem = { done: boolean } -export type Conversations = { +export type Conversation = { [uuid: string]: MessageItem } @@ -54,9 +56,9 @@ const ChatPanel: FC = observer(() => { let lastMessageId: string; let generating: boolean = false; - if (commonStore.conversationsOrder.length > 0) { - lastMessageId = commonStore.conversationsOrder[commonStore.conversationsOrder.length - 1]; - const lastMessage = commonStore.conversations[lastMessageId]; + if (commonStore.conversationOrder.length > 0) { + lastMessageId = commonStore.conversationOrder[commonStore.conversationOrder.length - 1]; + const lastMessage = commonStore.conversation[lastMessageId]; if (lastMessage.sender === botName) generating = !lastMessage.done; } @@ -68,9 +70,9 @@ const ChatPanel: FC = observer(() => { }, []); useEffect(() => { - if (commonStore.conversationsOrder.length === 0) { - commonStore.setConversationsOrder(['welcome']); - commonStore.setConversations({ + if (commonStore.conversationOrder.length === 0) { + commonStore.setConversationOrder(['welcome']); + commonStore.setConversation({ 'welcome': { sender: botName, type: MessageType.Normal, @@ -106,7 +108,7 @@ const ChatPanel: FC = observer(() => { const onSubmit = (message: string) => { const newId = uuid(); - commonStore.conversations[newId] = { + commonStore.conversation[newId] = { sender: userName, type: MessageType.Normal, color: 'brand', @@ -115,19 +117,19 @@ const ChatPanel: FC = observer(() => { side: 'right', done: true }; - commonStore.setConversations(commonStore.conversations); - commonStore.conversationsOrder.push(newId); - commonStore.setConversationsOrder(commonStore.conversationsOrder); + commonStore.setConversation(commonStore.conversation); + commonStore.conversationOrder.push(newId); + commonStore.setConversationOrder(commonStore.conversationOrder); const records: Record[] = []; - commonStore.conversationsOrder.forEach((uuid, index) => { - const conversation = commonStore.conversations[uuid]; - if (conversation.done && conversation.type === MessageType.Normal && conversation.sender === botName) { + commonStore.conversationOrder.forEach((uuid, index) => { + const messageItem = commonStore.conversation[uuid]; + if (messageItem.done && messageItem.type === MessageType.Normal && messageItem.sender === botName) { if (index > 0) { - const questionId = commonStore.conversationsOrder[index - 1]; - const question = commonStore.conversations[questionId]; + const questionId = commonStore.conversationOrder[index - 1]; + const question = commonStore.conversation[questionId]; if (question.done && question.type === MessageType.Normal && question.sender === userName) { - records.push({ question: question.content, answer: conversation.content }); + records.push({ question: question.content, answer: messageItem.content }); } } } @@ -136,7 +138,7 @@ const ChatPanel: FC = observer(() => { (messages as ConversationPair[]).push({ role: 'user', content: message }); const answerId = uuid(); - commonStore.conversations[answerId] = { + commonStore.conversation[answerId] = { sender: botName, type: MessageType.Normal, color: 'colorful', @@ -146,9 +148,9 @@ const ChatPanel: FC = observer(() => { side: 'left', done: false }; - commonStore.setConversations(commonStore.conversations); - commonStore.conversationsOrder.push(answerId); - commonStore.setConversationsOrder(commonStore.conversationsOrder); + commonStore.setConversation(commonStore.conversation); + commonStore.conversationOrder.push(answerId); + commonStore.setConversationOrder(commonStore.conversationOrder); setTimeout(scrollToBottom); let answer = ''; chatSseController = new AbortController(); @@ -169,10 +171,10 @@ const ChatPanel: FC = observer(() => { console.log('sse message', e); scrollToBottom(); if (e.data === '[DONE]') { - commonStore.conversations[answerId].done = true; - commonStore.conversations[answerId].content = commonStore.conversations[answerId].content.trim(); - commonStore.setConversations(commonStore.conversations); - commonStore.setConversationsOrder([...commonStore.conversationsOrder]); + commonStore.conversation[answerId].done = true; + commonStore.conversation[answerId].content = commonStore.conversation[answerId].content.trim(); + commonStore.setConversation(commonStore.conversation); + commonStore.setConversationOrder([...commonStore.conversationOrder]); return; } let data; @@ -184,19 +186,19 @@ const ChatPanel: FC = observer(() => { } if (data.choices && Array.isArray(data.choices) && data.choices.length > 0) { answer += data.choices[0]?.delta?.content || ''; - commonStore.conversations[answerId].content = answer; - commonStore.setConversations(commonStore.conversations); - commonStore.setConversationsOrder([...commonStore.conversationsOrder]); + commonStore.conversation[answerId].content = answer; + commonStore.setConversation(commonStore.conversation); + commonStore.setConversationOrder([...commonStore.conversationOrder]); } }, onclose() { console.log('Connection closed'); }, onerror(err) { - commonStore.conversations[answerId].type = MessageType.Error; - commonStore.conversations[answerId].done = true; - commonStore.setConversations(commonStore.conversations); - commonStore.setConversationsOrder([...commonStore.conversationsOrder]); + commonStore.conversation[answerId].type = MessageType.Error; + commonStore.conversation[answerId].done = true; + commonStore.setConversation(commonStore.conversation); + commonStore.setConversationOrder([...commonStore.conversationOrder]); throw err; } }); @@ -205,13 +207,13 @@ const ChatPanel: FC = observer(() => { return (
- {commonStore.conversationsOrder.map((uuid, index) => { - const conversation = commonStore.conversations[uuid]; + {commonStore.conversationOrder.map((uuid, index) => { + const messageItem = commonStore.conversation[uuid]; return
{ const utils = document.getElementById('utils-' + uuid); @@ -223,29 +225,29 @@ const ChatPanel: FC = observer(() => { }} >
- {conversation.content} + {messageItem.content}
- {(conversation.type === MessageType.Error || !conversation.done) && + {(messageItem.type === MessageType.Error || !messageItem.done) && }
- - + +
; @@ -259,8 +261,8 @@ const ChatPanel: FC = observer(() => { onConfirm={() => { if (generating) chatSseController?.abort(); - commonStore.setConversations({}); - commonStore.setConversationsOrder([]); + commonStore.setConversation({}); + commonStore.setConversationOrder([]); }} />