@@ -46,6 +46,17 @@ import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip";
4646import { CopyChat } from "./copy-chat" ;
4747import { MessageMetadata } from "./message-metadata" ;
4848
49+ const isFromPreviousDay = ( timestamp : number ) : boolean => {
50+ const messageDate = new Date ( timestamp ) ;
51+ const today = new Date ( ) ;
52+
53+ return (
54+ messageDate . getFullYear ( ) !== today . getFullYear ( ) ||
55+ messageDate . getMonth ( ) !== today . getMonth ( ) ||
56+ messageDate . getDate ( ) !== today . getDate ( )
57+ ) ;
58+ } ;
59+
4960export const useChatPersistence = ( ) => {
5061 const saveTimeoutRef = useRef < ReturnType < typeof setTimeout > | undefined > (
5162 undefined
@@ -56,8 +67,23 @@ export const useChatPersistence = () => {
5667 db . messages . orderBy ( "sequence" ) . toArray ( )
5768 ) ;
5869
70+ // Clear messages if they're from a previous day
71+ useEffect ( ( ) => {
72+ if ( storedMessages && storedMessages . length > 0 ) {
73+ const firstMessage = storedMessages [ 0 ] ;
74+ if ( firstMessage && isFromPreviousDay ( firstMessage . timestamp ) ) {
75+ db . messages . clear ( ) ;
76+ }
77+ }
78+ } , [ storedMessages ] ) ;
79+
80+ // Filter out stale messages from previous days
81+ const freshMessages = storedMessages ?. filter (
82+ ( msg ) => ! isFromPreviousDay ( msg . timestamp )
83+ ) ;
84+
5985 const initialMessages =
60- storedMessages ?. map ( ( { timestamp, sequence, ...message } ) => message ) ?? [ ] ;
86+ freshMessages ?. map ( ( { timestamp, sequence, ...message } ) => message ) ?? [ ] ;
6187
6288 const isLoading = storedMessages === undefined ;
6389
@@ -118,15 +144,20 @@ type ChatProps = {
118144 suggestions : string [ ] ;
119145} ;
120146
121- const ChatInner = ( { basePath, suggestions } : ChatProps ) => {
147+ type ChatInnerProps = ChatProps & {
148+ isOpen : boolean ;
149+ } ;
150+
151+ const ChatInner = ( { basePath, suggestions, isOpen } : ChatInnerProps ) => {
152+ const textareaRef = useRef < HTMLTextAreaElement > ( null ) ;
122153 const [ isInitialized , setIsInitialized ] = useState ( false ) ;
123154 const [ localPrompt , setLocalPrompt ] = useState ( "" ) ;
124155 const [ providerKey , setProviderKey ] = useState ( 0 ) ;
125156 const { prompt, setPrompt, setIsOpen } = useChatContext ( ) ;
126157 const { initialMessages, isLoading, saveMessages, clearMessages } =
127158 useChatPersistence ( ) ;
128159
129- const { messages, sendMessage, status, setMessages } = useChat ( {
160+ const { messages, sendMessage, status, setMessages, stop } = useChat ( {
130161 transport : new DefaultChatTransport ( {
131162 api : basePath ? `${ basePath } /api/chat` : "/api/chat" ,
132163 } ) ,
@@ -163,24 +194,42 @@ const ChatInner = ({ basePath, suggestions }: ChatProps) => {
163194 }
164195 } , [ messages , saveMessages , isInitialized ] ) ;
165196
166- const handleSuggestionClick = async ( suggestion : string ) => {
167- await sendMessage ( { text : suggestion } ) ;
197+ // Focus textarea when chat opens
198+ useEffect ( ( ) => {
199+ if ( isOpen ) {
200+ // Small delay to ensure the panel/drawer animation has started
201+ const timer = setTimeout ( ( ) => {
202+ textareaRef . current ?. focus ( ) ;
203+ } , 100 ) ;
204+ return ( ) => clearTimeout ( timer ) ;
205+ }
206+ } , [ isOpen ] ) ;
207+
208+ const handleSuggestionClick = async ( text : string ) => {
209+ if ( status === "streaming" || status === "submitted" ) {
210+ return ;
211+ }
168212 setLocalPrompt ( "" ) ;
169213 setPrompt ( "" ) ;
214+ await sendMessage ( { text } ) ;
170215 } ;
171216
172217 const handleSubmit : PromptInputProps [ "onSubmit" ] = async ( message , event ) => {
173218 event . preventDefault ( ) ;
174219
220+ if ( status === "streaming" || status === "submitted" ) {
221+ return ;
222+ }
223+
175224 const { text } = message ;
176225
177226 if ( ! text ) {
178227 return ;
179228 }
180229
181- await sendMessage ( { text } ) ;
182230 setLocalPrompt ( "" ) ;
183231 setPrompt ( "" ) ;
232+ await sendMessage ( { text } ) ;
184233 } ;
185234
186235 const handleClearChat = async ( ) => {
@@ -249,7 +298,7 @@ const ChatInner = ({ basePath, suggestions }: ChatProps) => {
249298 key = { message . id }
250299 >
251300 < MessageMetadata
252- inProgress = { status === "submitted" }
301+ inProgress = { status === "submitted" || status === "streaming" }
253302 parts = { message . parts as MyUIMessage [ "parts" ] }
254303 />
255304 { message . parts
@@ -260,7 +309,8 @@ const ChatInner = ({ basePath, suggestions }: ChatProps) => {
260309 className = "text-wrap"
261310 rehypePlugins = { [
262311 defaultRehypePlugins . raw ,
263- [
312+ defaultRehypePlugins . katex ,
313+ [
264314 harden ,
265315 {
266316 defaultOrigin :
@@ -320,13 +370,24 @@ const ChatInner = ({ basePath, suggestions }: ChatProps) => {
320370 setLocalPrompt ( e . target . value ) ;
321371 setPrompt ( e . target . value ) ;
322372 } }
373+ ref = { textareaRef }
323374 />
324375 </ PromptInputBody >
325376 < PromptInputFooter >
326377 < p className = "text-muted-foreground text-xs" >
327378 { localPrompt . length } / 1000
328379 </ p >
329- < PromptInputSubmit status = { status } />
380+ < PromptInputSubmit
381+ onClick = {
382+ status === "streaming"
383+ ? ( e ) => {
384+ e . preventDefault ( ) ;
385+ stop ( ) ;
386+ }
387+ : undefined
388+ }
389+ status = { status }
390+ />
330391 </ PromptInputFooter >
331392 </ PromptInput >
332393 </ PromptInputProvider >
@@ -382,7 +443,11 @@ export const Chat = ({ basePath, suggestions }: ChatProps) => {
382443 ) }
383444 data-state = { isOpen ? "open" : "closed" }
384445 >
385- < ChatInner basePath = { basePath } suggestions = { suggestions } />
446+ < ChatInner
447+ basePath = { basePath }
448+ isOpen = { isOpen }
449+ suggestions = { suggestions }
450+ />
386451 </ div >
387452 </ Portal . Root >
388453 < div className = "md:hidden" >
@@ -397,7 +462,11 @@ export const Chat = ({ basePath, suggestions }: ChatProps) => {
397462 </ Button >
398463 </ DrawerTrigger >
399464 < DrawerContent className = "h-[80dvh]" >
400- < ChatInner basePath = { basePath } suggestions = { suggestions } />
465+ < ChatInner
466+ basePath = { basePath }
467+ isOpen = { isOpen }
468+ suggestions = { suggestions }
469+ />
401470 </ DrawerContent >
402471 </ Drawer >
403472 </ div >
0 commit comments