From f3fd7267e526278f631eef9596dbc5823632f37b Mon Sep 17 00:00:00 2001 From: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com> Date: Mon, 10 Nov 2025 02:09:41 -0500 Subject: [PATCH 1/3] Add counter-clockwise spinning animation to plant icon during token generation --- components/chat.tsx | 17 +++++++++++------ components/header.tsx | 13 ++++++++++++- components/message.tsx | 7 +++++++ components/streaming-context.tsx | 28 ++++++++++++++++++++++++++++ tailwind.config.ts | 5 +++++ 5 files changed, 63 insertions(+), 7 deletions(-) create mode 100644 components/streaming-context.tsx diff --git a/components/chat.tsx b/components/chat.tsx index 2a775600..5a074042 100644 --- a/components/chat.tsx +++ b/components/chat.tsx @@ -16,6 +16,7 @@ import { MapDataProvider, useMapData } from './map/map-data-context'; // Add thi import { updateDrawingContext } from '@/lib/actions/chat'; // Import the server action import dynamic from 'next/dynamic' import { HeaderSearchButton } from './header-search-button' +import { StreamingProvider } from './streaming-context' type ChatProps = { id?: string // This is the chatId @@ -84,8 +85,9 @@ export function Chat({ id }: ChatProps) { // Mobile layout if (isMobile) { return ( - {/* Add Provider */} - + + {/* Add Provider */} +
{activeView ? : } @@ -110,14 +112,16 @@ export function Chat({ id }: ChatProps) { )}
-
+
+ ); } // Desktop layout return ( - {/* Add Provider */} - + + {/* Add Provider */} +
{/* This is the new div for scrolling */}
@@ -145,6 +149,7 @@ export function Chat({ id }: ChatProps) { {activeView ? : }
-
+
+ ); } diff --git a/components/header.tsx b/components/header.tsx index 5b5c564a..3809a78b 100644 --- a/components/header.tsx +++ b/components/header.tsx @@ -2,6 +2,7 @@ import React from 'react' import Image from 'next/image' import { useCalendarToggle } from './calendar-toggle-context' +import { useStreaming } from './streaming-context' import { ModeToggle } from './mode-toggle' import { cn } from '@/lib/utils' import HistoryContainer from './history-container' @@ -18,6 +19,7 @@ import { ProfileToggle } from './profile-toggle' export const Header = () => { const { toggleCalendar } = useCalendarToggle() + const { isStreaming } = useStreaming() return (
@@ -28,7 +30,16 @@ export const Header = () => {
diff --git a/components/message.tsx b/components/message.tsx index 264aa1f6..127e5fb1 100644 --- a/components/message.tsx +++ b/components/message.tsx @@ -7,9 +7,16 @@ import remarkGfm from 'remark-gfm' import remarkMath from 'remark-math' import rehypeKatex from 'rehype-katex' import 'katex/dist/katex.min.css' +import { useStreaming } from './streaming-context' +import { useEffect } from 'react' export function BotMessage({ content }: { content: StreamableValue }) { const [data, error, pending] = useStreamableValue(content) + const { setIsStreaming } = useStreaming() + + useEffect(() => { + setIsStreaming(pending) + }, [pending, setIsStreaming]) // Currently, sometimes error occurs after finishing the stream. if (error) return
Error
diff --git a/components/streaming-context.tsx b/components/streaming-context.tsx new file mode 100644 index 00000000..feb8536a --- /dev/null +++ b/components/streaming-context.tsx @@ -0,0 +1,28 @@ +'use client' + +import { createContext, useContext, useState, ReactNode } from 'react' + +interface StreamingContextType { + isStreaming: boolean + setIsStreaming: (streaming: boolean) => void +} + +const StreamingContext = createContext(undefined) + +export const useStreaming = () => { + const context = useContext(StreamingContext) + if (!context) { + throw new Error('useStreaming must be used within a StreamingProvider') + } + return context +} + +export const StreamingProvider = ({ children }: { children: ReactNode }) => { + const [isStreaming, setIsStreaming] = useState(false) + + return ( + + {children} + + ) +} diff --git a/tailwind.config.ts b/tailwind.config.ts index 68c971ee..8b2af9e8 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -68,10 +68,15 @@ const config = { from: { height: "var(--radix-accordion-content-height)" }, to: { height: "0" }, }, + "spin-ccw": { + from: { transform: "rotate(0deg)" }, + to: { transform: "rotate(-360deg)" }, + }, }, animation: { "accordion-down": "accordion-down 0.2s ease-out", "accordion-up": "accordion-up 0.2s ease-out", + "spin-ccw": "spin-ccw 2s linear infinite", }, fontFamily: { sans: ["var(--font-sans)", ...fontFamily.sans], From 5ab27d74230d3a59cd6b12dbadb7d4edabf9250b Mon Sep 17 00:00:00 2001 From: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com> Date: Thu, 13 Nov 2025 04:27:51 -0500 Subject: [PATCH 2/3] Fix SSR build error by providing default values in useStreaming hook --- components/streaming-context.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/streaming-context.tsx b/components/streaming-context.tsx index feb8536a..4d900505 100644 --- a/components/streaming-context.tsx +++ b/components/streaming-context.tsx @@ -12,7 +12,8 @@ const StreamingContext = createContext(undefin export const useStreaming = () => { const context = useContext(StreamingContext) if (!context) { - throw new Error('useStreaming must be used within a StreamingProvider') + // Return default values if used outside provider (e.g., during SSR) + return { isStreaming: false, setIsStreaming: () => {} } } return context } From d33cd2fbea56934fa3eee4801ef21fc86f2e6775 Mon Sep 17 00:00:00 2001 From: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com> Date: Mon, 17 Nov 2025 06:22:33 -0500 Subject: [PATCH 3/3] Move StreamingProvider to root layout to ensure it wraps Header component --- app/layout.tsx | 13 ++++++++----- components/chat.tsx | 17 ++++++----------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/app/layout.tsx b/app/layout.tsx index dd454b67..3f97ee89 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -16,6 +16,7 @@ import { CalendarToggleProvider } from '@/components/calendar-toggle-context' import { MapLoadingProvider } from '@/components/map-loading-context'; import ConditionalLottie from '@/components/conditional-lottie'; import { MapProvider } from '@/components/map/map-context' +import { StreamingProvider } from '@/components/streaming-context' const fontSans = FontSans({ subsets: ['latin'], @@ -60,8 +61,9 @@ export default function RootLayout({ - + - - - + + + + diff --git a/components/chat.tsx b/components/chat.tsx index 5a074042..2a775600 100644 --- a/components/chat.tsx +++ b/components/chat.tsx @@ -16,7 +16,6 @@ import { MapDataProvider, useMapData } from './map/map-data-context'; // Add thi import { updateDrawingContext } from '@/lib/actions/chat'; // Import the server action import dynamic from 'next/dynamic' import { HeaderSearchButton } from './header-search-button' -import { StreamingProvider } from './streaming-context' type ChatProps = { id?: string // This is the chatId @@ -85,9 +84,8 @@ export function Chat({ id }: ChatProps) { // Mobile layout if (isMobile) { return ( - - {/* Add Provider */} - + {/* Add Provider */} +
{activeView ? : } @@ -112,16 +110,14 @@ export function Chat({ id }: ChatProps) { )}
-
-
+ ); } // Desktop layout return ( - - {/* Add Provider */} - + {/* Add Provider */} +
{/* This is the new div for scrolling */}
@@ -149,7 +145,6 @@ export function Chat({ id }: ChatProps) { {activeView ? : }
-
-
+ ); }