From 9f5d4aec59b845b56f34f18aa99abbda438cf2b6 Mon Sep 17 00:00:00 2001 From: javayhu Date: Mon, 1 Sep 2025 23:44:18 +0800 Subject: [PATCH 1/3] chore: update ai elements --- src/components/ai-elements/message.tsx | 8 +-- src/components/ai-elements/reasoning.tsx | 37 +++++------- src/components/ai-elements/sources.tsx | 74 ++++++++++++++++++++++++ src/components/ai-elements/tool.tsx | 2 +- 4 files changed, 92 insertions(+), 29 deletions(-) create mode 100644 src/components/ai-elements/sources.tsx 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]} From f36018945de4d1c4f46fb87018b8d16e03a8664c Mon Sep 17 00:00:00 2001 From: javayhu Date: Mon, 1 Sep 2025 23:47:31 +0800 Subject: [PATCH 2/3] fix: change message component background color --- src/ai/chat/components/ChatBot.tsx | 2 +- src/components/ai-elements/message.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ai/chat/components/ChatBot.tsx b/src/ai/chat/components/ChatBot.tsx index 9ac8aac..6c71af6 100644 --- a/src/ai/chat/components/ChatBot.tsx +++ b/src/ai/chat/components/ChatBot.tsx @@ -70,7 +70,7 @@ export default function ChatBot() { }; return ( -
+
diff --git a/src/components/ai-elements/message.tsx b/src/components/ai-elements/message.tsx index ac6dcb8..dc56af7 100644 --- a/src/components/ai-elements/message.tsx +++ b/src/components/ai-elements/message.tsx @@ -34,7 +34,7 @@ export const MessageContent = ({ className={cn( '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', + 'group-[.is-assistant]:bg-card group-[.is-assistant]:text-card-foreground', 'is-user:dark', className )} From 3707500ed88e302a3ec97912c81d0d8dbb7c3a74 Mon Sep 17 00:00:00 2001 From: javayhu Date: Tue, 2 Sep 2025 00:16:10 +0800 Subject: [PATCH 3/3] feat: optimize fetching subscription period start and end time --- src/payment/provider/stripe.ts | 39 +++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/src/payment/provider/stripe.ts b/src/payment/provider/stripe.ts index 866fb60..944a7a6 100644 --- a/src/payment/provider/stripe.ts +++ b/src/payment/provider/stripe.ts @@ -537,6 +537,9 @@ export class StripeProvider implements PaymentProvider { return; } + const periodStart = this.getPeriodStart(stripeSubscription); + const periodEnd = this.getPeriodEnd(stripeSubscription); + // create fields const createFields: any = { id: randomUUID(), @@ -549,12 +552,8 @@ export class StripeProvider implements PaymentProvider { status: this.mapSubscriptionStatusToPaymentStatus( stripeSubscription.status ), - periodStart: stripeSubscription.current_period_start - ? new Date(stripeSubscription.current_period_start * 1000) - : null, - periodEnd: stripeSubscription.current_period_end - ? new Date(stripeSubscription.current_period_end * 1000) - : null, + periodStart: periodStart, + periodEnd: periodEnd, cancelAtPeriodEnd: stripeSubscription.cancel_at_period_end, trialStart: stripeSubscription.trial_start ? new Date(stripeSubscription.trial_start * 1000) @@ -614,12 +613,8 @@ export class StripeProvider implements PaymentProvider { .limit(1); // get new period start and end - const newPeriodStart = stripeSubscription.current_period_start - ? new Date(stripeSubscription.current_period_start * 1000) - : undefined; - const newPeriodEnd = stripeSubscription.current_period_end - ? new Date(stripeSubscription.current_period_end * 1000) - : undefined; + const newPeriodStart = this.getPeriodStart(stripeSubscription); + const newPeriodEnd = this.getPeriodEnd(stripeSubscription); // Check if this is a renewal (period has changed and subscription is active) const isRenewal = @@ -972,4 +967,24 @@ export class StripeProvider implements PaymentProvider { // Default to auto to let Stripe detect the language return 'auto'; } + + private getPeriodStart(subscription: Stripe.Subscription): Date | undefined { + const s: any = subscription as any; + const startUnix = + s.current_period_start ?? + s?.items?.data?.[0]?.current_period_start ?? + undefined; + return typeof startUnix === 'number' + ? new Date(startUnix * 1000) + : undefined; + } + + private getPeriodEnd(subscription: Stripe.Subscription): Date | undefined { + const s: any = subscription as any; + const endUnix = + s.current_period_end ?? + s?.items?.data?.[0]?.current_period_end ?? + undefined; + return typeof endUnix === 'number' ? new Date(endUnix * 1000) : undefined; + } }