diff --git a/components/common/chatUi/chatUi.less b/components/common/chatUi/chatUi.less index 0f66b3d..9c26505 100644 --- a/components/common/chatUi/chatUi.less +++ b/components/common/chatUi/chatUi.less @@ -378,6 +378,18 @@ } } } + + &-tooltip-select-item { + display: flex; + gap: 8px; + transition: background .15s cubic-bezier(.4,0,.2,1); + border-radius: 6px; + padding: 4px 6px; + &:hover { + cursor: pointer; + background: rgba(255, 255, 255, 0.055); + } + } } @keyframes pulseCursor { diff --git a/components/common/chatUi/chatUi.tsx b/components/common/chatUi/chatUi.tsx index 23af098..e56daa3 100644 --- a/components/common/chatUi/chatUi.tsx +++ b/components/common/chatUi/chatUi.tsx @@ -12,7 +12,7 @@ import { AiFillApi } from 'react-icons/ai'; import { SlArrowDown } from 'react-icons/sl'; import { IoClose } from 'react-icons/io5'; import { BsFillArrowUpCircleFill } from 'react-icons/bs'; -import { Button, Input, InputRef, message, Popover } from 'antd'; +import { Button, Input, InputRef, message, Popover, Tooltip, TooltipProps } from 'antd'; import { Typography } from '../typography'; import { v4 as uuidv4 } from 'uuid'; import { cloneDeep, isString } from 'lodash-es'; @@ -62,6 +62,10 @@ export enum ChatBotAnswerStatus { Error = 'error', } +export type ChatBoxRef = { + makeNewMsg: (msg: string) => void; +}; + export const ConversationItem: FC = ({ property, active, onSelect, onEdit, onRemove }) => { const bem = useBem('subql-chat-conversation-item'); return ( @@ -529,27 +533,39 @@ export const ChatUi: FC = ({ chatUrl, prompt, className, placeholde ); }; -const ChatBoxIcon: FC<{ className?: string }> = ({ className }) => ( - +export const ChatBoxIcon: FC<{ className?: string; width?: number; height?: number; fill?: string }> = ({ + className, + width = 26, + height = 28, + fill = 'white', +}) => ( + @@ -557,7 +573,7 @@ const ChatBoxIcon: FC<{ className?: string }> = ({ className }) => ( ); // maybe split to other file. -export const ChatBox: FC = (props) => { +export const ChatBox = forwardRef((props, ref) => { const { chatUrl, prompt = '', model, onSendMessage, onChatboxOpen } = props; const [popoverOpen, setPopoverOpen] = useState(false); const bem = useBem('subql-chatbox'); @@ -577,7 +593,7 @@ export const ChatBox: FC = (props) => { chatUrl, prompt, }); - const [answerStatus, setAnswerStatus] = useState(ChatBotAnswerStatus.Loading); + const [answerStatus, setAnswerStatus] = useState(ChatBotAnswerStatus.Empty); const pushNewMsgToChat = async ( newChat: ConversationProperty, @@ -593,23 +609,26 @@ export const ChatBox: FC = (props) => { }); }; - const sendMessage = async () => { + const sendMessage = async (msg?: string) => { const curChat = currentChat; - if (!currentInput) { + + const newMsg = currentInput || msg; + + if (!newMsg || answerStatus === ChatBotAnswerStatus.Loading) { return; } - setAnswerStatus(ChatBotAnswerStatus.Loading); try { + setAnswerStatus(ChatBotAnswerStatus.Loading); const newMessage = { role: 'user' as const, - content: currentInput, + content: newMsg, }; const newChat = { ...curChat, messages: [...curChat.messages, newMessage].filter((i) => i.content), - name: curChat.messages.length ? curChat.name : currentInput.slice(0, 40), + name: curChat.messages.length ? curChat.name : newMsg.slice(0, 40), }; newChat.chatUrl = newChat.messages.length - 1 > 0 ? newChat.chatUrl : chatUrl; newChat.prompt = newChat.prompt || prompt || ''; @@ -655,7 +674,7 @@ export const ChatBox: FC = (props) => { robotAnswer.content += parsed?.choices?.[0]?.delta?.content; await pushNewMsgToChat(newChat, robotAnswer, curChat); - console.warn(messageRef); + messageRef.current?.scrollToBottom(true); invalidJson = ''; } catch (e) { @@ -706,6 +725,13 @@ export const ChatBox: FC = (props) => { } }; + useImperativeHandle(ref, () => ({ + makeNewMsg: (msg: string) => { + setPopoverOpen(true); + sendMessage(msg); + }, + })); + return ( = (props) => { ); +}); + +ChatBox.displayName = 'ChatBox'; + +export const ChatBoxTooltipSelectItem: FC<{ + label: React.ReactNode; + value: string; + onSelect: (val: string) => void; +}> = ({ label, value, onSelect }) => { + const bem = useBem('subql-chatbox-tooltip-select-item'); + return ( +
{ + onSelect(value); + }} + > + + + {label} + +
+ ); +}; + +export const ChatBoxTooltip: FC<{ + children: React.ReactNode; + options: { label: React.ReactNode; value: string }[]; + tooltipProps?: TooltipProps; + chatBoxInstance?: ChatBoxRef; +}> = (props) => { + const { children, tooltipProps, options, chatBoxInstance } = props; + return ( + + {options.map((option) => { + return ( + { + chatBoxInstance?.makeNewMsg(val); + }} + /> + ); + })} + + } + > + {children} + + ); }; diff --git a/components/common/index.tsx b/components/common/index.tsx index 538164d..c93f1f1 100644 --- a/components/common/index.tsx +++ b/components/common/index.tsx @@ -35,7 +35,7 @@ export { default as SubqlCheckbox } from './checkbox'; export { SubqlCard } from './subqlCard'; export { SubqlTabs } from './subqlTabs'; export * from './notification'; -export { ChatUi, ChatBox } from './chatUi'; +export { ChatUi, ChatBox, ChatBoxTooltip, ChatBoxTooltipSelectItem, ChatBoxIcon, type ChatBoxRef } from './chatUi'; export type { IGraphiQL } from './GraphiQL'; export { default as SubqlProvider } from './provider'; export { default as Markdown } from './markdown/Markdown'; diff --git a/package.json b/package.json index ffac0ea..d5cfbdd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@subql/components", - "version": "3.0.1-4", + "version": "3.0.1-5", "description": "React UI components for SubQuery front ends", "repository": "git@github.com:subquery/subql-copmonents.git", "author": "Subquery",