diff --git a/app/globals.css b/app/globals.css index b2bc4f4..9beeb2c 100644 --- a/app/globals.css +++ b/app/globals.css @@ -5,68 +5,66 @@ @layer base { :root { --background: 0 0% 100%; - --foreground: 222.2 47.4% 11.2%; + --foreground: 240 10% 3.9%; - --muted: 210 40% 96.1%; - --muted-foreground: 215.4 16.3% 46.9%; + --muted: 240 4.8% 95.9%; + --muted-foreground: 240 3.8% 46.1%; --popover: 0 0% 100%; - --popover-foreground: 222.2 47.4% 11.2%; + --popover-foreground: 240 10% 3.9%; --card: 0 0% 100%; - --card-foreground: 222.2 47.4% 11.2%; + --card-foreground: 240 10% 3.9%; - --border: 214.3 31.8% 91.4%; - --input: 214.3 31.8% 91.4%; + --border: 240 5.9% 90%; + --input: 240 5.9% 90%; - --primary: 222.2 47.4% 11.2%; - --primary-foreground: 210 40% 98%; + --primary: 240 5.9% 10%; + --primary-foreground: 0 0% 98%; - --secondary: 210 40% 96.1%; - --secondary-foreground: 222.2 47.4% 11.2%; + --secondary: 240 4.8% 95.9%; + --secondary-foreground: 240 5.9% 10%; - --accent: 210 40% 96.1%; - --accent-foreground: 222.2 47.4% 11.2%; + --accent: 240 4.8% 95.9%; + --accent-foreground: ; - --destructive: 0 100% 50%; - --destructive-foreground: 210 40% 98%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 0 0% 98%; - --ring: 215 20.2% 65.1%; + --ring: 240 5% 64.9%; --radius: 0.5rem; } .dark { - --background: 224 71% 4%; - --foreground: 213 31% 91%; + --background: 240 10% 3.9%; + --foreground: 0 0% 98%; - --muted: 223 47% 11%; - --muted-foreground: 215.4 16.3% 56.9%; + --muted: 240 3.7% 15.9%; + --muted-foreground: 240 5% 64.9%; - --popover: 224 71% 4%; - --popover-foreground: 215 20.2% 65.1%; + --popover: 240 10% 3.9%; + --popover-foreground: 0 0% 98%; - --card: 224 71% 4%; - --card-foreground: 213 31% 91%; + --card: 240 10% 3.9%; + --card-foreground: 0 0% 98%; - --border: 216 34% 17%; - --input: 216 34% 17%; + --border: 240 3.7% 15.9%; + --input: 240 3.7% 15.9%; - --primary: 210 40% 98%; - --primary-foreground: 222.2 47.4% 1.2%; + --primary: 0 0% 98%; + --primary-foreground: 240 5.9% 10%; - --secondary: 222.2 47.4% 11.2%; - --secondary-foreground: 210 40% 98%; + --secondary: 240 3.7% 15.9%; + --secondary-foreground: 0 0% 98%; - --accent: 216 34% 17%; - --accent-foreground: 210 40% 98%; + --accent: 240 3.7% 15.9%; + --accent-foreground: ; - --destructive: 0 63% 31%; - --destructive-foreground: 210 40% 98%; + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 0 85.7% 97.3%; - --ring: 216 34% 17%; - - --radius: 0.5rem; + --ring: 240 3.7% 15.9%; } } @@ -76,10 +74,5 @@ } body { @apply bg-background text-foreground; - font-feature-settings: 'rlig' 1, 'calt' 1; } } - -pre:has(div.codeblock) { - padding: 0; -} diff --git a/components/chat-list.tsx b/components/chat-list.tsx index 2fae8fa..167506e 100644 --- a/components/chat-list.tsx +++ b/components/chat-list.tsx @@ -4,6 +4,7 @@ import { type Message } from 'ai-connector' import { ChatMessage } from '@/components/chat-message' import { EmptyScreen } from '@/components/empty-screen' +import { Separator } from '@/components/ui/separator' export interface ChatList { messages: Message[] @@ -11,15 +12,16 @@ export interface ChatList { export function ChatList({ messages }: ChatList) { return ( -
+
{messages.length > 0 ? ( - messages.map(message => ( - + messages.map((message, index) => ( +
+ + {index < messages.length - 1 && } +
)) ) : ( -
- -
+ )}
) diff --git a/components/chat-message.tsx b/components/chat-message.tsx index 5578c14..70a2eba 100644 --- a/components/chat-message.tsx +++ b/components/chat-message.tsx @@ -5,7 +5,7 @@ import remarkMath from 'remark-math' import { cn } from '@/lib/utils' import { fontMessage } from '@/lib/fonts' -import CodeBlock from '@/components/ui/codeblock' +import { CodeBlock } from '@/components/ui/codeblock' import { MemoizedReactMarkdown } from '@/components/markdown' import { OpenAI } from '@/components/icons' @@ -17,16 +17,17 @@ export function ChatMessage({ message, ...props }: ChatMessageProps) { return (
-
+
{children} diff --git a/components/chat-panel.tsx b/components/chat-panel.tsx index b4eb45a..c95a004 100644 --- a/components/chat-panel.tsx +++ b/components/chat-panel.tsx @@ -1,25 +1,54 @@ 'use client' -import { ExternalLink } from '@/components/external-link' import { UseChatHelpers } from 'ai-connector' -import { PromptForm } from './prompt-form' +import { RefreshCcw, StopCircle } from 'lucide-react' + +import { ExternalLink } from '@/components/external-link' +import { PromptForm } from '@/components/prompt-form' +import { Button } from '@/components/ui/button' export interface ChatPanelProps - extends Pick { + extends Pick< + UseChatHelpers, + 'append' | 'isLoading' | 'reload' | 'messages' | 'stop' + > { id?: string } export function ChatPanel({ - id, - append, isLoading, + stop, + append, reload, messages }: ChatPanelProps) { return ( -
-
-
+
+
+
+ {isLoading ? ( + + ) : ( + messages?.length > 0 && ( + + ) + )} +
+
{ append({ @@ -27,11 +56,10 @@ export function ChatPanel({ role: 'user' }) }} - onRefresh={messages.length ? reload : undefined} isLoading={isLoading} /> -

- This is an open source AI chatbot app built with{' '} +

+ Open source AI chatbot app built with{' '} Next.js and{' '} Vercel KV diff --git a/components/chat.tsx b/components/chat.tsx index d207ade..25833c5 100644 --- a/components/chat.tsx +++ b/components/chat.tsx @@ -11,23 +11,22 @@ export interface ChatProps { } export function Chat({ id, initialMessages }: ChatProps) { - const { messages, append, reload, isLoading } = useChat({ + const { messages, append, reload, stop, isLoading } = useChat({ initialMessages, id }) return ( -

-
- - -
+
+ +
) } diff --git a/components/empty-screen.tsx b/components/empty-screen.tsx index 4fe4bca..82feb1e 100644 --- a/components/empty-screen.tsx +++ b/components/empty-screen.tsx @@ -1,10 +1,10 @@ import { ArrowRight } from 'lucide-react' import { cn } from '@/lib/utils' +import { fontMessage } from '@/lib/fonts' import { useChatStore } from '@/lib/hooks/use-chat-store' import { ExternalLink } from '@/components/external-link' import { Button } from '@/components/ui/button' -import { OpenAI } from '@/components/icons' const exampleMessages = [ { @@ -25,45 +25,39 @@ export function EmptyScreen({ className }: React.ComponentProps<'div'>) { const { setDefaultMessage } = useChatStore() return ( -
-
- -
-
-

Welcome to Next.js Chatbot!

-

- This is an open source AI chatbot app built with{' '} - Next.js and{' '} - - Vercel KV - - . -

-

- You can start a conversation here or try the following examples: -

-
- {exampleMessages.map((message, index) => ( - - ))} -
+
+

+ Welcome to Next.js Chatbot! +

+

+ This is an open source AI chatbot app built with{' '} + Next.js and{' '} + + Vercel KV + + . +

+

+ You can start a conversation here or try the following examples: +

+
+ {exampleMessages.map((message, index) => ( + + ))}
) diff --git a/components/header.tsx b/components/header.tsx index 1bab4c9..9c412cf 100644 --- a/components/header.tsx +++ b/components/header.tsx @@ -11,14 +11,16 @@ export async function Header() { const chats = session?.user?.email ? await getChats(session.user.email) : [] return ( -
-
+
+
{/* @ts-ignore */} - - +
+ + +
-
diff --git a/components/prompt-form.tsx b/components/prompt-form.tsx index 82dad38..cbd40fe 100644 --- a/components/prompt-form.tsx +++ b/components/prompt-form.tsx @@ -1,12 +1,10 @@ 'use client' import * as React from 'react' -import { CornerDownLeft, Plus, RefreshCcw, StopCircle } from 'lucide-react' -import { useState } from 'react' +import { CornerDownLeft, Plus } from 'lucide-react' import Textarea from 'react-textarea-autosize' import { Button, buttonVariants } from '@/components/ui/button' -import { fontMessage } from '@/lib/fonts' import { useEnterSubmit } from '@/lib/hooks/use-enter-submit' import { cn } from '@/lib/utils' import { useChatStore } from '@/lib/hooks/use-chat-store' @@ -20,19 +18,12 @@ import Link from 'next/link' export interface PromptProps { onSubmit: (value: string) => void - onRefresh?: () => void - onAbort?: () => void isLoading: boolean } -export function PromptForm({ - onSubmit, - onRefresh, - onAbort, - isLoading -}: PromptProps) { +export function PromptForm({ onSubmit, isLoading }: PromptProps) { const { defaultMessage } = useChatStore() - const [input, setInput] = useState(defaultMessage) + const [input, setInput] = React.useState(defaultMessage) const { formRef, onKeyDown } = useEnterSubmit() const inputRef = React.useRef(null) @@ -50,88 +41,59 @@ export function PromptForm({
{ e.preventDefault() + if (input === '') { + return + } setInput('') await onSubmit(input) }} ref={formRef} > -
-
-
- {onRefresh ? ( - - ) : null} -
-
- -
+ + New Chat + + + New Chat + +