import { useState, useCallback, useEffect, useRef } from 'react' import { CaretRight, CaretDown, Brain, ArrowCounterClockwise } from '@phosphor-icons/react' import { AiActionCard, type AiActionStatus } from './AiActionCard' import { MarkdownContent } from './MarkdownContent' import type { NoteReference } from '../utils/ai-context' import { getTypeColor, getTypeLightColor } from '../utils/typeColors' export interface AiAction { tool: string toolId: string label: string path?: string status: AiActionStatus input?: string output?: string } export interface AiMessageProps { userMessage: string references?: NoteReference[] reasoning?: string reasoningDone?: boolean actions: AiAction[] response?: string isStreaming?: boolean onOpenNote?: (path: string) => void onNavigateWikilink?: (target: string) => void } function ReferencePill({ reference, onClick }: { reference: NoteReference onClick?: (path: string) => void }) { const color = getTypeColor(reference.type) const lightColor = getTypeLightColor(reference.type) return ( ) } function UserBubble({ content, references, onOpenNote }: { content: string references?: NoteReference[] onOpenNote?: (path: string) => void }) { return (
{references && references.length > 0 && (
{references.map(ref => ( ))}
)} {content}
) } function ReasoningBlock({ text, expanded, onToggle }: { text: string; expanded: boolean; onToggle: () => void }) { const contentRef = useRef(null) useEffect(() => { if (expanded && contentRef.current) { contentRef.current.scrollTop = contentRef.current.scrollHeight } }, [expanded, text]) return (
{expanded && (
{text}
)}
) } function ActionCardsList({ actions, onOpenNote, expandedIds, onToggleExpand }: { actions: AiAction[] onOpenNote?: (path: string) => void expandedIds: Set onToggleExpand: (toolId: string) => void }) { return (
{actions.map((action) => ( onToggleExpand(action.toolId)} onOpenNote={onOpenNote} /> ))}
) } function ResponseBlock({ text, onNavigateWikilink }: { text: string; onNavigateWikilink?: (target: string) => void }) { return (
) } function StreamingIndicator() { return (
) } export function AiMessage({ userMessage, references, reasoning, reasoningDone, actions, response, isStreaming, onOpenNote, onNavigateWikilink }: AiMessageProps) { // Manual override: null = follow auto behavior, true/false = user forced const [userOverride, setUserOverride] = useState(false) const [expandedActions, setExpandedActions] = useState>(new Set()) // Auto: expanded while reasoning streams, collapsed once done // User can manually toggle to override the auto state const autoExpanded = !reasoningDone const reasoningExpanded = userOverride ? !autoExpanded : autoExpanded const toggleAction = useCallback((toolId: string) => { setExpandedActions(prev => { const next = new Set(prev) if (next.has(toolId)) next.delete(toolId) else next.add(toolId) return next }) }, []) return (
{reasoning && ( setUserOverride(prev => !prev)} /> )} {actions.length > 0 && ( )} {response && } {isStreaming && !response && }
) }