Docs: Bot: Update

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/11079
GitOrigin-RevId: 48da2933e84289a5afca007ed054f5070bc11eaf
This commit is contained in:
Sean Park-Ross 2024-11-14 17:09:12 +00:00 committed by hasura-bot
parent ee3c1a5a9a
commit 5c7e20f83b
7 changed files with 413 additions and 195 deletions

View File

@ -5,6 +5,12 @@ const path = require('path');
const lightCodeTheme = require('prism-react-renderer/themes/vsLight');
const darkCodeTheme = require('prism-react-renderer/themes/dracula');
const BOT_ROUTES = {
development: 'ws://localhost:8000/bot/query',
production: 'wss://website-api.hasura.io/docs-services/docs-server/bot/query',
staging: 'wss://website-api.stage.hasura.io/docs-services/docs-server/bot/query',
};
/** @type {import('@docusaurus/types').Config} */
const config = {
title: 'Hasura GraphQL Docs',
@ -20,15 +26,19 @@ const config = {
staticDirectories: ['static', 'public'],
customFields: {
docsBotEndpointURL: (() => {
switch (process.env.release_mode) {
case 'development':
return 'ws://localhost:8000/hasura-docs-ai';
case 'production':
return 'wss://website-api.hasura.io/chat-bot/hasura-docs-ai';
case 'staging':
return 'wss://website-api.stage.hasura.io/chat-bot/hasura-docs-ai';
default:
return 'ws://localhost:8000/hasura-docs-ai'; // default to development if no match
if (process.env.CF_PAGES === '1') {
return BOT_ROUTES.staging; // if we're on CF pages, use the staging environment
} else {
switch (process.env.release_mode) {
case 'development':
return BOT_ROUTES.development; // if we're on the development environment, use the local server
case 'production':
return BOT_ROUTES.production;
case 'staging':
return BOT_ROUTES.production; // if we're in full staging on GCP and not cloudflare pages, use the production environment
default:
return BOT_ROUTES.development; // default to development if no match (env var not generally set on local dev)
}
}
})(),
hasuraVersion: 2,

View File

@ -5,25 +5,18 @@ import './styles.css';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import { CloseIcon, RespondingIconGray, SparklesIcon } from '@site/src/components/AiChatBot/icons';
import { useLocalStorage } from 'usehooks-ts';
import profilePic from '@site/static/img/docs-bot-profile-pic.webp';
// @ts-ignore
import profilePic from './docs-bot-profile-pic.webp';
import { v4 as uuidv4 } from 'uuid';
import ThumbsDown from './thumbs-down.svg';
import { BadBotResponse, BotWebsocketEvent, WebsocketPayload } from '@site/src/components/AiChatBot/types';
interface Message {
userMessage: string;
botResponse: string;
id?: string;
}
interface Query {
previousMessages: Message[];
currentUserInput: string;
}
// Websocket Event data types (stringified)
// { type: "loading", message: "Processing your request..." }
// { type: "responsePart", message: "...part of response..." }
// { type: "error", message: "error description" }
// { type: "endOfStream", message: "End of stream..." }
const initialMessages: Message[] = [
{
userMessage: '',
@ -51,10 +44,13 @@ export function AiChatBot({ style }) {
// Manage the text input
const [input, setInput] = useState<string>('');
// Manage the message thread ID
const [messageThreadId, setMessageThreadId] = useLocalStorage<String>(
const [messageThreadId, setMessageThreadId] = useLocalStorage<string>(
`hasuraV${customFields.hasuraVersion}ThreadId`,
uuidv4()
);
// Manage the responseQuality
const [badResponse, setBadResponse] = useState<BadBotResponse | null>(null);
// Manage the historical messages
const [messages, setMessages] = useLocalStorage<Message[]>(
`hasuraV${customFields.hasuraVersion}BotMessages`,
@ -83,7 +79,8 @@ export function AiChatBot({ style }) {
const sanitizeInput = (input: string): string => {
const sanitized = DOMPurify.sanitize(input.trim());
return sanitized.replace(/&/g, '&amp;')
return sanitized
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
@ -104,7 +101,7 @@ export function AiChatBot({ style }) {
behavior: 'smooth',
});
}
}, [currentMessage.botResponse]);
}, [currentMessage.botResponse, badResponse]);
// Detect if user scrolls up and disable auto-scrolling
const handleScroll = e => {
@ -125,8 +122,6 @@ export function AiChatBot({ style }) {
const queryDevToken = process.env.NODE_ENV === 'development' && DEV_TOKEN ? `&devToken=${DEV_TOKEN}` : '';
console.log('process.env.NODE_ENV', process.env.NODE_ENV);
const connectWebSocket = () => {
websocket = new WebSocket(
encodeURI(`${docsBotEndpointURL}?version=${hasuraVersion}&userId=${storedUserID}${queryDevToken}`)
@ -138,11 +133,13 @@ export function AiChatBot({ style }) {
clearTimeout(reconnectInterval);
};
// Handle incoming messages
websocket.onmessage = event => {
let response = { type: '', message: '' };
let response: BotWebsocketEvent;
try {
response = JSON.parse(event.data) as { type: string; message: string };
response = JSON.parse(event.data) as { type: BotWebsocketEvent['type']; message: string };
// TODO check if this is the correct type
} catch (e) {
console.error('error parsing websocket message', e);
}
@ -213,14 +210,44 @@ export function AiChatBot({ style }) {
}
if (ws) {
const toSend = JSON.stringify({ previousMessages: messages, currentUserInput: input, messageThreadId });
setCurrentMessage({ userMessage: sanitizedInput, botResponse: '' });
const messageId = uuidv4();
setCurrentMessage({ userMessage: sanitizedInput, botResponse: '', id: messageId });
setInput('');
ws.send(toSend);
const chatPayload: WebsocketPayload = {
payloadType: 'chatMessage',
chatMessage: {
previousMessages: messages,
currentUserInput: sanitizedInput,
messageId,
messageThreadId,
},
responseQuality: null,
};
ws.send(JSON.stringify(chatPayload));
setIsResponding(true);
}
};
const handleBadBotResponse = async () => {
if (badResponse) {
console.log('responseQuality', badResponse);
// TODO SANITIZE AND VALIDATE RESPONSE QUALITY!!!
if (ws) {
const badBotResponsePayload: WebsocketPayload = {
payloadType: 'badBotResponse',
badBotResponse: {
messageId: badResponse.messageId,
responseText: badResponse.responseText,
},
chatMessage: null,
};
ws.send(JSON.stringify(badBotResponsePayload));
}
setBadResponse(null);
}
};
const renderMessage = (content: string) => {
return (
<Markdown
@ -229,7 +256,7 @@ export function AiChatBot({ style }) {
a: {
props: {
target: '_blank',
rel: 'noopener noreferrer'
rel: 'noopener noreferrer',
},
},
},
@ -246,22 +273,16 @@ export function AiChatBot({ style }) {
window.location.href.endsWith('/overview/');
return (
<div className={'chat-popup'}>
<div className={isOnOverviewOrIndex ? 'chat-popup-index-and-overviews' : 'chat-popup-other-pages'}>
{isOpen ? (
<></>
) : (
<button className="open-chat-button" onClick={() => setIsOpen(!isOpen)}>
{SparklesIcon} Hasura Docs AI Chat
</button>
)}
{isOpen && (
<div className={isOnOverviewOrIndex ? '' : 'absolute -bottom-11 w-full min-w-[500px] right-[10px]'}>
{isOpen && (
<button className="close-chat-button" onClick={() => setIsOpen(!isOpen)}>
{CloseIcon} Close Chat
</button>
)}
<div className="chat-popup">
<button className="open-chat-button" onClick={() => setIsOpen(true)}>
{SparklesIcon} Docs Assistant
</button>
{isOpen && (
<div className="modal-overlay" onClick={() => setIsOpen(false)}>
<div onClick={e => e.stopPropagation()}>
<button className="close-chat-button" onClick={() => setIsOpen(false)}>
{CloseIcon} Close Chat
</button>
<div className="chat-window">
<div className="info-bar">
<div className={'bot-name-pic-container'}>
@ -272,7 +293,7 @@ export function AiChatBot({ style }) {
className="clear-button"
onClick={() => {
setMessages(initialMessages);
setCurrentMessage({ userMessage: '', botResponse: '' });
setCurrentMessage({ userMessage: '', botResponse: '', id: '' });
setMessageThreadId(uuidv4());
}}
>
@ -284,16 +305,12 @@ export function AiChatBot({ style }) {
<div key={index}>
{msg.userMessage && (
<div className="user-message-container">
<div className="formatted-text message user-message">
{renderMessage(msg.userMessage)}
</div>
<div className="formatted-text message user-message">{renderMessage(msg.userMessage)}</div>
</div>
)}
{msg.botResponse && (
<div className="bot-message-container">
<div className="formatted-text message bot-message">
{renderMessage(msg.botResponse)}
</div>
<div className="formatted-text message bot-message">{renderMessage(msg.botResponse)}</div>
</div>
)}
</div>
@ -313,12 +330,54 @@ export function AiChatBot({ style }) {
</div>
)}
</div>
{messages.length > 3 && !isResponding && (
<form
id="bad-response-form"
className="bad-response-form"
onSubmit={e => {
e.preventDefault();
handleBadBotResponse();
}}
>
<div className={'flex'}>
<button
className="thumbs-down-button"
type="button"
onClick={() => {
setBadResponse({
responseText: null,
messageId: messages.at(-1).id ?? '',
});
}}
>
<ThumbsDown className={'mb-4'} />
</button>
</div>
<div>
{badResponse !== null && (
<div className="bad-response-container">
<textarea
rows={4}
onChange={e =>
setBadResponse(prevState => ({ ...prevState, responseText: e.target.value }))
}
placeholder={'Sorry about that. Please tell us how we can improve.'}
></textarea>
<button className="feedback-submit-button" type={'submit'}>
Submit Feedback
</button>
</div>
)}
</div>
</form>
)}
<div className="responding-div">{isResponding ? RespondingIconGray : null}</div>
</div>
</div>
{/* Handles scrolling to the end */}
{/*<div ref={messagesEndRef} />*/}
<form
id={'chat-form'}
className="input-container"
onSubmit={e => {
e.preventDefault();
@ -338,8 +397,8 @@ export function AiChatBot({ style }) {
</form>
</div>
</div>
)}
</div>
</div>
)}
</div>
);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -1,13 +1,54 @@
import React from "react";
import React from 'react';
export const SparklesIcon = <svg className="sparkles-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 30" x="0px" y="0px"><path d="m15.6968,12.4905l-2.8295,1.4148c-.8491.4245-1.5375,1.113-1.962,1.962l-1.4148,2.8295c-.2021.4042-.7789.4042-.981,0l-1.4148-2.8295c-.4245-.8491-1.113-1.5375-1.962-1.962l-2.8295-1.4148c-.4042-.2021-.4042-.7789,0-.981l2.8295-1.4148c.8491-.4246,1.5375-1.113,1.962-1.9621l1.4148-2.8295c.2021-.4042.7789-.4042.981,0l1.4148,2.8295c.4245.8491,1.113,1.5375,1.962,1.9621l2.8295,1.4148c.4042.2021.4042.7789,0,.981Zm6.1732,6.2993l-1.2127-.6063c-.3639-.182-.6589-.477-.8409-.8409l-.6063-1.2126c-.0866-.1732-.3338-.1732-.4204,0l-.6063,1.2126c-.1819.3639-.477.6589-.8409.8409l-1.2127.6063c-.1732.0866-.1732.3338,0,.4204l1.2127.6063c.3639.1819.6589.477.8409.8409l.6063,1.2126c.0866.1732.3338.1732.4204,0l.6063-1.2126c.1819-.3639.477-.6589.8409-.8409l1.2127-.6063c.1732-.0866.1732-.3338,0-.4204Zm0-14l-1.2127-.6063c-.3639-.182-.6589-.477-.8409-.8409l-.6063-1.2126c-.0866-.1732-.3338-.1732-.4204,0l-.6063,1.2126c-.1819.3639-.477.6589-.8409.8409l-1.2127.6063c-.1732.0866-.1732.3338,0,.4204l1.2127.6063c.3639.1819.6589.477.8409.8409l.6063,1.2126c.0866.1732.3338.1732.4204,0l.6063-1.2126c.1819-.3639.477-.6589.8409-.8409l1.2127-.6063c.1732-.0866.1732-.3338,0-.4204Z"/><text x="0" y="39" fill="#111111" font-size="5px" font-weight="bold" font-family="'Helvetica Neue', Helvetica, Arial-Unicode, Arial, Sans-serif">Created by Royyan Wijaya</text><text x="0" y="44" fill="#000000" font-size="5px" font-weight="bold" font-family="'Helvetica Neue', Helvetica, Arial-Unicode, Arial, Sans-serif">from the Noun Project</text></svg>
export const SparklesIcon = (
<svg className="sparkles-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 30" x="0px" y="0px">
<path d="m15.6968,12.4905l-2.8295,1.4148c-.8491.4245-1.5375,1.113-1.962,1.962l-1.4148,2.8295c-.2021.4042-.7789.4042-.981,0l-1.4148-2.8295c-.4245-.8491-1.113-1.5375-1.962-1.962l-2.8295-1.4148c-.4042-.2021-.4042-.7789,0-.981l2.8295-1.4148c.8491-.4246,1.5375-1.113,1.962-1.9621l1.4148-2.8295c.2021-.4042.7789-.4042.981,0l1.4148,2.8295c.4245.8491,1.113,1.5375,1.962,1.9621l2.8295,1.4148c.4042.2021.4042.7789,0,.981Zm6.1732,6.2993l-1.2127-.6063c-.3639-.182-.6589-.477-.8409-.8409l-.6063-1.2126c-.0866-.1732-.3338-.1732-.4204,0l-.6063,1.2126c-.1819.3639-.477.6589-.8409.8409l-1.2127.6063c-.1732.0866-.1732.3338,0,.4204l1.2127.6063c.3639.1819.6589.477.8409.8409l.6063,1.2126c.0866.1732.3338.1732.4204,0l.6063-1.2126c.1819-.3639.477-.6589.8409-.8409l1.2127-.6063c.1732-.0866.1732-.3338,0-.4204Zm0-14l-1.2127-.6063c-.3639-.182-.6589-.477-.8409-.8409l-.6063-1.2126c-.0866-.1732-.3338-.1732-.4204,0l-.6063,1.2126c-.1819.3639-.477.6589-.8409.8409l-1.2127.6063c-.1732.0866-.1732.3338,0,.4204l1.2127.6063c.3639.1819.6589.477.8409.8409l.6063,1.2126c.0866.1732.3338.1732.4204,0l.6063-1.2126c.1819-.3639.477-.6589.8409-.8409l1.2127-.6063c.1732-.0866.1732-.3338,0-.4204Z" />
<text
x="0"
y="39"
fill="#111111"
font-size="5px"
font-weight="bold"
font-family="'Helvetica Neue', Helvetica, Arial-Unicode, Arial, Sans-serif"
>
Created by Royyan Wijaya
</text>
<text
x="0"
y="44"
fill="#000000"
font-size="5px"
font-weight="bold"
font-family="'Helvetica Neue', Helvetica, Arial-Unicode, Arial, Sans-serif"
>
from the Noun Project
</text>
</svg>
);
export const CloseIcon = <svg className="close-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 30" fill="none" x="0px" y="0px"><path fill-rule="evenodd" clip-rule="evenodd" d="M5.69292 18.3071C5.30239 17.9166 5.30239 17.2834 5.69292 16.8929L16.8929 5.69292C17.2834 5.30239 17.9166 5.30239 18.3071 5.69292C18.6977 6.08344 18.6977 6.71661 18.3071 7.10713L7.10713 18.3071C6.71661 18.6977 6.08344 18.6977 5.69292 18.3071Z" fill="white"/><path fill-rule="evenodd" clip-rule="evenodd" d="M5.69292 5.69292C6.08344 5.30239 6.71661 5.30239 7.10713 5.69292L18.3071 16.8929C18.6977 17.2834 18.6977 17.9166 18.3071 18.3071C17.9166 18.6977 17.2834 18.6977 16.8929 18.3071L5.69292 7.10713C5.30239 6.71661 5.30239 6.08344 5.69292 5.69292Z" fill="white"/></svg>
export const CloseIcon = (
<svg className="close-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 30" fill="none" x="0px" y="0px">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M5.69292 18.3071C5.30239 17.9166 5.30239 17.2834 5.69292 16.8929L16.8929 5.69292C17.2834 5.30239 17.9166 5.30239 18.3071 5.69292C18.6977 6.08344 18.6977 6.71661 18.3071 7.10713L7.10713 18.3071C6.71661 18.6977 6.08344 18.6977 5.69292 18.3071Z"
fill="white"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M5.69292 5.69292C6.08344 5.30239 6.71661 5.30239 7.10713 5.69292L18.3071 16.8929C18.6977 17.2834 18.6977 17.9166 18.3071 18.3071C17.9166 18.6977 17.2834 18.6977 16.8929 18.3071L5.69292 7.10713C5.30239 6.71661 5.30239 6.08344 5.69292 5.69292Z"
fill="white"
/>
</svg>
);
export const RespondingIconGray = <svg width="30" height="30" viewBox="0 0 40 40" xmlns="http://www.w3.org/2000/svg">
<circle cx="20" cy="20" fill="none" r="10" stroke="#BABABA" strokeWidth="2">
<animate attributeName="r" from="8" to="20" dur="1.5s" begin="0s" repeatCount="indefinite"/>
<animate attributeName="opacity" from="1" to="0" dur="1.5s" begin="0s" repeatCount="indefinite"/>
</circle>
<circle cx="20" cy="20" fill="#BABABA" r="10"/>
</svg>
export const RespondingIconGray = (
<svg width="30" height="30" viewBox="0 0 40 40" xmlns="http://www.w3.org/2000/svg">
<circle cx="20" cy="20" fill="none" r="10" stroke="#BABABA" strokeWidth="2">
<animate attributeName="r" from="8" to="20" dur="1.5s" begin="0s" repeatCount="indefinite" />
<animate attributeName="opacity" from="1" to="0" dur="1.5s" begin="0s" repeatCount="indefinite" />
</circle>
<circle cx="20" cy="20" fill="#BABABA" r="10" />
</svg>
);

View File

@ -1,217 +1,289 @@
.chat-popup {
position: fixed;
bottom: 10px;
right: 10px;
z-index: 1000;
position: fixed;
bottom: 10px;
right: 10px;
z-index: 1000;
}
.chat-popup-other-pages {
z-index: 1000;
position: relative;
}
.open-chat-button {
display: flex;
align-items: center;
justify-content: center;
height: 40px;
background-color: #1699e2;
color: white;
border-radius: 8px;
padding-right: 15px;
padding-left: 15px;
font-weight: bold;
border: none;
margin-bottom: 1.5rem;
margin-right: 1.5rem;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.2);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
height: 40px;
background-color: #1699e2;
color: white;
border-radius: 8px;
padding-right: 15px;
padding-left: 15px;
font-weight: bold;
border: none;
margin-bottom: 1.5rem;
margin-right: 1.5rem;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.2);
cursor: pointer;
}
.close-chat-button {
display: flex;
align-items: center;
justify-content: center;
height: 40px;
background-color: #1699e2;
color: white;
border-radius: 8px;
padding-right: 15px;
padding-left: 15px;
border: none;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.2);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
height: 40px;
background-color: #1699e2;
color: white;
border-radius: 8px;
padding-right: 15px;
padding-left: 15px;
border: none;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.2);
cursor: pointer;
}
.sparkles-icon {
width: 20px;
height: 20px;
fill: white;
margin-top: 3px;
margin-right: 5px;
width: 20px;
height: 20px;
fill: white;
margin-top: 3px;
margin-right: 5px;
}
.chat-window {
width: 100%;
max-width: 500px;
border: 1px solid #ccc;
background-color: #fff;
padding: 15px;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.2);
margin-top: 10px;
display: flex;
flex-direction: column;
max-height: 90vh;
min-height: 50vh;
height: auto;
overflow: auto;
border-radius: 10px;
width: 100%;
max-width: 500px;
border: 1px solid #ccc;
background-color: #fff;
padding: 15px;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.2);
margin-top: 10px;
display: flex;
flex-direction: column;
max-height: 90vh;
min-height: 50vh;
height: auto;
overflow: auto;
border-radius: 10px;
}
.close-icon {
width: 23px;
height: 23px;
margin-top: 5px;
fill: white;
margin-right: 3px;
width: 23px;
height: 23px;
margin-top: 5px;
fill: white;
margin-right: 3px;
}
.formatted-text {
white-space: pre-line;
color: #333;
white-space: pre-line;
color: #333;
}
.messages-container pre {
background-color: #8f8f8f;
background-color: #8f8f8f;
}
.formatted-text a {
color: blue;
text-decoration: underline;
color: blue;
text-decoration: underline;
}
.messages-container {
overflow-y: auto;
flex: 1;
overflow-y: auto;
flex: 1;
}
.chat-popup .message {
border-radius: 8px;
padding: 10px 15px;
margin: 5px 0;
max-width: 80%;
border-radius: 8px;
padding: 10px 15px;
margin: 5px 0;
max-width: 80%;
}
.user-message-container,
.bot-message-container {
width: 100%;
margin: 3px 0;
width: 100%;
margin: 3px 0;
}
.user-message-container {
display: flex;
justify-content: flex-end;
display: flex;
justify-content: flex-end;
}
.bot-message-container {
display: flex;
justify-content: flex-start;
display: flex;
justify-content: flex-start;
}
.user-message,
.bot-message {
display: block;
display: block;
}
.user-message {
text-align: right;
background-color: #1699e2;
color: white;
border-top-right-radius: 0;
text-align: right;
background-color: #1699e2;
color: white;
border-top-right-radius: 0;
}
.bot-message {
text-align: left;
background-color: #e9e9e9;
border-top-left-radius: 0;
text-align: left;
background-color: #e9e9e9;
border-top-left-radius: 0;
}
.responding-message {
}
.chat-popup input {
width: 80%;
padding: 10px;
border-radius: 5px 0 0 5px;
border: 1px solid #ccc;
outline: none;
height: 40px;
box-sizing: border-box;
flex: 1;
width: 80%;
padding: 10px;
border-radius: 5px 0 0 5px;
border: 1px solid #ccc;
outline: none;
height: 40px;
box-sizing: border-box;
flex: 1;
}
.chat-popup .input-container {
display: flex;
margin-top: auto;
width: 100%;
font-size: 16px;
background-color: #fff;
display: flex;
margin-top: auto;
width: 100%;
font-size: 16px;
background-color: #fff;
}
.chat-popup .input-text {
font-size: 16px;
color: #333;
background-color: white;
font-size: 16px;
color: #333;
background-color: white;
}
.chat-popup .input-text:disabled {
background-color: #eeeeee !important;
background-color: #eeeeee !important;
}
.chat-popup .input-button {
background-color: #1699e2;
color: white;
padding-left: 15px;
padding-right: 15px;
border: none;
height: 40px;
border-radius: 0 5px 5px 0;
cursor: pointer;
background-color: #1699e2;
color: white;
padding-left: 15px;
padding-right: 15px;
border: none;
height: 40px;
border-radius: 0 5px 5px 0;
cursor: pointer;
}
.chat-popup .bad-response-form .thumbs-down-button {
background-color: transparent;
border: none;
}
.chat-popup .bad-response-container {
display: flex;
flex-direction: column;
gap: 10px;
padding: 5px;
margin-bottom: 10px;
}
.chat-popup .bad-response-form textarea {
width: 100%;
border: 1px solid #ccc;
border-radius: 5px;
padding: 10px;
}
.chat-popup .feedback-submit-button {
background-color: #1699e2;
color: white;
padding-left: 15px;
padding-right: 15px;
padding-top: 10px;
padding-bottom: 10px;
border-radius: 5px;
border: none;
align-self: flex-end;
}
.chat-popup .input-button:disabled {
background-color: #ababab;
background-color: #ababab;
}
.chat-popup .info-bar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
background-color: #1699e2;
border-radius: 8px 8px 0 0;
padding: 10px 15px;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
background-color: #1699e2;
border-radius: 8px 8px 0 0;
padding: 10px 15px;
}
.bot-name-pic-container {
display: flex;
color: white;
display: flex;
color: white;
height: 30px;
}
.bot-name {
margin-right: 10px;
font-weight: bold;
font-size: 1.2rem;
margin-right: 10px;
font-weight: bold;
font-size: 1.2rem;
}
.bot-pic {
border-radius: 50%;
border-radius: 50%;
}
.clear-button {
background-color: transparent;
border: none;
color: white;
cursor: pointer;
font-size: 0.9rem;
background-color: transparent;
border: none;
color: white;
cursor: pointer;
font-size: 0.9rem;
}
html[data-theme=dark] .messages-container code {
background-color: #e0e0e0;
}
html[data-theme='dark'] .messages-container code {
background-color: #e0e0e0;
}
.chat-popup.minimized .chat-window {
transition: right 0.3s ease-in-out;
}
.minimize-chat-button {
position: absolute;
top: 0;
right: 0;
background-color: #1699e2;
color: white;
border: none;
padding: 8px 16px;
font-size: 12px;
cursor: pointer;
border-radius: 0 0 0 10px; /* Rounded corner on the bottom-left */
}
/* Additional styling for buttons and chat window when it is minimized */
.chat-popup.minimized .minimize-chat-button {
right: 350px; /* Adjust based on your chat window width */
}
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}

View File

@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 90 91" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M54.9271 87.6622C55.9648 92.6386 64.9707 91.9524 64.9707 78.2908C64.9707 64.6293 58.8776 59.6345 58.8776 55.1522C58.8776 52.4114 71.7032 54.4088 79.9466 54.4088C88.19 54.4088 90.8201 43.7305 82.8242 42.1896C93.439 38.4024 88.6752 27.0968 81.9761 27.0968C88.6752 26.064 88.845 12.0257 77.3118 13.6439C81.9761 7.42423 76.8095 1.76297 70.1161 1.11879C48.5059 -0.961018 20.6679 2.30047 20.6679 8.70652C20.6679 15.1126 20.6679 48.6729 20.6679 52.4114C20.6679 58.6908 47.9488 54.1954 54.9271 87.6622ZM5.53099 54.0323H8.98948C12.0442 54.0323 14.5205 51.555 14.5205 48.5005V10.457C14.5205 7.40182 12.0408 4.92515 8.98948 4.92515H5.53099C2.47631 4.92515 0 7.40248 0 10.457V48.5005C0 51.5557 2.47966 54.0323 5.53099 54.0323Z" fill="#9ba7b0"/>
</svg>

After

Width:  |  Height:  |  Size: 845 B

View File

@ -0,0 +1,33 @@
export interface ClientMessage {
userMessage: string;
botResponse: string;
}
export interface BadBotResponse {
messageId: string;
responseText: string | null;
}
export interface ChatMessagePayload {
payloadType: 'chatMessage';
chatMessage: {
previousMessages: ClientMessage[];
currentUserInput: string;
messageThreadId: string;
messageId: string;
};
responseQuality: null;
}
export interface BadBotResponsePayload {
payloadType: 'badBotResponse';
badBotResponse: BadBotResponse;
chatMessage: null;
}
export type WebsocketPayload = ChatMessagePayload | BadBotResponsePayload;
export interface BotWebsocketEvent {
type: 'loading' | 'responsePart' | 'error' | 'endOfStream';
message: string;
}