From 36bfbe490b2d8af697326905ae2cdf729973d896 Mon Sep 17 00:00:00 2001 From: cyrbuzz Date: Tue, 13 Aug 2024 17:12:06 +0800 Subject: [PATCH 1/2] feat: chat ui --- components/app/header/Header.less | 16 ++++++------ components/app/header/Header.tsx | 2 +- components/common/chatUi/chatUi.tsx | 8 +++--- components/common/dropdown/Dropdown.less | 6 ++--- components/styles.css | 32 ++++++++++-------------- 5 files changed, 30 insertions(+), 34 deletions(-) diff --git a/components/app/header/Header.less b/components/app/header/Header.less index f254bc1..6b3e0aa 100644 --- a/components/app/header/Header.less +++ b/components/app/header/Header.less @@ -37,7 +37,7 @@ &__dropdown-item.@{left}__dropdown-item.@{left}__dropdown-item { &:hover { - background-color: rgba(67, 136, 221, 0.40); + background-color: var(--sq-primary-card-hover); } } } @@ -46,10 +46,10 @@ color: #fff; &:hover { - color: var(--sq-blue600); + color: var(--sq-primary-link-hover); } &--active { - color: var(--sq-blue600); + color: var(--sq-primary-link-hover); } } } @@ -71,7 +71,7 @@ border-radius: 0; &--dark { &:hover { - background-color: rgba(67, 136, 221, 0.40); + background-color: var(--sq-primary-card-hover); } } } @@ -116,11 +116,11 @@ &:hover { text-decoration: underline; - color: var(--sq-blue600); + color: var(--sq-primary-link-hover); } &--active { - color: var(--sq-blue600); + color: var(--sq-primary-link-hover); } } @@ -262,11 +262,11 @@ color: #fff; &:hover { text-decoration: underline; - color: var(--sq-blue600); + color: var(--sq-primary); } &--active { - color: var(--sq-blue600); + color: var(--sq-primary); } } } diff --git a/components/app/header/Header.tsx b/components/app/header/Header.tsx index a97290c..75fd6a8 100644 --- a/components/app/header/Header.tsx +++ b/components/app/header/Header.tsx @@ -73,7 +73,7 @@ const LeftHeader = ({ leftElement, dropdownLinks, isMobile }: LeftHeaderProps) =
} + LeftLabelIcon={SubQuery Apps} menuitem={dropdownLinks.links.map((label, key) => ({ key, label: , diff --git a/components/common/chatUi/chatUi.tsx b/components/common/chatUi/chatUi.tsx index df2577b..d0d9be5 100644 --- a/components/common/chatUi/chatUi.tsx +++ b/components/common/chatUi/chatUi.tsx @@ -326,10 +326,9 @@ export const ChatUi: FC = ({ chatUrl, prompt, className, placeholde const parts = chunkValue.split('\n\n'); for (const part of parts) { - const partWithHandle = part.startsWith('data: ') ? part.slice(6, part.length).trim() : part; - if (!part.startsWith('data: ')) { + if (invalidJson) { try { - invalidJson += partWithHandle; + invalidJson += part; const parsed: { choices: { delta: { content: string } }[] } = JSON.parse(invalidJson); robotAnswer.content += parsed?.choices?.[0]?.delta?.content; @@ -341,6 +340,8 @@ export const ChatUi: FC = ({ chatUrl, prompt, className, placeholde continue; } + const partWithHandle = part.startsWith('data: ') ? part.slice(6, part.length).trim() : part; + if (partWithHandle) { try { const parsed: { choices: { delta: { content: string } }[] } = JSON.parse(partWithHandle); @@ -361,6 +362,7 @@ export const ChatUi: FC = ({ chatUrl, prompt, className, placeholde await pushNewMsgToChat(newChat, robotAnswer, curChat, curChats); } catch (e) { + console.warn('Reach this code', invalidJson); // to reach this code, it means the response is not valid or the code have something wrong. } } diff --git a/components/common/dropdown/Dropdown.less b/components/common/dropdown/Dropdown.less index 8cb54a9..2267214 100644 --- a/components/common/dropdown/Dropdown.less +++ b/components/common/dropdown/Dropdown.less @@ -12,13 +12,13 @@ } &--active { - color: var(--sq-blue600); + color: var(--sq-primary); .subql-typography { - color: var(--sq-blue600); + color: var(--sq-primary); } .subql-dropdown__icon { - color: var(--sq-blue600); + color: var(--sq-primary); } } diff --git a/components/styles.css b/components/styles.css index 19d1524..68c19ce 100644 --- a/components/styles.css +++ b/components/styles.css @@ -1,29 +1,22 @@ @font-face { - font-family: 'Inter'; - src: local('Inter-Regular'), url('https://static.subquery.network/design/fonts/Inter-Regular.ttf') format('truetype'); + font-family: 'PPNeue-Bold'; + src: local('PPNeueMontreal-Bold'), + url('https://static.subquery.network/design/fonts/PPNeueMontreal-Bold.ttf') format('truetype'); font-display: swap; } @font-face { - font-family: 'Inter-SemiBold'; - src: local('Inter-SemiBold'), - url('https://static.subquery.network/design/fonts/Inter-SemiBold.ttf') format('truetype'); - font-display: swap; -} - -@font-face { - font-family: 'Inter-Bold'; - src: local('Inter-Bold'), url('https://static.subquery.network/design/fonts/Inter-Bold.ttf') format('truetype'); - font-display: swap; -} - -@font-face { - font-family: 'Futura'; - src: url('https://static.subquery.network/design/fonts/Futura-Medium.ttf') format('truetype'); + font-family: 'PPNeue'; + src: local('PPNeue-Regular'), + url('https://static.subquery.network/design/fonts/PPNeueMontreal-Regular.ttf') format('truetype'); font-display: swap; } :root { + --sq-primary: #e968dd; + --sq-primary-card-hover: #e968dd33; + --sq-primary-link-hover: #d25ec7; + --sq-primary-pink: #ff4581; --sq-primary-blue: #1677ff; --sq-gradient: linear-gradient(96.26deg, var(--sq-primary-blue) 13.8%, var(--sq-primary-pink) 82.83%); @@ -50,6 +43,7 @@ --sq-pink400: #ff6a9a; --sq-pink500: #ff588e; --sq-pink600: #ff4581; + --sq-pink700: #e968dd; /* Gray */ --sq-gray100: #f9fafb; @@ -98,8 +92,8 @@ --sq-border-radius: 8px; /* Family */ - --sq-font-family: 'Inter', Arial, sans-serif; - --sq-font-family-header: 'Futura', Arial, sans-serif; + --sq-font-family: 'PPNeue', Arial, sans-serif; + --sq-font-family-header: 'PPNeue', Arial, sans-serif; } body { From 5089c7590daaa63eea95937059fd3a1a40d4df0d Mon Sep 17 00:00:00 2001 From: cyrbuzz Date: Wed, 14 Aug 2024 16:49:40 +0800 Subject: [PATCH 2/2] feat: chat ui --- components/common/chatUi/chatUi.less | 19 +++++++++++++------ components/common/chatUi/chatUi.tsx | 26 ++++++++++++++++++-------- components/styles.css | 4 +++- 3 files changed, 34 insertions(+), 15 deletions(-) diff --git a/components/common/chatUi/chatUi.less b/components/common/chatUi/chatUi.less index 900cf91..4ee39b4 100644 --- a/components/common/chatUi/chatUi.less +++ b/components/common/chatUi/chatUi.less @@ -178,6 +178,12 @@ align-items: center; gap: 12px; color: rgb(209, 213, 219); + + .subql-markdown-preview { + p { + margin-top: 0; + } + } } &__assistant { @@ -185,12 +191,13 @@ position: relative; &--lastOne&--loading { .subql-chat-conversation-message__item-span { - &::after { - content: "▍"; - align-self: flex-end; - margin-left: 8px; - animation: pulseCursor 1s cubic-bezier(0.4, 0, 0.6, 1) infinite; - + .subql-markdown-preview { + &::after { + content: "▍"; + align-self: flex-end; + margin-left: 8px; + animation: pulseCursor 1s cubic-bezier(0.4, 0, 0.6, 1) infinite; + } } } } diff --git a/components/common/chatUi/chatUi.tsx b/components/common/chatUi/chatUi.tsx index d0d9be5..5359717 100644 --- a/components/common/chatUi/chatUi.tsx +++ b/components/common/chatUi/chatUi.tsx @@ -16,6 +16,7 @@ import { Typography } from '../typography'; import { v4 as uuidv4 } from 'uuid'; import { cloneDeep, isString } from 'lodash-es'; import Address from '../address'; +import Markdown from '../markdown/Markdown'; const indexerName: { [key in string]: string } = { '0xd0af1919af890cfdd8d12be5cf1b1421224fc29a': 'Mainnet Operator', @@ -31,6 +32,7 @@ export interface ChatUiProps { placeholder?: React.ReactNode; width?: number; height?: number; + model?: string; } export type AiMessageType = 'text' | 'image_url'; @@ -127,7 +129,9 @@ export const ConversationMessage = forwardRef< bem('item'), bem(message.role, { [answerStatus]: - index === property.messages.length - 1 && message.role === 'assistant' ? true : undefined, + index === property.messages.length - 1 && message.role === 'assistant' && !message?.content?.length + ? true + : undefined, lastOne: index === property.messages.length - 1 && message.role === 'assistant' ? true : undefined, }), )} @@ -142,7 +146,9 @@ export const ConversationMessage = forwardRef< > )} {/* TODO: support array */} - {isString(message.content) ? message.content : ''} +
+ {isString(message.content) ? {message.content} : ''} +
); })} @@ -155,7 +161,8 @@ ConversationMessage.displayName = 'ConversationMessage'; // maybe later support custom workspace name const workspaceName = 'subql-chat-workspace'; -export const chatWithStream = async (url: string, body: { messages: Message[] }) => { +export const chatWithStream = async (url: string, body: { messages: Message[]; model?: string }) => { + const { model = 'gemma2' } = body; const res = await fetch(url, { headers: { accept: 'text/event-stream', @@ -163,7 +170,7 @@ export const chatWithStream = async (url: string, body: { messages: Message[] }) }, method: 'POST', body: JSON.stringify({ - model: 'gemma2', + model, messages: body.messages, stream: true, }), @@ -171,7 +178,7 @@ export const chatWithStream = async (url: string, body: { messages: Message[] }) return res; }; -export const ChatUi: FC = ({ chatUrl, prompt, className, placeholder, width, height }) => { +export const ChatUi: FC = ({ chatUrl, prompt, className, placeholder, width, height, model }) => { const bem = useBem('subql-chat'); const [chats, setChats] = React.useState([]); const [currentChat, setCurrentChat] = useState(); @@ -206,7 +213,7 @@ export const ChatUi: FC = ({ chatUrl, prompt, className, placeholde name: 'New Conversation', chatUrl, messages: [], - prompt: prompt || '', + prompt: '', }; oldWorkspace.unshift(chat); @@ -284,6 +291,7 @@ export const ChatUi: FC = ({ chatUrl, prompt, className, placeholde name: curChat.messages.length ? curChat.name : currentInput.slice(0, 40), }; newChat.chatUrl = newChat.messages.length - 1 > 0 ? newChat.chatUrl : chatUrl; + newChat.prompt = newChat.prompt || prompt || ''; const newChats = cloneDeep(curChats).map((chat) => { if (chat.id === curChat.id) { @@ -305,10 +313,12 @@ export const ChatUi: FC = ({ chatUrl, prompt, className, placeholde }; await pushNewMsgToChat(newChat, robotAnswer, curChat, curChats); - // set user's message first, then get the response const res = await chatWithStream(newChat.chatUrl, { - messages: prompt ? [{ role: 'system' as const, content: prompt }, ...newChat.messages] : newChat.messages, + messages: newChat.prompt + ? [{ role: 'system' as const, content: newChat.prompt }, ...newChat.messages] + : newChat.messages, + model, }); if (res.status === 200 && res.body) { diff --git a/components/styles.css b/components/styles.css index 68c19ce..c2f5262 100644 --- a/components/styles.css +++ b/components/styles.css @@ -1,8 +1,9 @@ @font-face { - font-family: 'PPNeue-Bold'; + font-family: 'PPNeue'; src: local('PPNeueMontreal-Bold'), url('https://static.subquery.network/design/fonts/PPNeueMontreal-Bold.ttf') format('truetype'); font-display: swap; + font-weight: bold; } @font-face { @@ -15,6 +16,7 @@ :root { --sq-primary: #e968dd; --sq-primary-card-hover: #e968dd33; + --sq-primary-card-deephover: #e968dd66; --sq-primary-link-hover: #d25ec7; --sq-primary-pink: #ff4581;