import { useRef, useEffect, useState } from 'react' import { useEditorStore } from '../../store/editorStore' import { ChatMessageItem } from './ChatMessageItem' import { ChatInput } from './ChatInput' import { FeedbackPanel } from '../Feedback/FeedbackPanel' import { parseAnnotationsFromAIResponse } from '../../utils/annotationParser' import './Chat.css' type TabId = 'chat' | 'feedback' export function ChatPanel(): JSX.Element { const { chatHistory, isAILoading, aiError, activeFileContent, activeFilePath, analysisMode, annotations, addUserMessage, startAssistantMessage, appendToLastAssistantMessage, setAILoading, setAIError, setAnnotations, clearChat } = useEditorStore() const [tab, setTab] = useState('chat') const scrollRef = useRef(null) const prevAnnotationCountRef = useRef(annotations.length) // Auto-switch to Feedback tab the first time annotations appear (0 → >0) useEffect(() => { const prev = prevAnnotationCountRef.current const curr = annotations.length if (prev === 0 && curr > 0) { setTab('feedback') } prevAnnotationCountRef.current = curr }, [annotations]) const sendMessage = async (text: string): Promise => { if (!activeFilePath || isAILoading) return setAIError(null) addUserMessage(text) startAssistantMessage() setAILoading(true) try { const mode = analysisMode === 'none' ? 'chat' : analysisMode await window.api.streamAIMessage( { mode, documentContent: activeFileContent, documentPath: activeFilePath, conversationHistory: chatHistory .slice(-10) .map((m) => ({ role: m.role, content: m.content })), userMessage: text }, (chunk: string) => { appendToLastAssistantMessage(chunk) } ) // After streaming, parse AI response for annotations const currentHistory = useEditorStore.getState().chatHistory const lastMsg = currentHistory[currentHistory.length - 1] if (lastMsg?.role === 'assistant' && lastMsg.content.length > 0) { const parsed = parseAnnotationsFromAIResponse(lastMsg.content, activeFileContent) if (parsed.length > 0) { setAnnotations(parsed) } } } catch (err) { const message = err instanceof Error ? err.message : 'An error occurred' setAIError(message) } finally { setAILoading(false) } } // Auto-scroll to bottom when new content arrives useEffect(() => { const el = scrollRef.current if (el) { el.scrollTop = el.scrollHeight } }, [chatHistory]) const hasFile = Boolean(activeFilePath) return (
{/* ── Tab bar ── */}
{/* Clear button floated right, only visible in Chat tab */} {tab === 'chat' && chatHistory.length > 0 && ( )}
{/* ── Panel content ── */} {tab === 'feedback' ? ( ) : ( <>
{!hasFile && (

Open a chapter to start a conversation about it.

)} {hasFile && chatHistory.length === 0 && (

Ask anything about the current chapter — passive voice, plot, character, style...

)} {chatHistory.map((msg) => ( ))} {isAILoading && chatHistory[chatHistory.length - 1]?.content === '' && (
)} {aiError && (
{aiError}
)}
)}
) }