diff --git a/bun_output.txt b/bun_output.txt new file mode 100644 index 00000000..ca2ac96b --- /dev/null +++ b/bun_output.txt @@ -0,0 +1,16 @@ +$ next dev --turbo + ▲ Next.js 15.3.8 (Turbopack) + - Local: http://localhost:3000 + - Network: http://192.168.0.2:3000 + - Environments: .env + + ✓ Starting... + ✓ Compiled middleware in 386ms + ✓ Ready in 1880ms + ○ Compiling / ... + ✓ Compiled / in 28.6s +Chat DB actions loaded. Ensure getCurrentUserId() is correctly implemented for server-side usage if applicable. + GET / 200 in 33121ms + GET / 200 in 976ms +[Auth] Supabase URL or Anon Key is not set for server-side auth. + POST / 200 in 1775ms diff --git a/components/chat-panel.tsx b/components/chat-panel.tsx index ca2fbc6f..f03002a5 100644 --- a/components/chat-panel.tsx +++ b/components/chat-panel.tsx @@ -43,6 +43,7 @@ export const ChatPanel = forwardRef(({ messages, i const inputRef = useRef(null) const formRef = useRef(null) const fileInputRef = useRef(null) + const activeSuggestionRef = useRef('') useImperativeHandle(ref, () => ({ handleAttachmentClick() { @@ -91,6 +92,12 @@ export const ChatPanel = forwardRef(({ messages, i return } + if (debounceTimeoutRef.current) { + clearTimeout(debounceTimeoutRef.current) + } + activeSuggestionRef.current = '' + setSuggestions(null) + const content: ({ type: 'text'; text: string } | { type: 'image'; image: string })[] = [] if (input) { content.push({ type: 'text', text: input }) @@ -119,6 +126,7 @@ export const ChatPanel = forwardRef(({ messages, i formData.append('drawnFeatures', JSON.stringify(mapData.drawnFeatures || [])) setInput('') + setSuggestions(null) clearAttachment() const responseMessage = await submit(formData) @@ -126,7 +134,12 @@ export const ChatPanel = forwardRef(({ messages, i } const handleClear = async () => { + if (debounceTimeoutRef.current) { + clearTimeout(debounceTimeoutRef.current) + } + activeSuggestionRef.current = '' setMessages([]) + setSuggestions(null) clearAttachment() await clearChat() } @@ -140,17 +153,27 @@ export const ChatPanel = forwardRef(({ messages, i const wordCount = value.trim().split(/\s+/).filter(Boolean).length if (wordCount < 2) { setSuggestions(null) + activeSuggestionRef.current = '' return } + const currentQuery = value + activeSuggestionRef.current = currentQuery + debounceTimeoutRef.current = setTimeout(async () => { - const suggestionsStream = await getSuggestions(value, mapData) - for await (const partialSuggestions of readStreamableValue( - suggestionsStream - )) { - if (partialSuggestions) { - setSuggestions(partialSuggestions as PartialRelated) + if (activeSuggestionRef.current !== currentQuery) return + try { + const suggestionsStream = await getSuggestions(value, mapData) + for await (const partialSuggestions of readStreamableValue( + suggestionsStream + )) { + if (activeSuggestionRef.current !== currentQuery) break + if (partialSuggestions) { + setSuggestions(partialSuggestions as PartialRelated) + } } + } catch (error) { + console.error(error) } }, 500) // 500ms debounce delay }, diff --git a/patch_suggestions.js b/patch_suggestions.js new file mode 100644 index 00000000..e8262792 --- /dev/null +++ b/patch_suggestions.js @@ -0,0 +1,112 @@ +const fs = require('fs'); + +const path = 'components/chat-panel.tsx'; +let content = fs.readFileSync(path, 'utf8'); + +// We will add activeSuggestionRef to the component +content = content.replace( + 'const fileInputRef = useRef(null)', + 'const fileInputRef = useRef(null)\n const activeSuggestionRef = useRef(\'\')' +); + +// We will update debouncedGetSuggestions +const oldDebounce = ` const debouncedGetSuggestions = useCallback( + (value: string) => { + if (debounceTimeoutRef.current) { + clearTimeout(debounceTimeoutRef.current) + } + + const wordCount = value.trim().split(/\\s+/).filter(Boolean).length + if (wordCount < 2) { + setSuggestions(null) + return + } + + debounceTimeoutRef.current = setTimeout(async () => { + const suggestionsStream = await getSuggestions(value, mapData) + for await (const partialSuggestions of readStreamableValue( + suggestionsStream + )) { + if (partialSuggestions) { + setSuggestions(partialSuggestions as PartialRelated) + } + } + }, 500) // 500ms debounce delay + }, + [mapData, setSuggestions] + )`; + +const newDebounce = ` const debouncedGetSuggestions = useCallback( + (value: string) => { + if (debounceTimeoutRef.current) { + clearTimeout(debounceTimeoutRef.current) + } + + const wordCount = value.trim().split(/\\s+/).filter(Boolean).length + if (wordCount < 2) { + setSuggestions(null) + activeSuggestionRef.current = '' + return + } + + const currentQuery = value + activeSuggestionRef.current = currentQuery + + debounceTimeoutRef.current = setTimeout(async () => { + if (activeSuggestionRef.current !== currentQuery) return + try { + const suggestionsStream = await getSuggestions(value, mapData) + for await (const partialSuggestions of readStreamableValue( + suggestionsStream + )) { + if (activeSuggestionRef.current !== currentQuery) break + if (partialSuggestions) { + setSuggestions(partialSuggestions as PartialRelated) + } + } + } catch (error) { + console.error(error) + } + }, 500) // 500ms debounce delay + }, + [mapData, setSuggestions] + )`; + +content = content.replace(oldDebounce, newDebounce); + +const oldHandleSubmit = ` const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault() + if (!input.trim() && !selectedFile) { + return + }`; + +const newHandleSubmit = ` const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault() + if (!input.trim() && !selectedFile) { + return + } + + if (debounceTimeoutRef.current) { + clearTimeout(debounceTimeoutRef.current) + } + activeSuggestionRef.current = '' + setSuggestions(null)`; + +content = content.replace(oldHandleSubmit, newHandleSubmit); + +const oldHandleClear = ` const handleClear = async () => { + setMessages([]) + setSuggestions(null)`; + +const newHandleClear = ` const handleClear = async () => { + if (debounceTimeoutRef.current) { + clearTimeout(debounceTimeoutRef.current) + } + activeSuggestionRef.current = '' + setMessages([]) + setSuggestions(null)`; + +content = content.replace(oldHandleClear, newHandleClear); + +fs.writeFileSync(path, content); +console.log("Patched suggestions with active tracking"); diff --git a/test-grep.sh b/test-grep.sh new file mode 100644 index 00000000..9bcc5c82 --- /dev/null +++ b/test-grep.sh @@ -0,0 +1 @@ +grep -rnw -A 10 -B 5 "const handleSubmit = async" components/chat-panel.tsx