import { useState, useRef, useEffect, useMemo } from 'react' import { Robot, X, PaperPlaneRight, Plus, Link } from '@phosphor-icons/react' import { AiMessage } from './AiMessage' import { useAiAgent, type AiAgentMessage } from '../hooks/useAiAgent' import { collectLinkedEntries, buildContextualPrompt } from '../utils/ai-context' import type { VaultEntry } from '../types' export type { AiAgentMessage } from '../hooks/useAiAgent' interface AiPanelProps { onClose: () => void onOpenNote?: (path: string) => void vaultPath: string activeEntry?: VaultEntry | null entries?: VaultEntry[] allContent?: Record } function PanelHeader({ onClose, onClear }: { onClose: () => void; onClear: () => void }) { return (
AI Chat
) } function ContextBar({ activeEntry, linkedCount }: { activeEntry: VaultEntry; linkedCount: number }) { return (
{activeEntry.title} {linkedCount > 0 && ( + {linkedCount} linked )}
) } function EmptyState({ hasContext }: { hasContext: boolean }) { return (

{hasContext ? 'Ask about this note and its linked context' : 'Open a note, then ask the AI about it' }

{hasContext ? 'Summarize, find connections, expand ideas' : 'The AI will use the active note as context' }

) } function MessageHistory({ messages, isActive, onOpenNote, hasContext }: { messages: AiAgentMessage[]; isActive: boolean; onOpenNote?: (path: string) => void; hasContext: boolean }) { const endRef = useRef(null) useEffect(() => { endRef.current?.scrollIntoView({ behavior: 'smooth' }) }, [messages, isActive]) return (
{messages.length === 0 && !isActive && } {messages.map((msg, i) => ( ))}
) } function InputBar({ input, onInputChange, onSend, onKeyDown, isActive, hasContext }: { input: string; onInputChange: (v: string) => void onSend: () => void; onKeyDown: (e: React.KeyboardEvent) => void isActive: boolean; hasContext: boolean }) { const sendDisabled = isActive || !input.trim() return (
onInputChange(e.target.value)} onKeyDown={onKeyDown} className="flex-1 border border-border bg-transparent text-foreground" style={{ fontSize: 13, borderRadius: 8, padding: '8px 10px', outline: 'none', fontFamily: 'inherit', }} placeholder={hasContext ? 'Ask about this note...' : 'Ask the AI agent...'} disabled={isActive} data-testid="agent-input" />
) } export function AiPanel({ onClose, onOpenNote, vaultPath, activeEntry, entries, allContent }: AiPanelProps) { const [input, setInput] = useState('') const linkedEntries = useMemo(() => { if (!activeEntry || !entries) return [] return collectLinkedEntries(activeEntry, entries) }, [activeEntry, entries]) const contextPrompt = useMemo(() => { if (!activeEntry || !allContent) return undefined return buildContextualPrompt(activeEntry, linkedEntries, allContent) }, [activeEntry, linkedEntries, allContent]) const agent = useAiAgent(vaultPath, contextPrompt) const hasContext = !!activeEntry const isActive = agent.status === 'thinking' || agent.status === 'tool-executing' const handleSend = () => { if (!input.trim() || isActive) return agent.sendMessage(input) setInput('') } const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault() handleSend() } } return ( ) }