diff --git a/README.md b/README.md index 0cd435c..c85a233 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ docker run -d --name babel-duck --env-file .env -p 9000:9000 orenoid/babel-duck: - [ ] 对话模板 - [ ] 复述练习模式 +- [ ] 移动端适配 - [ ] 语音回放 - [ ] 支持多模态语音 - [ ] 接入更多 LLM/TTS/STT 服务 diff --git a/src/app/chat/components/chat.tsx b/src/app/chat/components/chat.tsx index 6a91de0..e5abf71 100644 --- a/src/app/chat/components/chat.tsx +++ b/src/app/chat/components/chat.tsx @@ -72,7 +72,7 @@ export function Chat({ chatID, chatTitle, loadChatByID, className = "" }: { throw new Error(`Chat intelligence with type ${type} not found`) } } - const inputHandlers = chatSettings !== undefined ? + const visibleInputHandlers = chatSettings !== undefined ? chatSettings.inputHandlers .filter((handler) => handler.display) .map((handler) => handler.handler) @@ -251,11 +251,40 @@ export function Chat({ chatID, chatTitle, loadChatByID, className = "" }: { {/* input */} { - updateInputHandlerInLocalStorage(chatID, inputHandlers?.length ?? 0, handler) - }} className="w-4/5" + if (chatSettings === undefined) return + updateInputHandlerInLocalStorage(chatID, visibleInputHandlers?.length ?? 0, handler) + const originalInputHandlers = chatSettings.inputHandlers + dispatch(setCurrentChatSettings({ + chatID, + chatSettings: { + ...chatSettings, + inputHandlers: [ + ...originalInputHandlers.map((handler) => ({ handler: handler.handler.serialize(), display: handler.display })), + { handler: handler.serialize(), display: true } + ], + } + })) + }} + updateInputHandler={(index, handler) => { + if (chatSettings === undefined) return + updateInputHandlerInLocalStorage(chatID, index, handler) + const originalInputHandlers = chatSettings.inputHandlers + dispatch(setCurrentChatSettings({ + chatID, + chatSettings: { + ...chatSettings, + inputHandlers: originalInputHandlers.map((h, i) => + i === index + ? { handler: handler.serialize(), display: h.display } + : { handler: h.handler.serialize(), display: h.display } + ), + } + })) + }} + className="w-4/5" chatID={chatID} msgListSwitchSignal={msgListSwitchSignal} - inputHandlers={inputHandlers} + inputHandlers={visibleInputHandlers} addMesssage={addMesssage} messageList={currentMessageList} // Temporarily forbid nested multi-level discussions, the component has already supported, // it's just the AI might be unable to handle too many levels diff --git a/src/app/chat/components/input.tsx b/src/app/chat/components/input.tsx index 6cf66a1..1de3bc2 100644 --- a/src/app/chat/components/input.tsx +++ b/src/app/chat/components/input.tsx @@ -1,31 +1,31 @@ "use client"; -import { useState, useRef, useEffect, useCallback, useContext } from "react"; -import { FaBackspace, FaMicrophone } from "react-icons/fa"; -import { LuSettings, LuUserCog2 } from "react-icons/lu"; -import { Audio, Oval } from "react-loader-spinner"; -import { messageAddedCallbackOptions } from "./chat"; -import { ChatSettingsContext } from "./chat-settings"; -import { SpecialRoles, TextMessage } from "./message"; +import { I18nText } from "@/app/i18n/i18n"; +import { TmpFilledButton, TmpTransparentButton } from "@/app/ui-utils/components/button"; +import { SemiTransparentOverlay } from "@/app/ui-utils/components/overlay"; import { IconCircleWrapper } from "@/app/ui-utils/components/wrapper"; import { diffChars } from "diff"; +import { IMediaRecorder } from "extendable-media-recorder"; +import { useCallback, useContext, useEffect, useRef, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { FaBackspace, FaMicrophone } from "react-icons/fa"; +import { FiPlus } from "react-icons/fi"; import { LiaComments } from "react-icons/lia"; +import { LuSettings, LuUserCog2 } from "react-icons/lu"; import { PiKeyReturnBold } from "react-icons/pi"; -import { isOpenAILikeMessage, Message, OpenAILikeMessage } from "../lib/message"; +import { TbPencil } from "react-icons/tb"; +import { Audio, Oval } from "react-loader-spinner"; import Switch from "react-switch"; -import { IMediaRecorder } from "extendable-media-recorder"; import { Tooltip } from "react-tooltip"; -import { FiPlus } from "react-icons/fi"; -import { updateInputHandlerInLocalStorage, updateInputSettingsPayloadInLocalStorage } from "../lib/chat"; +import { updateInputSettingsPayloadInLocalStorage } from "../lib/chat"; +import { isOpenAILikeMessage, Message, OpenAILikeMessage } from "../lib/message"; +import { messageAddedCallbackOptions } from "./chat"; +import { ChatSettingsContext } from "./chat-settings"; import { - InputHandler, - CustomInputHandlerCreator + CustomInputHandlerCreator, + InputHandler } from "./input-handlers"; -import { SemiTransparentOverlay } from "@/app/ui-utils/components/overlay"; -import { useTranslation } from "react-i18next"; -import { I18nText } from "@/app/i18n/i18n"; -import { TmpFilledButton, TmpTransparentButton } from "@/app/ui-utils/components/button"; -import { TbPencil } from "react-icons/tb"; +import { SpecialRoles, TextMessage } from "./message"; import { TutorialDiffView, TutorialInput } from "./tutorial-input"; export async function reviseMessage( @@ -220,7 +220,11 @@ export type MsgListSwitchSignal = | { type: 'backFromFollowUpDiscussion', message: Message, handledMsg: Message, handlerInstruction: string, key: number } export function MessageInput({ - chatID, messageList, inputHandlers, addMesssage, addInputHandler: pAddInputHandler, msgListSwitchSignal, allowFollowUpDiscussion, startFollowUpDiscussion, className = "" + chatID, messageList, inputHandlers, msgListSwitchSignal, allowFollowUpDiscussion, className = "", + addMesssage, + addInputHandler: pAddInputHandler, + startFollowUpDiscussion, + updateInputHandler, }: { chatID: string messageList: Message[]; @@ -228,9 +232,9 @@ export function MessageInput({ allowFollowUpDiscussion: boolean; // callback functions addMesssage: (message: Message, callbackOpts?: messageAddedCallbackOptions) => void; - // TODO tech-debt: 新增 inputHandler 应该通过更新 chatSettings 来实现 addInputHandler: (handler: InputHandler) => void; startFollowUpDiscussion: (userInstruction: string, messageToRevise: Message, revisedText: Message) => void; + updateInputHandler: (index: number, handler: InputHandler) => void; // signals msgListSwitchSignal: MsgListSwitchSignal, className?: string; @@ -321,17 +325,17 @@ export function MessageInput({ } pAddInputHandler(handler); setCompState(compState.previousState); - inputHandlers.push(handler); // TODO need to find out why this would work... } - function updateInputHandler(handler: InputHandler) { + function _updateInputHandler(handler: InputHandler) { if (compState.type !== 'settingsPanel') { return; } - updateInputHandlerInLocalStorage(chatID, compState.handlerIndex, handler); - inputHandlers[compState.handlerIndex] = handler; + updateInputHandler(compState.handlerIndex, handler); setCompState(compState.previousState); } - function updateInputSettingsPayload(payload: object) { + // update the input component settings + // TODO tech-debt: maybe should just achieve this by directly updating the chat settings + function updateInputCompSettings(payload: object) { updateInputSettingsPayloadInLocalStorage(chatID, payload); } @@ -401,7 +405,7 @@ export function MessageInput({ {compState.type === 'settingsPanel' && compState.handlerIndex === index && configurable && <> - + } @@ -444,7 +448,7 @@ export function MessageInput({ addMessage={addMesssage} updateMessage={updateMessage} revisionMessage={isNormal ? [compState.message, compState.fromRevision] : undefined} rejectionSignal={rejectionSignal} />} {inputComponentType === 'tutorialInput' && } ; } diff --git a/src/app/settings/components/settings.tsx b/src/app/settings/components/settings.tsx index 6d08df7..4588664 100644 --- a/src/app/settings/components/settings.tsx +++ b/src/app/settings/components/settings.tsx @@ -443,9 +443,7 @@ function CommonChatSettings({ chatSettings, updateChatSettings, className = "" } if (handler.handler.deletable) { updateChatSettings({ ...chatSettings, - inputHandlers: chatSettings.inputHandlers.filter((h) => - h.handler.implType !== handler.handler.implType - ) + inputHandlers: chatSettings.inputHandlers.filter((_, i) => i !== index) }) } }}