Skip to content

Commit

Permalink
Merge pull request #51 from miurla/collapsible
Browse files Browse the repository at this point in the history
Add collapsible feature
  • Loading branch information
miurla authored Apr 18, 2024
2 parents 48537af + 2f0a641 commit e4dd951
Show file tree
Hide file tree
Showing 9 changed files with 111 additions and 11 deletions.
15 changes: 11 additions & 4 deletions app/action.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ async function submit(formData?: FormData, skip?: boolean) {
const aiState = getMutableAIState<typeof AI>()
const uiStream = createStreamableUI()
const isGenerating = createStreamableValue(true)
const isCollapsed = createStreamableValue(false)

const messages: ExperimentalMessage[] = aiState.get() as any
// Limit the number of messages to 14
messages.splice(0, Math.max(messages.length - 14, 0))
// Limit the number of messages to 10
messages.splice(0, Math.max(messages.length - 10, 0))
// Get the user input from the form data
const userInput = skip
? `{"action": "skip"}`
Expand Down Expand Up @@ -50,13 +51,17 @@ async function submit(formData?: FormData, skip?: boolean) {

uiStream.done()
isGenerating.done()
isCollapsed.done(false)
aiState.done([
...aiState.get(),
{ role: 'assistant', content: `inquiry: ${inquiry?.question}` }
])
return
}

// Set the collapsed state to true
isCollapsed.done(true)

// Generate the answer
let answer = ''
let errorOccurred = false
Expand Down Expand Up @@ -95,7 +100,8 @@ async function submit(formData?: FormData, skip?: boolean) {
return {
id: Date.now(),
isGenerating: isGenerating.value,
component: uiStream.value
component: uiStream.value,
isCollapsed: isCollapsed.value
}
}

Expand All @@ -110,7 +116,8 @@ const initialAIState: {
// The initial UI state that the client will keep track of, which contains the message IDs and their UI nodes.
const initialUIState: {
id: number
isGenerating: StreamableValue<boolean>
isGenerating?: StreamableValue<boolean>
isCollapsed?: StreamableValue<boolean>
component: React.ReactNode
}[] = []

Expand Down
Binary file modified bun.lockb
Binary file not shown.
19 changes: 15 additions & 4 deletions components/chat-messages.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
import { useUIState } from 'ai/rsc'
import { StreamableValue, useUIState } from 'ai/rsc'
import type { AI } from '@/app/action'
import { CollapsibleMessage } from './collapsible-message'

export function ChatMessages() {
const [messages, setMessages] = useUIState<typeof AI>()

return (
<>
{messages.map((message: { id: number; component: React.ReactNode }) => (
<div key={message.id}>{message.component}</div>
))}
{messages.map(
(message: {
id: number
component: React.ReactNode
isCollapsed?: StreamableValue<boolean>
}) => (
<CollapsibleMessage
key={message.id}
message={message}
isLastMessage={message.id === messages[messages.length - 1].id}
/>
)
)}
</>
)
}
1 change: 0 additions & 1 deletion components/chat-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ export function ChatPanel() {
...currentMessages,
{
id: Date.now(),
isGenerating: false,
component: <UserMessage message={input} />
}
])
Expand Down
72 changes: 72 additions & 0 deletions components/collapsible-message.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import React, { useEffect, useState } from 'react'
import {
Collapsible,
CollapsibleTrigger,
CollapsibleContent
} from '@radix-ui/react-collapsible'
import { Button } from './ui/button'
import { ChevronDown } from 'lucide-react'
import { StreamableValue, useStreamableValue } from 'ai/rsc'
import { cn } from '@/lib/utils'
import { Separator } from './ui/separator'

interface CollapsibleMessageProps {
message: {
id: number
isCollapsed?: StreamableValue<boolean>
component: React.ReactNode
}
isLastMessage?: boolean
}

export const CollapsibleMessage: React.FC<CollapsibleMessageProps> = ({
message,
isLastMessage = false
}) => {
const [data] = useStreamableValue(message.isCollapsed)
const isCollapsed = data ?? false
const [open, setOpen] = useState(isLastMessage)

useEffect(() => {
setOpen(isLastMessage)
}, [isCollapsed, isLastMessage])

// if not collapsed, return the component
if (!isCollapsed) {
return message.component
}

return (
<Collapsible
open={open}
onOpenChange={value => {
setOpen(value)
}}
>
<CollapsibleTrigger asChild>
<div
className={cn(
'w-full flex justify-end',
!isCollapsed ? 'hidden' : ''
)}
>
<Button
variant="ghost"
size={'icon'}
className={cn('-mt-3 rounded-full')}
>
<ChevronDown
className={cn(
open ? 'rotate-180' : 'rotate-0',
'h-4 w-4 transition-all'
)}
/>
<span className="sr-only">collapse</span>
</Button>
</div>
</CollapsibleTrigger>
<CollapsibleContent>{message.component}</CollapsibleContent>
{!open && <Separator className="my-2 bg-muted" />}
</Collapsible>
)
}
1 change: 0 additions & 1 deletion components/search-related.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ export const SearchRelated: React.FC<SearchRelatedProps> = ({

const userMessage = {
id: Date.now(),
isGenerating: false,
component: <UserMessage message={query} isFirstMessage={false} />
}

Expand Down
11 changes: 11 additions & 0 deletions components/ui/collapsible.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"use client"

import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"

const Collapsible = CollapsiblePrimitive.Root

const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger

const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent

export { Collapsible, CollapsibleTrigger, CollapsibleContent }
2 changes: 1 addition & 1 deletion components/user-message.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const UserMessage: React.FC<UserMessageProps> = ({
isFirstMessage
}) => {
return (
<div className={cn({ 'pt-4': !isFirstMessage })}>
<div className={cn({ 'mt-4': !isFirstMessage })}>
<div className="text-xl">{message}</div>
</div>
)
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"@ai-sdk/openai": "^0.0.2",
"@radix-ui/react-avatar": "^1.0.4",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-collapsible": "^1.0.3",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-label": "^2.0.2",
Expand Down

0 comments on commit e4dd951

Please # to comment.