mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-14 17:02:49 +03:00
Docs: Bot: Update
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/11079 GitOrigin-RevId: 48da2933e84289a5afca007ed054f5070bc11eaf
This commit is contained in:
parent
ee3c1a5a9a
commit
5c7e20f83b
@ -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,
|
||||
|
@ -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, '&')
|
||||
return sanitized
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
@ -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>
|
||||
);
|
||||
}
|
||||
|
BIN
docs/src/components/AiChatBot/docs-bot-profile-pic.webp
Normal file
BIN
docs/src/components/AiChatBot/docs-bot-profile-pic.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.9 KiB |
@ -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>
|
||||
);
|
||||
|
@ -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;
|
||||
}
|
||||
|
3
docs/src/components/AiChatBot/thumbs-down.svg
Normal file
3
docs/src/components/AiChatBot/thumbs-down.svg
Normal 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 |
33
docs/src/components/AiChatBot/types.ts
Normal file
33
docs/src/components/AiChatBot/types.ts
Normal 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user