11"use client" ;
22
3- import {
4- useChat ,
5- useDeepThinking ,
6- type Reaction ,
7- type Source ,
8- } from "@kapaai/react-sdk" ;
3+ import { useChat , useDeepThinking , type Reaction , type Source } from "@kapaai/react-sdk" ;
94import {
105 AlertCircleIcon ,
116 ChevronRightIcon ,
@@ -20,11 +15,7 @@ import { useEffect, useState } from "react";
2015import { createPortal } from "react-dom" ;
2116import { cn } from "@prisma-docs/ui/lib/cn" ;
2217import { buttonVariants } from "@/components/ui/button" ;
23- import {
24- Tooltip ,
25- TooltipContent ,
26- TooltipTrigger ,
27- } from "@prisma-docs/ui/components/tooltip" ;
18+ import { Tooltip , TooltipContent , TooltipTrigger } from "@prisma-docs/ui/components/tooltip" ;
2819import {
2920 Drawer ,
3021 DrawerContent ,
@@ -46,28 +37,17 @@ import {
4637 ConversationContent ,
4738 ConversationScrollButton ,
4839} from "@/components/ai-elements/conversation" ;
49- import {
50- Message ,
51- MessageContent ,
52- MessageResponseMarkdown ,
53- } from "@/components/ai-elements/message" ;
40+ import { Message , MessageContent , MessageResponseMarkdown } from "@/components/ai-elements/message" ;
5441import { Shimmer } from "@/components/ai-elements/shimmer" ;
5542import { Spinner } from "@/components/ai-elements/spinner" ;
56- import {
57- PromptInput ,
58- PromptInputFooter ,
59- } from "@/components/ai-elements/prompt-input" ;
43+ import { PromptInput , PromptInputFooter } from "@/components/ai-elements/prompt-input" ;
6044import { CopyChat } from "@/components/ai-elements/copy-chat" ;
6145
6246interface AIChatSidebarProps {
6347 exampleQuestions ?: string [ ] ;
6448}
6549
66- const SourcesDisplay = ( {
67- sources,
68- } : {
69- sources : ( Source | StoredSource ) [ ] ;
70- } ) => {
50+ const SourcesDisplay = ( { sources } : { sources : ( Source | StoredSource ) [ ] } ) => {
7151 const [ showAll , setShowAll ] = useState ( false ) ;
7252 const displayedSources = showAll ? sources : sources . slice ( 0 , 3 ) ;
7353 const hasMore = sources . length > 3 ;
@@ -132,14 +112,11 @@ const FeedbackButtons = ({
132112 "h-7 w-7" ,
133113 currentReaction === "upvote" &&
134114 "text-green-600 dark:text-green-400 bg-green-50 dark:bg-green-950" ,
135- disabled && "opacity-50 cursor-not-allowed"
115+ disabled && "opacity-50 cursor-not-allowed" ,
136116 ) }
137117 >
138118 < ThumbsUpIcon
139- className = { cn (
140- "size-3.5" ,
141- currentReaction === "upvote" && "fill-current"
142- ) }
119+ className = { cn ( "size-3.5" , currentReaction === "upvote" && "fill-current" ) }
143120 />
144121 </ button >
145122 ) }
@@ -159,14 +136,11 @@ const FeedbackButtons = ({
159136 "h-7 w-7" ,
160137 currentReaction === "downvote" &&
161138 "text-red-600 dark:text-red-400 bg-red-50 dark:bg-red-950" ,
162- disabled && "opacity-50 cursor-not-allowed"
139+ disabled && "opacity-50 cursor-not-allowed" ,
163140 ) }
164141 >
165142 < ThumbsDownIcon
166- className = { cn (
167- "size-3.5" ,
168- currentReaction === "downvote" && "fill-current"
169- ) }
143+ className = { cn ( "size-3.5" , currentReaction === "downvote" && "fill-current" ) }
170144 />
171145 </ button >
172146 ) }
@@ -185,6 +159,7 @@ const ChatInner = ({
185159 onClose : ( ) => void ;
186160} ) => {
187161 const [ inputValue , setInputValue ] = useState ( "" ) ;
162+ const { pendingMessage, setPendingMessage } = useAIChatContext ( ) ;
188163 const {
189164 initialMessages,
190165 isLoading : isPersistenceLoading ,
@@ -205,6 +180,14 @@ const ChatInner = ({
205180
206181 const deepThinking = useDeepThinking ( ) ;
207182
183+ // Auto-submit pending message from the floating input
184+ useEffect ( ( ) => {
185+ if ( pendingMessage && ! isGeneratingAnswer ) {
186+ submitQuery ( pendingMessage ) ;
187+ setPendingMessage ( "" ) ;
188+ }
189+ } , [ pendingMessage , isGeneratingAnswer , submitQuery , setPendingMessage ] ) ;
190+
208191 useEffect ( ( ) => {
209192 if ( conversation . length === 0 ) return ;
210193
@@ -258,9 +241,7 @@ const ChatInner = ({
258241 } ;
259242
260243 const messagesForCopy : ChatMessage [ ] = conversation . flatMap ( ( qa ) => {
261- const msgs : ChatMessage [ ] = [
262- { id : `${ qa . id } -user` , role : "user" , content : qa . question } ,
263- ] ;
244+ const msgs : ChatMessage [ ] = [ { id : `${ qa . id } -user` , role : "user" , content : qa . question } ] ;
264245 if ( qa . answer ) {
265246 msgs . push ( {
266247 id : `${ qa . id } -assistant` ,
@@ -273,30 +254,24 @@ const ChatInner = ({
273254
274255 const hasActiveConversation = conversation . length > 0 ;
275256 const hasPersistedMessages = initialMessages . length > 0 ;
276- const showEmptyState =
277- ! hasActiveConversation && ! hasPersistedMessages && ! isPersistenceLoading ;
257+ const showEmptyState = ! hasActiveConversation && ! hasPersistedMessages && ! isPersistenceLoading ;
278258
279259 return (
280260 < div className = "flex size-full w-full flex-col overflow-hidden bg-fd-background" >
281261 < div className = "flex items-center justify-between px-4 py-2.5 border-b shrink-0" >
282262 < h2 className = "font-semibold text-sm" > Chat</ h2 >
283263 < div className = "flex items-center gap-1" >
284- < CopyChat
285- messages = { hasActiveConversation ? messagesForCopy : initialMessages }
286- />
264+ < CopyChat messages = { hasActiveConversation ? messagesForCopy : initialMessages } />
287265 < Tooltip >
288266 < TooltipTrigger
289267 render = { ( props ) => (
290268 < button
291269 { ...props }
292270 onClick = { handleClearChat }
293- disabled = {
294- ( ! hasActiveConversation && ! hasPersistedMessages ) ||
295- isGeneratingAnswer
296- }
271+ disabled = { ( ! hasActiveConversation && ! hasPersistedMessages ) || isGeneratingAnswer }
297272 className = { cn (
298273 buttonVariants ( { variant : "ghost" , size : "icon-sm" } ) ,
299- "disabled:opacity-50"
274+ "disabled:opacity-50" ,
300275 ) }
301276 >
302277 < Trash2 className = "size-3.5" />
@@ -311,9 +286,7 @@ const ChatInner = ({
311286 < button
312287 { ...props }
313288 onClick = { onClose }
314- className = { cn (
315- buttonVariants ( { variant : "ghost" , size : "icon-sm" } )
316- ) }
289+ className = { cn ( buttonVariants ( { variant : "ghost" , size : "icon-sm" } ) ) }
317290 >
318291 < ChevronRightIcon className = "size-3.5" />
319292 </ button >
@@ -333,9 +306,7 @@ const ChatInner = ({
333306 < MessagesSquareIcon className = "size-6 text-fd-primary" />
334307 </ div >
335308 < div >
336- < h3 className = "font-semibold text-fd-foreground" >
337- How can I help?
338- </ h3 >
309+ < h3 className = "font-semibold text-fd-foreground" > How can I help?</ h3 >
339310 < p className = "text-sm text-fd-muted-foreground mt-1" >
340311 Ask me anything about Prisma
341312 </ p >
@@ -373,8 +344,7 @@ const ChatInner = ({
373344 < >
374345 { conversation . map ( ( qa , index ) => {
375346 const isLastItem = index === conversation . length - 1 ;
376- const isLoading =
377- isLastItem && ( isGeneratingAnswer || isPreparingAnswer ) ;
347+ const isLoading = isLastItem && ( isGeneratingAnswer || isPreparingAnswer ) ;
378348 const hasAnswer = ! ! qa . answer ;
379349 const isComplete = qa . id && ! isLoading ;
380350
@@ -387,9 +357,7 @@ const ChatInner = ({
387357 < Message from = "assistant" >
388358 < MessageContent >
389359 { hasAnswer ? (
390- < MessageResponseMarkdown >
391- { qa . answer }
392- </ MessageResponseMarkdown >
360+ < MessageResponseMarkdown > { qa . answer } </ MessageResponseMarkdown >
393361 ) : isLoading ? (
394362 < div className = "flex items-center gap-2 overflow-hidden" >
395363 < Spinner />
@@ -443,18 +411,14 @@ const ChatInner = ({
443411 < Message key = { message . id } from = { message . role } >
444412 < MessageContent >
445413 { message . role === "assistant" ? (
446- < MessageResponseMarkdown >
447- { message . content }
448- </ MessageResponseMarkdown >
414+ < MessageResponseMarkdown > { message . content } </ MessageResponseMarkdown >
449415 ) : (
450416 message . content
451417 ) }
452418 </ MessageContent >
453419 { message . role === "assistant" &&
454420 message . sources &&
455- message . sources . length > 0 && (
456- < SourcesDisplay sources = { message . sources } />
457- ) }
421+ message . sources . length > 0 && < SourcesDisplay sources = { message . sources } /> }
458422 </ Message >
459423 ) ) }
460424 </ >
@@ -523,14 +487,18 @@ export const AIChatSidebar = ({
523487 onClick = { ( ) => setIsOpen ( ! isOpen ) }
524488 className = { cn (
525489 buttonVariants ( { variant : "outline" } ) ,
526- "hidden shrink-0 shadow-none md:inline-flex items-center gap-2 h-8 group cursor-pointer"
490+ "hidden shrink-0 shadow-none md:inline-flex items-center gap-2 h-8 group cursor-pointer" ,
527491 ) }
528492 >
529493 < MessagesSquareIcon className = "size-4 text-fd-muted-foreground group-hover:text-fd-accent-foreground" />
530494 < span > Ask AI</ span >
531495 < div className = "ms-auto inline-flex gap-0.5" >
532- < kbd className = "rounded-md border bg-fd-background px-1.5 text-fd-muted-foreground group-hover:text-fd-accent-foreground" > { isMac ? "⌘" : "Ctrl" } </ kbd >
533- < kbd className = "rounded-md border bg-fd-background px-1.5 text-fd-muted-foreground group-hover:text-fd-accent-foreground" > I</ kbd >
496+ < kbd className = "rounded-md border bg-fd-background px-1.5 text-fd-muted-foreground group-hover:text-fd-accent-foreground" >
497+ { isMac ? "⌘" : "Ctrl" }
498+ </ kbd >
499+ < kbd className = "rounded-md border bg-fd-background px-1.5 text-fd-muted-foreground group-hover:text-fd-accent-foreground" >
500+ I
501+ </ kbd >
534502 </ div >
535503 </ button >
536504
@@ -542,28 +510,22 @@ export const AIChatSidebar = ({
542510 "inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-[500px]" ,
543511 "translate-x-full data-[state=open]:translate-x-0" ,
544512 "pointer-events-none data-[state=open]:pointer-events-auto" ,
545- "hidden md:flex"
513+ "hidden md:flex" ,
546514 ) }
547515 data-state = { isOpen ? "open" : "closed" }
548516 >
549- < ChatInner
550- exampleQuestions = { exampleQuestions }
551- onClose = { ( ) => setIsOpen ( false ) }
552- />
517+ < ChatInner exampleQuestions = { exampleQuestions } onClose = { ( ) => setIsOpen ( false ) } />
553518 </ div > ,
554- document . body
519+ document . body ,
555520 ) }
556521
557522 < div className = "md:hidden" >
558- < Drawer
559- open = { isMobile ? isOpen : false }
560- onOpenChange = { isMobile ? setIsOpen : undefined }
561- >
523+ < Drawer open = { isMobile ? isOpen : false } onOpenChange = { isMobile ? setIsOpen : undefined } >
562524 < DrawerTrigger asChild >
563525 < button
564526 className = { cn (
565527 buttonVariants ( { variant : "outline" , size : "sm" } ) ,
566- "shadow-none inline-flex items-center gap-1.5"
528+ "shadow-none inline-flex items-center gap-1.5" ,
567529 ) }
568530 >
569531 < MessagesSquareIcon className = "size-3.5 text-fd-muted-foreground" />
@@ -575,10 +537,7 @@ export const AIChatSidebar = ({
575537 < DrawerTitle > AI Chat</ DrawerTitle >
576538 < DrawerDescription > Ask questions about Prisma</ DrawerDescription >
577539 </ DrawerHeader >
578- < ChatInner
579- exampleQuestions = { exampleQuestions }
580- onClose = { ( ) => setIsOpen ( false ) }
581- />
540+ < ChatInner exampleQuestions = { exampleQuestions } onClose = { ( ) => setIsOpen ( false ) } />
582541 </ DrawerContent >
583542 </ Drawer >
584543 </ div >
0 commit comments