:lightning: further chat performance tweaks

This commit is contained in:
TC
2026-06-16 21:03:52 +10:00
parent 4f1fbec868
commit 5a2dfee268

View File

@@ -1,4 +1,5 @@
import { useRef, useEffect, useState } from 'react' import { useRef, useEffect, useState, memo } from 'react'
import { useShallow } from 'zustand/react/shallow'
import { useEditorStore } from '../../store/editorStore' import { useEditorStore } from '../../store/editorStore'
import { ChatMessageItem } from './ChatMessageItem' import { ChatMessageItem } from './ChatMessageItem'
import { ChatInput } from './ChatInput' import { ChatInput } from './ChatInput'
@@ -7,6 +8,35 @@ import { parseAnnotationsFromAIResponse } from '../../utils/annotationParser'
import type { Attachment, TextAnnotation } from '../../types/editor' import type { Attachment, TextAnnotation } from '../../types/editor'
import './Chat.css' import './Chat.css'
const StreamingBubble = memo(function StreamingBubble({ scrollContainerRef }: { scrollContainerRef: React.RefObject<HTMLDivElement> }) {
const streamingContent = useEditorStore(s => s.streamingContent)
const isAILoading = useEditorStore(s => s.isAILoading)
const rafRef = useRef<number | null>(null)
useEffect(() => {
if (!isAILoading) return
if (rafRef.current !== null) return
rafRef.current = requestAnimationFrame(() => {
rafRef.current = null
const el = scrollContainerRef.current
if (el) el.scrollTop = el.scrollHeight
})
}, [streamingContent, isAILoading, scrollContainerRef])
if (!isAILoading) return null
return (
<div className="chat-message chat-message-assistant">
<div className="chat-message-label">Editor AI</div>
<div className="chat-message-content">
{streamingContent
? <span>{streamingContent}</span>
: <div className="chat-typing"><span /><span /><span /></div>
}
</div>
</div>
)
})
function formatSessionTime(createdAt: number): string { function formatSessionTime(createdAt: number): string {
const date = new Date(createdAt) const date = new Date(createdAt)
const now = new Date() const now = new Date()
@@ -34,7 +64,6 @@ export function ChatPanel(): JSX.Element {
addUserMessage, addUserMessage,
finalizeAssistantMessage, finalizeAssistantMessage,
setStreamingContent, setStreamingContent,
streamingContent,
setAILoading, setAILoading,
setAIError, setAIError,
setAnnotations, setAnnotations,
@@ -43,7 +72,29 @@ export function ChatPanel(): JSX.Element {
setActiveSession, setActiveSession,
rightPanelTab, rightPanelTab,
setRightPanelTab, setRightPanelTab,
} = useEditorStore() } = useEditorStore(useShallow(s => ({
chatHistory: s.chatHistory,
chatSessionsByFile: s.chatSessionsByFile,
activeSessionIdByFile: s.activeSessionIdByFile,
isAILoading: s.isAILoading,
aiError: s.aiError,
activeFileContent: s.activeFileContent,
activeFilePath: s.activeFilePath,
analysisMode: s.analysisMode,
annotations: s.annotations,
annotationsByFile: s.annotationsByFile,
addUserMessage: s.addUserMessage,
finalizeAssistantMessage: s.finalizeAssistantMessage,
setStreamingContent: s.setStreamingContent,
setAILoading: s.setAILoading,
setAIError: s.setAIError,
setAnnotations: s.setAnnotations,
linkAnnotationsToMessage: s.linkAnnotationsToMessage,
newChat: s.newChat,
setActiveSession: s.setActiveSession,
rightPanelTab: s.rightPanelTab,
setRightPanelTab: s.setRightPanelTab,
})))
const tab = rightPanelTab const tab = rightPanelTab
const setTab = setRightPanelTab const setTab = setRightPanelTab
@@ -138,7 +189,7 @@ export function ChatPanel(): JSX.Element {
} }
} }
// Auto-scroll to bottom when new content arrives // Auto-scroll to bottom when a new chat message is added
useEffect(() => { useEffect(() => {
if (scrollRafRef.current !== null) return if (scrollRafRef.current !== null) return
scrollRafRef.current = requestAnimationFrame(() => { scrollRafRef.current = requestAnimationFrame(() => {
@@ -146,7 +197,7 @@ export function ChatPanel(): JSX.Element {
const el = scrollRef.current const el = scrollRef.current
if (el) el.scrollTop = el.scrollHeight if (el) el.scrollTop = el.scrollHeight
}) })
}, [chatHistory, streamingContent]) }, [chatHistory])
const hasFile = Boolean(activeFilePath) const hasFile = Boolean(activeFilePath)
const allAnnotationsForFile: TextAnnotation[] = const allAnnotationsForFile: TextAnnotation[] =
@@ -252,17 +303,7 @@ export function ChatPanel(): JSX.Element {
/> />
) )
})} })}
{isAILoading && ( <StreamingBubble scrollContainerRef={scrollRef} />
<div className="chat-message chat-message-assistant">
<div className="chat-message-label">Editor AI</div>
<div className="chat-message-content">
{streamingContent
? <span>{streamingContent}</span>
: <div className="chat-typing"><span /><span /><span /></div>
}
</div>
</div>
)}
{aiError && ( {aiError && (
<div className="chat-error">{aiError}</div> <div className="chat-error">{aiError}</div>
)} )}