diff --git a/src/components/ai-elements/message.tsx b/src/components/ai-elements/message.tsx index 8eaf6f8..ac6dcb8 100644 --- a/src/components/ai-elements/message.tsx +++ b/src/components/ai-elements/message.tsx @@ -35,11 +35,12 @@ export const MessageContent = ({ 'flex flex-col gap-2 overflow-hidden rounded-lg px-4 py-3 text-foreground text-sm', 'group-[.is-user]:bg-primary group-[.is-user]:text-primary-foreground', 'group-[.is-assistant]:bg-secondary group-[.is-assistant]:text-foreground', + 'is-user:dark', className )} {...props} > -
{children}
+ {children} ); @@ -54,10 +55,7 @@ export const MessageAvatar = ({ className, ...props }: MessageAvatarProps) => ( - + {name?.slice(0, 2) || 'ME'} diff --git a/src/components/ai-elements/reasoning.tsx b/src/components/ai-elements/reasoning.tsx index 2c6e6c7..021974f 100644 --- a/src/components/ai-elements/reasoning.tsx +++ b/src/components/ai-elements/reasoning.tsx @@ -38,13 +38,14 @@ export type ReasoningProps = ComponentProps & { }; const AUTO_CLOSE_DELAY = 1000; +const MS_IN_S = 1000; export const Reasoning = memo( ({ className, isStreaming = false, open, - defaultOpen = false, + defaultOpen = true, onOpenChange, duration: durationProp, children, @@ -70,23 +71,22 @@ export const Reasoning = memo( setStartTime(Date.now()); } } else if (startTime !== null) { - setDuration(Math.round((Date.now() - startTime) / 1000)); + setDuration(Math.round((Date.now() - startTime) / MS_IN_S)); setStartTime(null); } }, [isStreaming, startTime, setDuration]); // Auto-open when streaming starts, auto-close when streaming ends (once only) useEffect(() => { - if (isStreaming && !isOpen) { - setIsOpen(true); - } else if (!isStreaming && isOpen && !defaultOpen && !hasAutoClosedRef) { - // Add a small delay before closing to allow user to see the content - const timer = setTimeout(() => { - setIsOpen(false); - setHasAutoClosedRef(true); - }, AUTO_CLOSE_DELAY); - return () => clearTimeout(timer); - } + if (defaultOpen && !isStreaming && isOpen && !hasAutoClosedRef) { + // Add a small delay before closing to allow user to see the content + const timer = setTimeout(() => { + setIsOpen(false); + setHasAutoClosedRef(true); + }, AUTO_CLOSE_DELAY); + + return () => clearTimeout(timer); + } }, [isStreaming, isOpen, defaultOpen, setIsOpen, hasAutoClosedRef]); const handleOpenChange = (newOpen: boolean) => { @@ -110,19 +110,10 @@ export const Reasoning = memo( } ); -export type ReasoningTriggerProps = ComponentProps< - typeof CollapsibleTrigger -> & { - title?: string; -}; +export type ReasoningTriggerProps = ComponentProps; export const ReasoningTrigger = memo( - ({ - className, - title = 'Reasoning', - children, - ...props - }: ReasoningTriggerProps) => { + ({ className, children, ...props }: ReasoningTriggerProps) => { const { isStreaming, isOpen, duration } = useReasoning(); return ( diff --git a/src/components/ai-elements/sources.tsx b/src/components/ai-elements/sources.tsx new file mode 100644 index 0000000..bd4caa0 --- /dev/null +++ b/src/components/ai-elements/sources.tsx @@ -0,0 +1,74 @@ +'use client'; + +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from '@/components/ui/collapsible'; +import { cn } from '@/lib/utils'; +import { BookIcon, ChevronDownIcon } from 'lucide-react'; +import type { ComponentProps } from 'react'; + +export type SourcesProps = ComponentProps<'div'>; + +export const Sources = ({ className, ...props }: SourcesProps) => ( + +); + +export type SourcesTriggerProps = ComponentProps & { + count: number; +}; + +export const SourcesTrigger = ({ + className, + count, + children, + ...props +}: SourcesTriggerProps) => ( + + {children ?? ( + <> +

Used {count} sources

+ + + )} +
+); + +export type SourcesContentProps = ComponentProps; + +export const SourcesContent = ({ + className, + ...props +}: SourcesContentProps) => ( + +); + +export type SourceProps = ComponentProps<'a'>; + +export const Source = ({ href, title, children, ...props }: SourceProps) => ( + + {children ?? ( + <> + + {title} + + )} + +); diff --git a/src/components/ai-elements/tool.tsx b/src/components/ai-elements/tool.tsx index c530727..5de574d 100644 --- a/src/components/ai-elements/tool.tsx +++ b/src/components/ai-elements/tool.tsx @@ -50,7 +50,7 @@ const getStatusBadge = (status: ToolUIPart['state']) => { } as const; return ( - + {icons[status]} {labels[status]}