11"use client" ;
22
33import { Button } from "@/components/ui/button" ;
4+ import { Collapsible , CollapsibleContent , CollapsibleTrigger } from "@/components/ui/collapsible" ;
45import { Sheet , SheetContent , SheetDescription , SheetHeader , SheetTitle , SheetTrigger } from "@/components/ui/sheet" ;
56import { useChat } from "@ai-sdk/react" ;
67import { DefaultChatTransport } from "ai" ;
7- import { Bot , Loader2 , Send , Sparkles , User } from "lucide-react" ;
8+ import { Bot , ChevronDown , ChevronUp , Loader2 , Send , Sparkles , User } from "lucide-react" ;
89import { useEffect , useRef , useState } from "react" ;
910import { Streamdown } from "streamdown" ;
1011
@@ -15,8 +16,16 @@ export interface AIAssistantProps {
1516export function AIAssistant ( { questionId } : AIAssistantProps ) {
1617 const [ open , setOpen ] = useState ( false ) ;
1718 const [ input , setInput ] = useState ( "" ) ;
19+ const [ reasoningOpenMap , setReasoningOpenMap ] = useState < Record < string , boolean > > ( { } ) ;
1820 const messagesEndRef = useRef < HTMLDivElement > ( null ) ;
1921
22+ const toggleReasoning = ( messageId : string ) => {
23+ setReasoningOpenMap ( ( prev ) => ( {
24+ ...prev ,
25+ [ messageId ] : ! prev [ messageId ] ,
26+ } ) ) ;
27+ } ;
28+
2029 const { messages, sendMessage, status } = useChat ( {
2130 transport : new DefaultChatTransport ( {
2231 api : "/api/chat" ,
@@ -182,6 +191,61 @@ export function AIAssistant({ questionId }: AIAssistantProps) {
182191 ) ;
183192 }
184193
194+ if ( part . type === "reasoning" ) {
195+ return (
196+ < Collapsible
197+ key = { `${ message . id } -reasoning-${ index } ` }
198+ open = { reasoningOpenMap [ `${ message . id } -${ index } ` ] }
199+ onOpenChange = { ( ) => toggleReasoning ( `${ message . id } -${ index } ` ) }
200+ className = "border-l-2 border-primary/30 pl-3"
201+ >
202+ < CollapsibleTrigger asChild >
203+ < Button
204+ variant = "ghost"
205+ size = "sm"
206+ className = { `
207+ flex w-full items-center justify-between p-0
208+ text-xs
209+ hover:bg-transparent
210+ ` }
211+ >
212+ < span
213+ className = { `
214+ flex items-center gap-1.5 text-muted-foreground
215+ ` }
216+ >
217+ < Sparkles className = "h-3 w-3" />
218+ 思考過程
219+ </ span >
220+ { reasoningOpenMap [ `${ message . id } -${ index } ` ]
221+ ? (
222+ < ChevronUp
223+ className = { `h-3 w-3 text-muted-foreground` }
224+ />
225+ )
226+ : (
227+ < ChevronDown
228+ className = { `h-3 w-3 text-muted-foreground` }
229+ />
230+ ) }
231+ </ Button >
232+ </ CollapsibleTrigger >
233+ < CollapsibleContent className = "mt-2" >
234+ < div
235+ className = { `
236+ text-xs leading-relaxed whitespace-pre-wrap
237+ text-muted-foreground
238+ ` }
239+ >
240+ < Streamdown >
241+ { part . text }
242+ </ Streamdown >
243+ </ div >
244+ </ CollapsibleContent >
245+ </ Collapsible >
246+ ) ;
247+ }
248+
185249 if ( part . type . startsWith ( "tool-" ) ) {
186250 return (
187251 < div
0 commit comments