Skip to content

Commit

Permalink
[Chat]: Remember model and empty state (#274)
Browse files Browse the repository at this point in the history
- Remembers previously selected model
- Adds empty states with example prompts.
  • Loading branch information
richardgill authored Jun 15, 2024
1 parent 2fdf0d2 commit 21b9282
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 13 deletions.
12 changes: 3 additions & 9 deletions apps/www/src/components/ModelCards.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Model } from "@/types";
import React from "react";
import { BackgroundCard } from "./cards/BackgroundCard";

type ModelCardsProps = {
models: Model[];
Expand All @@ -9,21 +10,14 @@ export const ModelCards: React.FC<ModelCardsProps> = ({ models }) => {
return (
<div className="mt-12 grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
{models.map((model, index) => (
<div
key={index}
className="bg-background group relative overflow-hidden rounded-2xl border p-5 md:p-8"
>
<div
aria-hidden="true"
className="absolute inset-0 aspect-video -translate-y-1/2 rounded-full border bg-gradient-to-b from-purple-500/80 to-white opacity-25 blur-2xl duration-300 group-hover:-translate-y-1/4 dark:from-white dark:to-white dark:opacity-5 dark:group-hover:opacity-10"
/>
<BackgroundCard key={index}>
<div className="flex flex-col items-center gap-4">
<div className="border-border relative flex size-12 rounded-2xl border shadow-sm *:relative *:m-auto *:size-6">
{model.logo}
</div>
<p className="text-lg font-semibold">{model.title}</p>
</div>
</div>
</BackgroundCard>
))}
</div>
);
Expand Down
26 changes: 26 additions & 0 deletions apps/www/src/components/cards/BackgroundCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { cn } from "@/lib/utils";
import React from "react";

export const BackgroundCard = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, children, ...props }, ref) => {
return (
<div
ref={ref}
{...props}
className={cn(
"bg-background group relative overflow-hidden rounded-2xl border p-5 md:p-8",
className,
)}
>
<div
aria-hidden="true"
className="absolute inset-0 aspect-video -translate-y-1/2 rounded-full border bg-gradient-to-b from-purple-500/80 to-white opacity-25 blur-2xl duration-300 group-hover:-translate-y-1/4 dark:from-white dark:to-white dark:opacity-5 dark:group-hover:opacity-10"
/>
{children}
</div>
);
});

BackgroundCard.displayName = "BackgroundCard";
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ import * as R from "remeda";
import {
AutosizeTextarea,
type AutosizeTextAreaRef,
} from "./ui/custom/AutosizeTextarea";
} from "../ui/custom/AutosizeTextarea";
import { EmptyState } from "./EmptyState";

const IS_SERVER = typeof window === "undefined";
const CHAT_OPENAI_API_KEY = "CHAT_OPENAI_API_KEY";
const LAST_MODEL = "LAST_MODEL";
const CHAT_GPT_MODELS = ["gpt-3.5-turbo", "gpt-4-turbo", "gpt-4o"];

const CONTAINER_CLASSES = "mx-auto max-w-2xl container";
Expand Down Expand Up @@ -56,14 +58,15 @@ const ChatMessage: React.FC<{
};

export const Chat = () => {
const submitButtonRef = React.useRef<HTMLButtonElement>(null);
const messagesDivRef = React.useRef<HTMLInputElement>(null);
const messageTextAreaRef = React.useRef<AutosizeTextAreaRef>(null);
const storage = !IS_SERVER ? window.localStorage : null;
const [currentApiKey, setCurrentApiKey] = React.useState<string>(
storage?.getItem(CHAT_OPENAI_API_KEY) ?? "",
);
const [selectedChatGptModel, setSelectedChatGptModel] =
React.useState<string>(CHAT_GPT_MODELS[0]);
React.useState<string>(storage?.getItem(LAST_MODEL) ?? CHAT_GPT_MODELS[0]);
const [systemMessage, setSystemMessage] = React.useState<string>("");
const [missingApiKey, setMissingApiKey] = React.useState<boolean>(false);
const [error, setError] = React.useState<Error>();
Expand Down Expand Up @@ -113,9 +116,11 @@ export const Chat = () => {
const handleUpdateChatGptModel = (value: string) => {
setMissingApiKey(false);
setSelectedChatGptModel(value);
storage?.setItem(LAST_MODEL, value);
};

const onSubmit = (e: React.FormEvent) => {
console.log("zzz subbmitting");
if (
currentApiKey.length === 0 &&
selectedChatGptModel != CHAT_GPT_MODELS[0]
Expand Down Expand Up @@ -194,7 +199,23 @@ export const Chat = () => {
className="flex flex-1 flex-col-reverse overflow-y-auto pt-4 pb-3"
>
{/* This div takes up all the remaining space so the messages start at the top */}
<div className="flex flex-1 flex-col" />{" "}
<div className={`flex flex-1 flex-col justify-center`}>
{messages.length <= 1 && (
<EmptyState
onExampleClick={(example) => {
setSystemMessage(example.systemPrompt);
if (example.userPrompt) {
handleInputChange({
target: { value: example.userPrompt },
} as React.ChangeEvent<HTMLTextAreaElement>);
if (submitButtonRef?.current) {
setTimeout(() => submitButtonRef?.current?.click(), 1);
}
}
}}
/>
)}
</div>
{reversedMessagesWithoutSystem.map((message, index) => {
const { role } = message;
const isStreamFinished =
Expand Down Expand Up @@ -268,6 +289,7 @@ export const Chat = () => {
className="focus-visible:ring-0 pr-0 resize-none bg-transparent focus-within:outline-none sm:text-base border-none"
/>
<Button
ref={submitButtonRef}
disabled={isLoading || !input}
className="self-end"
onClick={onSubmit}
Expand Down
48 changes: 48 additions & 0 deletions apps/www/src/components/chat/EmptyState.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { BackgroundCard } from "../cards/BackgroundCard";

type Example = {
systemPrompt: string;
userPrompt?: string;
description: string;
};
const examples: Example[] = [
{
systemPrompt: "You speak like a pirate when responding.",
description: "🏴‍☠️ Pirate",
},
{
systemPrompt: `You reply with buttons like this: ⦅buttons;Star ⭐;Confetti 🎉⦆
button replies must always start with ⦅buttons; exactly
You constantly fish for github stars using the button (which explodes stars of the screen) for https://github.com/llm-ui-kit/llm-ui, whilst being charming and funny, like the libraries author Richard Gill.`,
userPrompt: "How can I help out llm-ui?",
description: "Star ⭐ buttons",
},
];

const Example: React.FC<{
example: Example;
onClick: (example: Example) => void;
}> = ({ example, onClick }) => {
return (
<BackgroundCard
onClick={() => onClick(example)}
className="hover:cursor-pointer text-center "
>
{example.description}
</BackgroundCard>
);
};

type EmptyStateProps = {
onExampleClick: (example: Example) => void;
};

export const EmptyState: React.FC<EmptyStateProps> = ({ onExampleClick }) => (
<div className="grid grid-cols-2 gap-6 mx-auto max-w-4xl container">
{examples.map((example, index) => {
return <Example key={index} example={example} onClick={onExampleClick} />;
})}
</div>
);
2 changes: 1 addition & 1 deletion apps/www/src/pages/chat.astro
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
import MainLayoutFullScreen from "@/layouts/MainLayoutFullScreen.astro";
import { Chat as ChatComponent } from "@/components/Chat";
import { Chat as ChatComponent } from "@/components/chat/Chat";
---

<MainLayoutFullScreen title="Chat">
Expand Down

0 comments on commit 21b9282

Please sign in to comment.