Skip to content

Commit

Permalink
Merge pull request #32 from reaviz/chat-only
Browse files Browse the repository at this point in the history
Chat Only View
  • Loading branch information
amcdnl authored Aug 7, 2024
2 parents f9d7f17 + 274bbe8 commit a5bc94a
Show file tree
Hide file tree
Showing 6 changed files with 234 additions and 14 deletions.
14 changes: 9 additions & 5 deletions src/Chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { useHotkeys } from 'reakeys';
import { cn, useComponentTheme } from 'reablocks';
import { Session } from './types';
import { ChatTheme, chatTheme } from './theme';
import { ChatContext } from './ChatContext';
import { ChatContext, ChatViewType } from './ChatContext';
import { PluggableList } from 'react-markdown/lib';
import { AnimatePresence } from 'framer-motion';
import { useDimensions } from './utils/useDimensions';
Expand All @@ -28,11 +28,13 @@ export interface ChatProps extends PropsWithChildren {
className?: string;

/**
* The type of prompt to display. Companion prompts are smaller and are
* meant to be displayed alongside other content. Full prompts are larger
* and are meant to be displayed on their own.
* The type of prompt to display.
*
* - Companion: Smaller prompt screen with session lists.
* - Console: Full screen experience.
* - Chat: Only chat, no sessions.
*/
viewType?: 'companion' | 'console';
viewType?: ChatViewType;

/**
* The list of sessions to display.
Expand Down Expand Up @@ -172,6 +174,7 @@ export const Chat: FC<ChatProps> = ({
disabled,
isLoading,
isCompact,
viewType,
activeSessionId: internalActiveSessionID,
selectSession: handleSelectSession,
deleteSession: handleDeleteSession,
Expand All @@ -183,6 +186,7 @@ export const Chat: FC<ChatProps> = ({
[
isLoading,
isCompact,
viewType,
disabled,
theme,
remarkPlugins,
Expand Down
3 changes: 3 additions & 0 deletions src/ChatContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ import { Session } from './types';
import { ChatTheme } from './theme';
import { PluggableList } from 'react-markdown/lib';

export type ChatViewType = 'chat' | 'companion' | 'console';

export interface ChatContextProps {
sessions: Session[];
disabled?: boolean;
activeSessionId: string | null;
theme?: ChatTheme;
isLoading?: boolean;
isCompact?: boolean;
viewType?: ChatViewType;
activeSession?: Session | null;
remarkPlugins?: PluggableList[];
selectSession?: (sessionId: string) => void;
Expand Down
8 changes: 4 additions & 4 deletions src/SessionMessages/SessionMessagePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import { motion } from 'framer-motion';
import BackIcon from '@/assets/back.svg?react';

export const SessionMessagePanel: FC<PropsWithChildren> = ({ children }) => {
const { activeSessionId, theme, isCompact, selectSession } =
const { activeSessionId, theme, isCompact, selectSession, viewType } =
useContext(ChatContext);
const isVisible = isCompact && activeSessionId;
const isVisible = (isCompact && activeSessionId) || viewType === 'chat' || !isCompact;

return (
(!isCompact || isVisible) && (
isVisible && (
<motion.div
initial={{ translateX: '200%' }}
animate={{
Expand All @@ -29,7 +29,7 @@ export const SessionMessagePanel: FC<PropsWithChildren> = ({ children }) => {
})}
>
<div className={cn(theme.messages.inner)}>
{isCompact && (
{(isCompact && viewType !== 'chat') && (
<Button
variant="text"
size="small"
Expand Down
6 changes: 3 additions & 3 deletions src/SessionMessages/SessionMessages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export const SessionMessages: React.FC<SessionMessagesProps> = ({
}, [activeSession, isAnimating]);

function handleShowMore() {
showNext(10);
showNext(limit);
requestAnimationFrame(() => (contentRef.current.scrollTop = 0));
}

Expand All @@ -78,7 +78,7 @@ export const SessionMessages: React.FC<SessionMessagesProps> = ({

const { data, hasMore, showNext } = useInfinityList({
items: reversedConvos,
limit
size: limit
});

// Reverse the data to the last one last now
Expand Down Expand Up @@ -110,7 +110,7 @@ export const SessionMessages: React.FC<SessionMessagesProps> = ({
initial="hidden"
animate="visible"
onAnimationComplete={() => {
setIsAnimating(false);
requestAnimationFrame(() => setIsAnimating(false));
}}
>
{children(convosToRender)}
Expand Down
4 changes: 2 additions & 2 deletions src/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export interface ChatTheme {

export const chatTheme: ChatTheme = {
base: 'dark:text-white text-gray-500',
console: 'flex w-full gap-10 h-full',
console: 'flex w-full gap-4 h-full',
companion: 'w-full h-full overflow-hidden',
empty: 'text-center flex-1',
sessions: {
Expand All @@ -110,7 +110,7 @@ export const chatTheme: ChatTheme = {
},
messages: {
base: '',
console: 'flex flex-col mr-5 flex-1 overflow-hidden',
console: 'flex flex-col mx-5 flex-1 overflow-hidden',
companion: 'flex w-full h-full',
back: 'self-start p-0 my-2',
inner: 'flex-1 h-full flex flex-col',
Expand Down
213 changes: 213 additions & 0 deletions stories/Chat.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
import { Meta } from '@storybook/react';
import {
Chat,
SessionsList,
SessionsGroup,
SessionListItem,
NewSessionButton,
SessionMessages,
SessionGroups,
ChatInput,
SessionMessagePanel,
SessionMessagesHeader,
SessionMessage,
Session
} from '../src';
import {
fakeSessions,
sessionWithSources,
sessionsWithFiles
} from './examples';
import { useState } from 'react';
import Placeholder from '@/assets/placeholder.svg?react';
import PlaceholderDark from '@/assets/placeholder-dark.svg?react';

export default {
title: 'Demos/Chat',
component: Chat
} as Meta;

export const Compact = () => {
const [activeId, setActiveId] = useState<string>(fakeSessions[0].id);
const [sessions, setSessions] = useState<Session[]>([
...fakeSessions,
...sessionsWithFiles,
...sessionWithSources
]);

return (
<div
className="dark:bg-gray-950 bg-white"
style={{
width: 350,
height: 500,
padding: 20,
borderRadius: 5
}}
>
<Chat
viewType="chat"
sessions={sessions}
activeSessionId={activeId}
onNewSession={() => {
const newId = (sessions.length + 1).toLocaleString();
setSessions([
...sessions,
{
id: newId,
title: `New Session #${newId}`,
createdAt: new Date(),
updatedAt: new Date(),
conversations: []
}
]);
setActiveId(newId);
}}
onSelectSession={setActiveId}
onDeleteSession={() => alert('delete!')}
>
<SessionMessagePanel>
<SessionMessages>
{conversations =>
conversations.map((conversation, index) => (
<SessionMessage
key={conversation.id}
conversation={conversation}
isLast={index === conversations.length - 1}
/>
))
}
</SessionMessages>
<ChatInput />
</SessionMessagePanel>
</Chat>
</div>
);
};

export const FullScreen = () => {
const [activeId, setActiveId] = useState<string>(fakeSessions[0].id);
const [sessions, setSessions] = useState<Session[]>([
...fakeSessions,
...sessionsWithFiles,
...sessionWithSources
]);

return (
<div
className="dark:bg-gray-950 bg-white"
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
padding: 20,
margin: 20,
borderRadius: 5
}}
>
<Chat
viewType="chat"
sessions={sessions}
activeSessionId={activeId}
onNewSession={() => {
const newId = (sessions.length + 1).toLocaleString();
setSessions([
...sessions,
{
id: newId,
title: `New Session #${newId}`,
createdAt: new Date(),
updatedAt: new Date(),
conversations: []
}
]);
setActiveId(newId);
}}
onSelectSession={setActiveId}
onDeleteSession={() => alert('delete!')}
>
<SessionMessagePanel>
<SessionMessages>
{conversations =>
conversations.map((conversation, index) => (
<SessionMessage
key={conversation.id}
conversation={conversation}
isLast={index === conversations.length - 1}
/>
))
}
</SessionMessages>
<ChatInput />
</SessionMessagePanel>
</Chat>
</div>
);
};

export const Empty = () => {
const [activeId, setActiveId] = useState<string>();
const [sessions, setSessions] = useState<Session[]>([]);

return (
<div
className="dark:bg-gray-950 bg-white"
style={{
width: 350,
height: 500,
padding: 20,
borderRadius: 5
}}
>
<Chat
viewType="chat"
sessions={sessions}
activeSessionId={activeId}
onNewSession={() => {
const newId = (sessions.length + 1).toLocaleString();
setSessions([
...sessions,
{
id: newId,
title: `New Session #${newId}`,
createdAt: new Date(),
updatedAt: new Date(),
conversations: []
}
]);
setActiveId(newId);
}}
onSelectSession={setActiveId}
onDeleteSession={() => alert('delete!')}
>
<SessionMessagePanel>
<SessionMessages
newSessionContent={
<div className="flex flex-col gap-2 items-center justify-center h-full">
<Placeholder className="h-[50%] block dark:hidden max-w-[100%]" />
<PlaceholderDark className="h-[50%] hidden dark:block max-w-[100%]" />
<p className="text-gray-500 max-w-[400px] text-center">
Welcome to Reachat, a UI library for effortlessly building and
customizing chat experiences with Tailwind.
</p>
</div>
}
>
{conversations =>
conversations.map((conversation, index) => (
<SessionMessage
key={conversation.id}
conversation={conversation}
isLast={index === conversations.length - 1}
/>
))
}
</SessionMessages>
<ChatInput />
</SessionMessagePanel>
</Chat>
</div>
);
};

0 comments on commit a5bc94a

Please sign in to comment.