Refactor to use ai/rsc (#253)
This commit is contained in:
parent
69ca8fcc22
commit
e85ba803dd
66 changed files with 2799 additions and 740 deletions
|
|
@ -1,102 +1,124 @@
|
|||
import * as React from 'react'
|
||||
import { type UseChatHelpers } from 'ai/react'
|
||||
|
||||
import { shareChat } from '@/app/actions'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { PromptForm } from '@/components/prompt-form'
|
||||
import { ButtonScrollToBottom } from '@/components/button-scroll-to-bottom'
|
||||
import { IconRefresh, IconShare, IconStop } from '@/components/ui/icons'
|
||||
import { IconShare } from '@/components/ui/icons'
|
||||
import { FooterText } from '@/components/footer'
|
||||
import { ChatShareDialog } from '@/components/chat-share-dialog'
|
||||
import { useAIState, useActions, useUIState } from 'ai/rsc'
|
||||
import type { AI } from '@/lib/chat/actions'
|
||||
import { nanoid } from 'nanoid'
|
||||
import { UserMessage } from './stocks/message'
|
||||
|
||||
export interface ChatPanelProps
|
||||
extends Pick<
|
||||
UseChatHelpers,
|
||||
| 'append'
|
||||
| 'isLoading'
|
||||
| 'reload'
|
||||
| 'messages'
|
||||
| 'stop'
|
||||
| 'input'
|
||||
| 'setInput'
|
||||
> {
|
||||
export interface ChatPanelProps {
|
||||
id?: string
|
||||
title?: string
|
||||
input: string
|
||||
setInput: (value: string) => void
|
||||
}
|
||||
|
||||
export function ChatPanel({
|
||||
id,
|
||||
title,
|
||||
isLoading,
|
||||
stop,
|
||||
append,
|
||||
reload,
|
||||
input,
|
||||
setInput,
|
||||
messages
|
||||
}: ChatPanelProps) {
|
||||
export function ChatPanel({ id, title, input, setInput }: ChatPanelProps) {
|
||||
const [aiState] = useAIState()
|
||||
const [messages, setMessages] = useUIState<typeof AI>()
|
||||
const { submitUserMessage } = useActions()
|
||||
const [shareDialogOpen, setShareDialogOpen] = React.useState(false)
|
||||
|
||||
const exampleMessages = [
|
||||
{
|
||||
heading: 'Explain the concept',
|
||||
subheading: 'of a serverless function',
|
||||
message: `Explain the concept of a serverless function`
|
||||
},
|
||||
{
|
||||
heading: 'What are the benefits',
|
||||
subheading: 'of using turborepo in my codebase?',
|
||||
message: 'What are the benefits of using turborepo in my codebase?'
|
||||
},
|
||||
{
|
||||
heading: 'List differences between',
|
||||
subheading: 'pages and app router in Next.js',
|
||||
message: `List differences between pages and app router in Next.js`
|
||||
},
|
||||
{
|
||||
heading: 'What is the price',
|
||||
subheading: `of VRCL in the stock market?`,
|
||||
message: `What is the price of VRCL in the stock market?`
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="fixed inset-x-0 bottom-0 w-full bg-gradient-to-b from-muted/30 from-0% to-muted/30 to-50% animate-in duration-300 ease-in-out dark:from-background/10 dark:from-10% dark:to-background/80 peer-[[data-state=open]]:group-[]:lg:pl-[250px] peer-[[data-state=open]]:group-[]:xl:pl-[300px]">
|
||||
<div className="fixed inset-x-0 bottom-0 w-full bg-gradient-to-b from-muted/30 from-0% to-muted/30 to-50% duration-300 ease-in-out animate-in dark:from-background/10 dark:from-10% dark:to-background/80 peer-[[data-state=open]]:group-[]:lg:pl-[250px] peer-[[data-state=open]]:group-[]:xl:pl-[300px]">
|
||||
<ButtonScrollToBottom />
|
||||
|
||||
<div className="mx-auto sm:max-w-2xl sm:px-4">
|
||||
<div className="flex items-center justify-center h-12">
|
||||
{isLoading ? (
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => stop()}
|
||||
className="bg-background"
|
||||
>
|
||||
<IconStop className="mr-2" />
|
||||
Stop generating
|
||||
</Button>
|
||||
) : (
|
||||
messages?.length >= 2 && (
|
||||
<div className="flex space-x-2">
|
||||
<Button variant="outline" onClick={() => reload()}>
|
||||
<IconRefresh className="mr-2" />
|
||||
Regenerate response
|
||||
</Button>
|
||||
{id && title ? (
|
||||
<>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setShareDialogOpen(true)}
|
||||
>
|
||||
<IconShare className="mr-2" />
|
||||
Share
|
||||
</Button>
|
||||
<ChatShareDialog
|
||||
open={shareDialogOpen}
|
||||
onOpenChange={setShareDialogOpen}
|
||||
onCopy={() => setShareDialogOpen(false)}
|
||||
shareChat={shareChat}
|
||||
chat={{
|
||||
id,
|
||||
title,
|
||||
messages
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
<div className="mb-4 grid grid-cols-2 gap-2 px-4 sm:px-0">
|
||||
{messages.length === 0 &&
|
||||
exampleMessages.map((example, index) => (
|
||||
<div
|
||||
key={example.heading}
|
||||
className={`cursor-pointer rounded-lg border bg-white p-4 hover:bg-zinc-50 dark:bg-zinc-950 dark:hover:bg-zinc-900 ${
|
||||
index > 1 && 'hidden md:block'
|
||||
}`}
|
||||
onClick={async () => {
|
||||
setMessages(currentMessages => [
|
||||
...currentMessages,
|
||||
{
|
||||
id: nanoid(),
|
||||
display: <UserMessage>{example.message}</UserMessage>
|
||||
}
|
||||
])
|
||||
|
||||
const responseMessage = await submitUserMessage(
|
||||
example.message
|
||||
)
|
||||
|
||||
setMessages(currentMessages => [
|
||||
...currentMessages,
|
||||
responseMessage
|
||||
])
|
||||
}}
|
||||
>
|
||||
<div className="text-sm font-semibold">{example.heading}</div>
|
||||
<div className="text-sm text-zinc-600">
|
||||
{example.subheading}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
))}
|
||||
</div>
|
||||
<div className="px-4 py-2 space-y-4 border-t shadow-lg bg-background sm:rounded-t-xl sm:border md:py-4">
|
||||
<PromptForm
|
||||
onSubmit={async value => {
|
||||
await append({
|
||||
id,
|
||||
content: value,
|
||||
role: 'user'
|
||||
})
|
||||
}}
|
||||
input={input}
|
||||
setInput={setInput}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
|
||||
{messages?.length >= 2 ? (
|
||||
<div className="flex h-12 items-center justify-center">
|
||||
<div className="flex space-x-2">
|
||||
{id && title ? (
|
||||
<>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setShareDialogOpen(true)}
|
||||
>
|
||||
<IconShare className="mr-2" />
|
||||
Share
|
||||
</Button>
|
||||
<ChatShareDialog
|
||||
open={shareDialogOpen}
|
||||
onOpenChange={setShareDialogOpen}
|
||||
onCopy={() => setShareDialogOpen(false)}
|
||||
shareChat={shareChat}
|
||||
chat={{
|
||||
id,
|
||||
title,
|
||||
messages: aiState.messages
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<div className="space-y-4 border-t bg-background px-4 py-2 shadow-lg sm:rounded-t-xl sm:border md:py-4">
|
||||
<PromptForm input={input} setInput={setInput} />
|
||||
<FooterText className="hidden sm:block" />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue