-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #76 from assistants-hub/43_full_screen_chatbot
#43 Fullscreen assistants with permalink
- Loading branch information
Showing
9 changed files
with
474 additions
and
205 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
import { Button, TextInput } from 'flowbite-react'; | ||
import React, { useContext, useEffect, useRef } from 'react'; | ||
import AssistantContext from '@/app/assistants/[id]/AssistantContext'; | ||
import { useChatContext } from '@/app/assistants/[id]/chat/useChatContext'; | ||
import { getInputMessageLabel, getPrimaryColor } from '@/app/utils/assistant'; | ||
import { ChatPageHeader } from '@/app/assistants/[id]/chat/ChatPageHeader'; | ||
import { Message } from '@/app/types/message'; | ||
import ChatMessage from '@/app/assistants/[id]/chat/ChatMessage'; | ||
import ChatMessageStreaming from '@/app/assistants/[id]/chat/ChatMessageStreaming'; | ||
import ChatTyping from '@/app/assistants/[id]/chat/ChatTyping'; | ||
|
||
export default function ChatPage() { | ||
const { assistant } = useContext(AssistantContext); | ||
|
||
const bottomRef = useRef(null); | ||
const messagesRef = useRef<HTMLDivElement | null>(null); | ||
|
||
const { | ||
typedMessage, | ||
setTypedMessage, | ||
messageStatus, | ||
streamText, | ||
messages, | ||
sendMessage, | ||
} = useChatContext(); | ||
|
||
useEffect(() => { | ||
if (messagesRef?.current && 'scrollIntoView' in messagesRef.current) { | ||
messagesRef.current.scrollIntoView({ block: 'end', behavior: 'smooth' }); | ||
} | ||
}, [messages]); | ||
|
||
useEffect(() => { | ||
// @ts-ignore | ||
bottomRef.current?.scrollIntoView({ behavior: 'smooth', block: 'end' }); | ||
}, [streamText]); | ||
|
||
return ( | ||
<div key='1' className='flex h-screen flex-col'> | ||
<div className='flex space-y-4 bg-gray-100 pb-2 pt-1'> | ||
<ChatPageHeader /> | ||
</div> | ||
<div className='flex-grow space-y-4 overflow-auto p-4 py-0 pt-0'> | ||
<div className='mx-auto flex max-w-2xl flex-col rounded-b rounded-t-none'> | ||
<div className={'max-w-2xl overflow-y-auto'}> | ||
<div | ||
className='flex flex-col gap-3 self-center overflow-y-auto px-4 py-2' | ||
ref={messagesRef} | ||
> | ||
{messages.map((message: Message, index) => { | ||
return <ChatMessage key={index} message={message} />; | ||
})} | ||
{streamText ? ( | ||
<> | ||
<ChatMessageStreaming | ||
message={streamText} | ||
></ChatMessageStreaming> | ||
<div ref={bottomRef} /> | ||
</> | ||
) : ( | ||
<></> | ||
)} | ||
{messageStatus === 'in_progress' && !streamText ? ( | ||
<ChatTyping /> | ||
) : ( | ||
<></> | ||
)} | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
<div className='z-100 mx-auto w-full max-w-2xl rounded-lg border border-2 bg-white p-6 shadow md:w-[800px]'> | ||
{messageStatus === 'in_progress' ? ( | ||
<span className='text-xs font-normal text-gray-500 dark:text-white'> | ||
{assistant.name} is typing... | ||
</span> | ||
) : ( | ||
<></> | ||
)} | ||
<div className='items-top flex space-x-2'> | ||
<TextInput | ||
className='block w-full rounded-lg border bg-white text-sm text-gray-900 dark:text-white dark:placeholder-gray-400' | ||
placeholder={getInputMessageLabel(assistant)} | ||
readOnly={false} | ||
disabled={messageStatus === 'in_progress'} | ||
value={typedMessage} | ||
onKeyDown={(e) => { | ||
if (e.key === 'Enter' && !e.shiftKey) { | ||
sendMessage(); | ||
} | ||
e.stopPropagation(); | ||
}} | ||
onChange={(event) => { | ||
setTypedMessage(event.target.value); | ||
}} | ||
></TextInput> | ||
<Button | ||
as='span' | ||
className='inline-flex cursor-pointer justify-center border-transparent bg-transparent' | ||
style={{ | ||
color: getPrimaryColor(assistant), | ||
}} | ||
onClick={sendMessage} | ||
> | ||
<svg | ||
className='h-5 w-5 rotate-90 rtl:-rotate-90' | ||
aria-hidden='true' | ||
xmlns='http://www.w3.org/2000/svg' | ||
fill='currentColor' | ||
viewBox='0 0 18 20' | ||
> | ||
<path d='m17.914 18.594-8-18a1 1 0 0 0-1.828 0l-8 18a1 1 0 0 0 1.157 1.376L8 18.281V9a1 1 0 0 1 2 0v9.281l6.758 1.689a1 1 0 0 0 1.156-1.376Z' /> | ||
</svg> | ||
</Button> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { useGetAssistant } from '@/app/assistants/[id]/client'; | ||
import React, { useEffect, useState } from 'react'; | ||
import { Assistant } from '@/app/types/assistant'; | ||
import ChatPage from '@/app/assistants/[id]/chat/ChatPage'; | ||
import { Spinner } from 'flowbite-react'; | ||
import AssistantContext from '@/app/assistants/[id]/AssistantContext'; | ||
|
||
export default function ChatPageContextWrapper(props: { assistantId: string }) { | ||
let { assistantLoading, assistantResponse, assistantEmpty, reload } = | ||
useGetAssistant(props.assistantId); | ||
|
||
const [loading, setLoading] = useState(true); | ||
const [assistant, setAssistant] = useState<Assistant>(assistantResponse); | ||
|
||
useEffect(() => { | ||
if (assistantResponse) { | ||
setAssistant(assistantResponse); | ||
setLoading(false); | ||
} | ||
}, [assistantLoading]); | ||
|
||
const changeAssistant = async (assistant: Assistant) => { | ||
setAssistant(assistant); | ||
// We don't allow changes from the end-user so this is a no-op | ||
}; | ||
|
||
return loading ? ( | ||
<div className='bg-grey flex h-[calc(100vh-120px)] items-center justify-center '> | ||
<Spinner color='info' aria-label='Loading assistant..' /> | ||
</div> | ||
) : ( | ||
<AssistantContext.Provider | ||
value={{ assistant, setAssistant: changeAssistant }} | ||
> | ||
<ChatPage /> | ||
</AssistantContext.Provider> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
'use client'; | ||
|
||
import { Card, Dropdown } from 'flowbite-react'; | ||
import Image from 'next/image'; | ||
import { useContext } from 'react'; | ||
import AssistantContext from '@/app/assistants/[id]/AssistantContext'; | ||
import { getImageHash } from '@/app/utils/hash'; | ||
|
||
export function ChatPageHeader() { | ||
const { assistant } = useContext(AssistantContext); | ||
|
||
return ( | ||
<Card className='mx-auto my-auto flex max-w-2xl'> | ||
<div className='grid grid-cols-12 items-center'> | ||
<div className='col-span-2'> | ||
<Image | ||
className='rounded-full' | ||
src={ | ||
assistant.avatar | ||
? assistant.avatar | ||
: '/images/people/avatar/' + getImageHash(assistant.id) + '.jpg' | ||
} | ||
width={64} | ||
height={64} | ||
alt='Assistant' | ||
/> | ||
</div> | ||
<div className='col-span-10 items-center justify-center'> | ||
<p className='max-w-md text-xl font-semibold leading-relaxed'> | ||
{assistant.name} | ||
</p> | ||
<p className='max-w-md text-sm leading-relaxed'> | ||
{assistant.description} | ||
</p> | ||
</div> | ||
</div> | ||
</Card> | ||
); | ||
} |
Oops, something went wrong.