'use client' import { useState, useEffect, useCallback, useRef } from 'react' import { Dialog, DialogContent } from '@/components/ui/dialog' import { Button } from '@/components/ui/button' import { Textarea } from '@/components/ui/textarea' import { Checkbox } from '@/components/ui/checkbox' import { X, Link2, Sparkles, Edit, Check } from 'lucide-react' import { cn } from '@/lib/utils' import { Note } from '@/lib/types' import { useLanguage } from '@/lib/i18n/LanguageProvider' interface FusionModalProps { isOpen: boolean onClose: () => void notes: Array> onConfirmFusion: (mergedNote: { title: string; content: string }, options: FusionOptions) => Promise } interface FusionOptions { archiveOriginals: boolean keepAllTags: boolean useLatestTitle: boolean createBacklinks: boolean } export function FusionModal({ isOpen, onClose, notes, onConfirmFusion }: FusionModalProps) { const { t } = useLanguage() const [selectedNoteIds, setSelectedNoteIds] = useState(notes.filter(n => n.id).map(n => n.id!)) const [customPrompt, setCustomPrompt] = useState('') const [fusionPreview, setFusionPreview] = useState('') const [isGenerating, setIsGenerating] = useState(false) const [isEditing, setIsEditing] = useState(false) const [generationError, setGenerationError] = useState(null) const hasGeneratedRef = useRef(false) const [options, setOptions] = useState({ archiveOriginals: true, keepAllTags: true, useLatestTitle: false, createBacklinks: false }) const handleGenerateFusion = useCallback(async () => { setIsGenerating(true) setGenerationError(null) setFusionPreview('') try { // Call AI API to generate fusion const res = await fetch('/api/ai/echo/fusion', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ noteIds: selectedNoteIds, prompt: customPrompt }) }) const data = await res.json() if (!res.ok) { throw new Error(data.error || 'Failed to generate fusion') } if (!data.fusedNote) { throw new Error('No fusion content returned from API') } setFusionPreview(data.fusedNote) } catch (error) { console.error('[FusionModal] Failed to generate:', error) const errorMessage = error instanceof Error ? error.message : t('memoryEcho.fusion.generateError') setGenerationError(errorMessage) } finally { setIsGenerating(false) } }, [selectedNoteIds, customPrompt]) // Auto-generate fusion preview when modal opens with selected notes useEffect(() => { // Reset generation state when modal closes if (!isOpen) { hasGeneratedRef.current = false setGenerationError(null) setFusionPreview('') return } // Generate only once when modal opens and we have 2+ notes if (isOpen && selectedNoteIds.length >= 2 && !hasGeneratedRef.current && !isGenerating) { hasGeneratedRef.current = true handleGenerateFusion() } }, [isOpen, selectedNoteIds.length, isGenerating, handleGenerateFusion]) const handleConfirm = async () => { if (isGenerating) { return } if (!fusionPreview) { await handleGenerateFusion() return } setIsGenerating(true) try { // Parse the preview into title and content const lines = fusionPreview.split('\n') const title = lines[0].replace(/^#+\s*/, '').trim() const content = lines.slice(1).join('\n').trim() await onConfirmFusion( { title, content }, options ) onClose() } finally { setIsGenerating(false) } } const selectedNotes = notes.filter(n => n.id && selectedNoteIds.includes(n.id)) return ( {/* Header */}

{t('memoryEcho.fusion.title')}

{t('memoryEcho.fusion.mergeNotes', { count: selectedNoteIds.length })}

{/* Section 1: Note Selection */}

{t('memoryEcho.fusion.notesToMerge')}

{notes.filter(n => n.id).map((note) => (
{ if (checked && note.id) { setSelectedNoteIds([...selectedNoteIds, note.id]) } else if (note.id) { setSelectedNoteIds(selectedNoteIds.filter(id => id !== note.id)) } }} />
))}
{/* Section 2: Custom Prompt (Optional) */}

{t('memoryEcho.fusion.optionalPrompt')}