import { useState, useRef, useEffect } from 'react'
import { Send, ChevronDown, ChevronRight } from 'lucide-react'
import { api } from '../api'
import type { AppSettings, ChatResponse, CausalChain } from '../types'
interface Props {
documentId: string
settings: AppSettings
}
interface Message {
role: 'user' | 'assistant'
content: string
response?: ChatResponse
}
function ChainCard({ chain, index }: { chain: CausalChain; index: number }) {
const [open, setOpen] = useState(index === 0)
return (
{open && (
{chain.path.map((node, i) => (
{node}
{i < chain.path.length - 1 && →}
))}
{chain.edges.map((edge, i) => (
{edge.cause}
{edge.relation}
{edge.effect}
[{edge.confidence.toFixed(2)}]
{edge.evidence && (
"{edge.evidence}"
)}
))}
)}
)
}
export function ChatPanel({ documentId, settings }: Props) {
const [messages, setMessages] = useState([])
const [input, setInput] = useState('')
const [loading, setLoading] = useState(false)
const [error, setError] = useState(null)
const bottomRef = useRef(null)
useEffect(() => {
bottomRef.current?.scrollIntoView({ behavior: 'smooth' })
}, [messages])
const send = async () => {
const msg = input.trim()
if (!msg || loading) return
setInput('')
setError(null)
setMessages(prev => [...prev, { role: 'user', content: msg }])
setLoading(true)
try {
const res = await api.chat(documentId, msg, settings)
setMessages(prev => [...prev, { role: 'assistant', content: res.answer, response: res }])
} catch (e: any) {
setError(String(e.message))
} finally {
setLoading(false)
}
}
const SAMPLES = [
'Why did quarterly revenue decrease?',
'What caused inventory shortages?',
'What are the downstream effects of port congestion?',
]
return (
{messages.length === 0 && (
Sample Questions
{SAMPLES.map(q => (
))}
)}
{messages.map((m, i) => (
{m.role === 'user' ? (
{m.content}
) : (
{m.content}
{m.response && m.response.chains.length > 0 && (
Retrieved Causal Chains ({m.response.chains.length})
{m.response.chains.map((chain, ci) => (
))}
)}
)}
))}
{loading && (
Retrieving causal chains...
)}
{error &&
{error}
}
setInput(e.target.value)}
onKeyDown={e => e.key === 'Enter' && send()}
disabled={loading}
style={{ flex: 1 }}
/>
)
}