Skip to content

Commit ace1204

Browse files
committed
Fixed small issues
1 parent bddd6e0 commit ace1204

2 files changed

Lines changed: 81 additions & 29 deletions

File tree

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@
2121
"postcss": "^8.4.33",
2222
"react": "^18.2.0",
2323
"react-dom": "^18.2.0",
24+
"react-markdown": "^10.1.0",
2425
"react-router-dom": "^6.21.3",
26+
"rehype-raw": "^7.0.0",
27+
"remark-gfm": "^4.0.1",
2528
"tailwindcss": "^3.4.1",
2629
"uuid": "^9.0.1"
2730
},

src/templates/shared/components/Chatbot.tsx

Lines changed: 78 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
/* eslint-disable no-confusing-arrow */
22
import { useEffect, useRef, useState } from 'react';
3+
import ReactMarkdown from 'react-markdown';
4+
import rehypeRaw from 'rehype-raw';
5+
import remarkGfm from 'remark-gfm';
36
import {
47
Button,
58
Widget,
@@ -10,6 +13,8 @@ import {
1013
useCopyToClipboard,
1114
Modal,
1215
Drawer,
16+
LoadingSpinner,
17+
TextLink,
1318
} from '@neo4j-ndl/react';
1419

1520
import ChatBotAvatar from '../assets/chatbot-ai.png';
@@ -86,6 +91,7 @@ export default function Chatbot(props: ChatbotProps) {
8691

8792
const [typingMessageId, setTypingMessageId] = useState<number | null>(null);
8893
const [currentTypingText, setCurrentTypingText] = useState<string>('');
94+
const [isLoading, setIsLoading] = useState<boolean>(false);
8995

9096
const handleCloseModal = () => setIsOpenModal(false);
9197

@@ -129,30 +135,38 @@ export default function Chatbot(props: ChatbotProps) {
129135
}, 20);
130136
};
131137

132-
const handleSubmit = (e: { preventDefault: () => void }) => {
138+
const handleSubmit = async (e: { preventDefault: () => void }) => {
133139
e.preventDefault();
134140
if (!inputMessage.trim() || !currentSession) {
135141
return;
136142
}
137-
143+
138144
const date = new Date();
139145
const datetime = `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`;
140-
const userMessage: ChatMessage = {
141-
id: Date.now(),
142-
user: 'user',
143-
message: inputMessage,
144-
datetime: datetime,
146+
const userMessage: ChatMessage = {
147+
id: Date.now(),
148+
user: 'user',
149+
message: inputMessage,
150+
datetime: datetime
145151
};
146-
152+
147153
addMessageToCurrentSession(userMessage);
148154
setInputMessage('');
149155

156+
setIsLoading(true);
157+
158+
// Simulate API delay (~2 seconds)
159+
// This is where you would call your backend API to get the chatbot response
160+
await new Promise(resolve => setTimeout(resolve, 1000));
161+
150162
const chatbotReply = {
151163
response:
152164
'Hello, here is an example response with sources. To use the chatbot, plug this to your backend with a fetch containing an object response of type: {response: string, src: Array<string>}',
153165
src: ['1:1234-abcd-efgh-ijkl-5678:2', '3:8765-zyxw-vuts-rqpo-4321:4'],
154166
}; // Replace with getting a response from your chatbot through your APIs
155167

168+
setIsLoading(false);
169+
156170
simulateTypingEffect(chatbotReply);
157171
};
158172

@@ -162,7 +176,7 @@ export default function Chatbot(props: ChatbotProps) {
162176

163177
useEffect(() => {
164178
scrollToBottom();
165-
}, [currentSession?.messages, typingMessageId, currentTypingText]);
179+
}, [currentSession?.messages, typingMessageId, currentTypingText, isLoading]);
166180

167181
const handleNewSession = () => {
168182
createNewSession();
@@ -355,7 +369,6 @@ export default function Chatbot(props: ChatbotProps) {
355369
</Typography>
356370
</div>
357371
</div>
358-
359372
<div className='flex-1 overflow-y-auto pb-6 n-bg-palette-neutral-bg-default'>
360373
<div className='flex flex-col gap-3 p-3 min-h-full'>
361374
{currentMessages.map((chat) => (
@@ -396,15 +409,22 @@ export default function Chatbot(props: ChatbotProps) {
396409
}`}
397410
>
398411
<div>
399-
{chat.message.split(/`(.+?)`/).map((part, index) =>
400-
index % 2 === 1 ? (
401-
<span key={index} style={formattedTextStyle}>
402-
{part}
403-
</span>
404-
) : (
405-
part
406-
)
407-
)}
412+
<ReactMarkdown
413+
components={{
414+
code: ({ children }) => (
415+
<span style={formattedTextStyle}>
416+
{children}
417+
</span>
418+
),
419+
a: ({ ...props }) => (
420+
<TextLink type="external" href={props.href} target="_blank" >{props.children}</TextLink>
421+
)
422+
}}
423+
remarkPlugins={[remarkGfm]}
424+
rehypePlugins={[rehypeRaw]}
425+
>
426+
{chat.message}
427+
</ReactMarkdown>
408428
</div>
409429
<div className='text-right align-bottom pt-3'>
410430
<Typography variant='body-small'>{chat.datetime}</Typography>
@@ -449,6 +469,29 @@ export default function Chatbot(props: ChatbotProps) {
449469
</div>
450470
))}
451471

472+
{isLoading && (
473+
<div ref={messagesEndRef} className='flex gap-2.5 items-end flex-row'>
474+
<div className='w-8 h-8 mr-4 ml-4'>
475+
<Avatar
476+
className='-ml-4'
477+
hasStatus
478+
name='KM'
479+
size='x-large'
480+
source={ChatBotAvatar}
481+
status='online'
482+
type='image'
483+
shape='square'
484+
/>
485+
</div>
486+
<Widget header='' isElevated={true} className='p-4 self-start max-w-[55%] n-bg-palette-neutral-bg-weak'>
487+
<div className='flex items-center gap-2'>
488+
<LoadingSpinner size='small' />
489+
<Typography variant='body-medium'>Thinking...</Typography>
490+
</div>
491+
</Widget>
492+
</div>
493+
)}
494+
452495
{typingMessageId && currentTypingText && (
453496
<div ref={messagesEndRef} className='flex gap-2.5 items-end flex-row'>
454497
<div className='w-8 h-8 mr-4 ml-4'>
@@ -465,16 +508,22 @@ export default function Chatbot(props: ChatbotProps) {
465508
</div>
466509
<Widget header='' isElevated={true} className='p-4 self-start max-w-[55%] n-bg-palette-neutral-bg-weak'>
467510
<div>
468-
{currentTypingText.split(/`(.+?)`/).map((part, index) =>
469-
index % 2 === 1 ? (
470-
<span key={index} style={formattedTextStyle}>
471-
{part}
472-
</span>
473-
) : (
474-
part
475-
)
476-
)}
477-
<span className='animate-pulse'>|</span>
511+
<ReactMarkdown
512+
components={{
513+
code: ({ children }) => (
514+
<span style={formattedTextStyle}>
515+
{children}
516+
</span>
517+
),
518+
a: ({ ...props }) => (
519+
<TextLink type="external" href={props.href} target="_blank" >{props.children}</TextLink>
520+
)
521+
}}
522+
remarkPlugins={[remarkGfm]}
523+
rehypePlugins={[rehypeRaw]}
524+
>
525+
{currentTypingText}
526+
</ReactMarkdown>
478527
</div>
479528
<div className='text-right align-bottom pt-3'>
480529
<Typography variant='body-small'>Typing...</Typography>

0 commit comments

Comments
 (0)