From ca295588c426001489d00907c1a255db00436d1a Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Sun, 9 Jul 2023 16:15:58 +0800 Subject: [PATCH 1/5] fix: #2308 improve chat actions --- app/components/chat.module.scss | 69 ++++++++------- app/components/chat.tsx | 148 +++++++++++++++----------------- app/locales/cn.ts | 2 +- app/locales/en.ts | 2 +- package.json | 1 + yarn.lock | 5 ++ 6 files changed, 112 insertions(+), 115 deletions(-) diff --git a/app/components/chat.module.scss b/app/components/chat.module.scss index fa3a1cf2..99b2d022 100644 --- a/app/components/chat.module.scss +++ b/app/components/chat.module.scss @@ -240,24 +240,39 @@ &:last-child { animation: slide-in ease 0.3s; } - - &:hover { - .chat-message-actions { - opacity: 1; - transform: translateY(0px); - max-width: 100%; - height: 40px; - } - - .chat-message-action-date { - opacity: 0.2; - } - } } .chat-message-user { display: flex; flex-direction: row-reverse; + + .chat-message-header { + flex-direction: row-reverse; + } +} + +.chat-message-header { + margin-top: 20px; + display: flex; + align-items: center; + + .chat-message-actions { + display: flex; + box-sizing: border-box; + font-size: 12px; + align-items: flex-end; + justify-content: space-between; + transition: all ease 0.3s; + transform: scale(0.9) translateY(5px); + margin: 0 10px; + opacity: 0; + pointer-events: none; + + .chat-input-actions { + display: flex; + flex-wrap: nowrap; + } + } } .chat-message-container { @@ -270,6 +285,12 @@ .chat-message-edit { opacity: 0.9; } + + .chat-message-actions { + opacity: 1; + pointer-events: all; + transform: scale(1) translateY(0); + } } } @@ -278,7 +299,6 @@ } .chat-message-avatar { - margin-top: 20px; position: relative; .chat-message-edit { @@ -318,27 +338,6 @@ border: var(--border-in-light); position: relative; transition: all ease 0.3s; - - .chat-message-actions { - display: flex; - box-sizing: border-box; - font-size: 12px; - align-items: flex-end; - justify-content: space-between; - transition: all ease 0.3s 0.15s; - transform: translateX(-5px) scale(0.9) translateY(30px); - opacity: 0; - height: 0; - max-width: 0; - position: absolute; - left: 0; - z-index: 2; - - .chat-input-actions { - display: flex; - flex-wrap: nowrap; - } - } } .chat-message-action-date { diff --git a/app/components/chat.tsx b/app/components/chat.tsx index 13105e84..c479a08a 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -708,27 +708,26 @@ export function Chat() { let lastUserMessageIndex: number | null = null; for (let i = 0; i < session.messages.length; i += 1) { const message = session.messages[i]; - if (message.id === messageId) { - break; - } if (message.role === "user") { lastUserMessageIndex = i; } + if (message.id === messageId) { + break; + } } return lastUserMessageIndex; }; - const deleteMessage = (userIndex: number) => { - chatStore.updateCurrentSession((session) => - session.messages.splice(userIndex, 2), + const deleteMessage = (msgId?: number) => { + chatStore.updateCurrentSession( + (session) => + (session.messages = session.messages.filter((m) => m.id !== msgId)), ); }; - const onDelete = (botMessageId: number) => { - const userIndex = findLastUserIndex(botMessageId); - if (userIndex === null) return; - deleteMessage(userIndex); + const onDelete = (msgId: number) => { + deleteMessage(msgId); }; const onResend = (botMessageId: number) => { @@ -737,20 +736,16 @@ export function Chat() { if (userIndex === null) return; setIsLoading(true); - const content = session.messages[userIndex].content; - deleteMessage(userIndex); - chatStore.onUserInput(content).then(() => setIsLoading(false)); + const userMsg = session.messages[userIndex]; + deleteMessage(userMsg.id); + deleteMessage(botMessageId); + chatStore.onUserInput(userMsg.content).then(() => setIsLoading(false)); inputRef.current?.focus(); }; - const onPinMessage = (botMessage: ChatMessage) => { - if (!botMessage.id) return; - const userMessageIndex = findLastUserIndex(botMessage.id); - if (userMessageIndex === null) return; - - const userMessage = session.messages[userMessageIndex]; + const onPinMessage = (message: ChatMessage) => { chatStore.updateCurrentSession((session) => - session.mask.context.push(userMessage, botMessage), + session.mask.context.push(message), ); showToast(Locale.Chat.Actions.PinToastContent, { @@ -923,11 +918,12 @@ export function Chat() { > {messages.map((message, i) => { const isUser = message.role === "user"; - const showActions = - !isUser && - i > 0 && - !(message.preview || message.content.length === 0) && - i >= context.length; // do not show actions for context prompts + // const showActions = + // !isUser && + // i > 0 && + // !(message.preview || message.content.length === 0) && + // i >= context.length; // do not show actions for context prompts + const showActions = true; const showTyping = message.preview || message.streaming; const shouldShowClearContextDivider = i === clearContextIndex - 1; @@ -941,64 +937,38 @@ export function Chat() { } >
-
-
- } - onClick={async () => { - const newMessage = await showPrompt( - Locale.Chat.Actions.Edit, - message.content, - 10, - ); - chatStore.updateCurrentSession((session) => { - const m = session.messages.find( - (m) => m.id === message.id, +
+
+
+ } + onClick={async () => { + const newMessage = await showPrompt( + Locale.Chat.Actions.Edit, + message.content, + 10, ); - if (m) { - m.content = newMessage; - } - }); - }} - > + chatStore.updateCurrentSession((session) => { + const m = session.messages.find( + (m) => m.id === message.id, + ); + if (m) { + m.content = newMessage; + } + }); + }} + > +
+ {isUser ? ( + + ) : ( + + )}
- {isUser ? ( - - ) : ( - - )} -
- {showTyping && ( -
- {Locale.Chat.Typing} -
- )} -
- onRightClick(e, message)} - onDoubleClickCapture={() => { - if (!isMobileScreen) return; - setUserInput(message.content); - }} - fontSize={fontSize} - parentRef={scrollRef} - defaultShow={i >= messages.length - 10} - /> {showActions && (
-
+
{message.streaming ? ( )}
+ {showTyping && ( +
+ {Locale.Chat.Typing} +
+ )} +
+ onRightClick(e, message)} + onDoubleClickCapture={() => { + if (!isMobileScreen) return; + setUserInput(message.content); + }} + fontSize={fontSize} + parentRef={scrollRef} + defaultShow={i >= messages.length - 10} + /> +
{showActions && (
diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 07e87cbe..c6ba4ed7 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -26,7 +26,7 @@ const cn = { Stop: "停止", Retry: "重试", Pin: "固定", - PinToastContent: "已将 2 条对话固定至预设提示词", + PinToastContent: "已将 1 条对话固定至预设提示词", PinToastAction: "查看", Delete: "删除", Edit: "编辑", diff --git a/app/locales/en.ts b/app/locales/en.ts index 9373e2b1..23b6e7ca 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -28,7 +28,7 @@ const en: LocaleType = { Stop: "Stop", Retry: "Retry", Pin: "Pin", - PinToastContent: "Pinned 2 messages to contextual prompts", + PinToastContent: "Pinned 1 messages to contextual prompts", PinToastAction: "View", Delete: "Delete", Edit: "Edit", diff --git a/package.json b/package.json index cec288f4..20b76a44 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "fuse.js": "^6.6.2", "html-to-image": "^1.11.11", "mermaid": "^10.2.3", + "nanoid": "^4.0.2", "next": "^13.4.6", "node-fetch": "^3.3.1", "react": "^18.2.0", diff --git a/yarn.lock b/yarn.lock index 4e86fd7c..1c76bd4e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4639,6 +4639,11 @@ nanoid@^3.3.4: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== +nanoid@^4.0.2: + version "4.0.2" + resolved "https://registry.npmmirror.com/nanoid/-/nanoid-4.0.2.tgz#140b3c5003959adbebf521c170f282c5e7f9fb9e" + integrity sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw== + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" From 6014b765f4d42585cd91d07887cc27fd64ae2880 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Sun, 9 Jul 2023 16:26:00 +0800 Subject: [PATCH 2/5] feat: close #2294 add documents for adding a new translation --- docs/translation.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 docs/translation.md diff --git a/docs/translation.md b/docs/translation.md new file mode 100644 index 00000000..ebe1d6d7 --- /dev/null +++ b/docs/translation.md @@ -0,0 +1,12 @@ +# How to add a new translation? + +Assume that we are adding a new translation for `new`. + +1. copy `app/locales/en.ts` to `app/locales/new.ts`; +2. edit `new.ts`, change `const en: LocaleType = ` to `const new: PartialLocaleType`, and `export default new;`; +3. edit `app/locales/index.ts`: +4. `import new from './new.ts'`; +5. add `new` to `ALL_LANGS`; +6. add `new: "new lang"` to `ALL_LANG_OPTIONS`; +7. translate the strings in `new.ts`; +8. submit a pull request, and the author will merge it. From c7e976c8c58210a27491ba352da18e13a8bf8e91 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Sun, 9 Jul 2023 16:26:18 +0800 Subject: [PATCH 3/5] chore: update readme --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index cba9d35f..1ca37656 100644 --- a/README.md +++ b/README.md @@ -263,6 +263,10 @@ bash <(curl -s https://raw.githubusercontent.com/Yidadaa/ChatGPT-Next-Web/main/s ![More](./docs/images/more.png) +## Translation + +If you want to add a new translation, read this [document](./docs/translation.md). + ## Donation [Buy Me a Coffee](https://www.buymeacoffee.com/yidadaa) From 90d8f3117f787584e54b250c0914d09b8617dc09 Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Sun, 9 Jul 2023 16:28:15 +0800 Subject: [PATCH 4/5] fix: #2295 use correct methods to migrate state --- app/store/config.ts | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/app/store/config.ts b/app/store/config.ts index fee009c0..cf390c74 100644 --- a/app/store/config.ts +++ b/app/store/config.ts @@ -139,19 +139,20 @@ export const useAppConfig = create()( name: StoreKey.Config, version: 3.4, migrate(persistedState, version) { - if (version === 3.4) return persistedState as any; - const state = persistedState as ChatConfig; - state.modelConfig.sendMemory = true; - state.modelConfig.historyMessageCount = 4; - state.modelConfig.compressMessageLengthThreshold = 1000; - state.modelConfig.frequency_penalty = 0; - state.modelConfig.top_p = 1; - state.modelConfig.template = DEFAULT_INPUT_TEMPLATE; - state.dontShowMaskSplashScreen = false; - state.hideBuiltinMasks = false; - return state; + if (version < 3.4) { + state.modelConfig.sendMemory = true; + state.modelConfig.historyMessageCount = 4; + state.modelConfig.compressMessageLengthThreshold = 1000; + state.modelConfig.frequency_penalty = 0; + state.modelConfig.top_p = 1; + state.modelConfig.template = DEFAULT_INPUT_TEMPLATE; + state.dontShowMaskSplashScreen = false; + state.hideBuiltinMasks = false; + } + + return state as any; }, }, ), From b55b01cb13ac3ab96d0c621c94b2968424825d2f Mon Sep 17 00:00:00 2001 From: Yidadaa Date: Sun, 9 Jul 2023 16:39:46 +0800 Subject: [PATCH 5/5] feat: #2308 improve chat actions ux --- app/components/chat.tsx | 41 +++++++++++++++++++++-------------------- app/locales/cn.ts | 1 + app/locales/en.ts | 1 + 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/app/components/chat.tsx b/app/components/chat.tsx index c479a08a..02c0dd92 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -730,16 +730,18 @@ export function Chat() { deleteMessage(msgId); }; - const onResend = (botMessageId: number) => { - // find last user input message and resend - const userIndex = findLastUserIndex(botMessageId); - if (userIndex === null) return; + const onResend = (message: ChatMessage) => { + let content = message.content; + + if (message.role === "assistant" && message.id) { + const userIndex = findLastUserIndex(message.id); + if (userIndex) { + content = session.messages.at(userIndex)?.content ?? content; + } + } setIsLoading(true); - const userMsg = session.messages[userIndex]; - deleteMessage(userMsg.id); - deleteMessage(botMessageId); - chatStore.onUserInput(userMsg.content).then(() => setIsLoading(false)); + chatStore.onUserInput(content).then(() => setIsLoading(false)); inputRef.current?.focus(); }; @@ -918,12 +920,11 @@ export function Chat() { > {messages.map((message, i) => { const isUser = message.role === "user"; - // const showActions = - // !isUser && - // i > 0 && - // !(message.preview || message.content.length === 0) && - // i >= context.length; // do not show actions for context prompts - const showActions = true; + const isContext = i < context.length; + const showActions = + i > 0 && + !(message.preview || message.content.length === 0) && + !isContext; const showTyping = message.preview || message.streaming; const shouldShowClearContextDivider = i === clearContextIndex - 1; @@ -980,7 +981,7 @@ export function Chat() { } - onClick={() => onResend(message.id ?? i)} + onClick={() => onResend(message)} />
- {showActions && ( -
- {message.date.toLocaleString()} -
- )} +
+ {isContext + ? Locale.Chat.IsContext + : message.date.toLocaleString()} +
{shouldShowClearContextDivider && } diff --git a/app/locales/cn.ts b/app/locales/cn.ts index c6ba4ed7..c32014be 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -66,6 +66,7 @@ const cn = { Reset: "清除记忆", SaveAs: "存为面具", }, + IsContext: "预设提示词", }, Export: { Title: "分享聊天记录", diff --git a/app/locales/en.ts b/app/locales/en.ts index 23b6e7ca..d96b978f 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -68,6 +68,7 @@ const en: LocaleType = { Reset: "Reset to Default", SaveAs: "Save as Mask", }, + IsContext: "Contextual Prompt", }, Export: { Title: "Export Messages",