import React, { useRef } from 'react'; import { Note, NoteType, GenerationStatus, Attachment } from '../types'; import { LoadingSpinner } from './LoadingSpinner'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism'; interface NoteCardProps { note: Note; isRoot?: boolean; onRetry?: (noteId: string) => void; onExport?: () => void; isExporting?: boolean; onUpdate?: (noteId: string, updates: Partial | string) => void; onDelete?: (noteId: string) => void; onRegenerateImage?: (noteId: string, feedback: string) => void; onAddAttachment?: (noteId: string, attachment: Attachment) => void; onRemoveAttachment?: (noteId: string, attachmentId: string) => void; onAddTag?: (noteId: string, tag: string) => void; onRemoveTag?: (noteId: string, tag: string) => void; getNoteTitle?: (noteId: string) => string; onNavigateToNote?: (noteId: string) => void; dragHandleProps?: any; } const NoteCard: React.FC = ({ note, isRoot = false, onRetry, onExport, isExporting = false, onUpdate, onDelete, onRegenerateImage, onAddAttachment, onRemoveAttachment, onAddTag, onRemoveTag, getNoteTitle, onNavigateToNote, dragHandleProps }) => { const [isEditing, setIsEditing] = React.useState(false); const [editContent, setEditContent] = React.useState(note.content); const [feedback, setFeedback] = React.useState(''); const [showFeedback, setShowFeedback] = React.useState(false); const [useFeedback, setUseFeedback] = React.useState(false); const [showDeleteConfirm, setShowDeleteConfirm] = React.useState(false); const [showSuccess, setShowSuccess] = React.useState(false); const fileInputRef = useRef(null); const isImage = note.type === NoteType.IMAGE; const isLoading = note.status === GenerationStatus.GENERATING; const isIdle = note.status === GenerationStatus.IDLE; const isError = note.status === GenerationStatus.ERROR; const handleSave = () => { if (onUpdate) { onUpdate(note.id, { content: editContent }); } setIsEditing(false); setShowSuccess(true); setTimeout(() => setShowSuccess(false), 2500); }; const toggleTask = () => { if (onUpdate) { onUpdate(note.id, { isTask: !note.isTask }); } }; const toggleTaskStatus = () => { if (onUpdate) { onUpdate(note.id, { isCompleted: !note.isCompleted }); } }; const handleCancel = () => { setEditContent(note.content); setIsEditing(false); }; const handleFileChange = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file || !onAddAttachment) return; const reader = new FileReader(); reader.onloadend = () => { const base64String = reader.result as string; let type: 'image' | 'audio' | 'file' = 'file'; if (file.type.startsWith('image/')) type = 'image'; else if (file.type.startsWith('audio/')) type = 'audio'; const newAttachment: Attachment = { id: Date.now().toString(), type, url: base64String, name: file.name }; onAddAttachment(note.id, newAttachment); }; reader.readAsDataURL(file); if (fileInputRef.current) fileInputRef.current.value = ''; }; const renderAttachments = () => { if (!note.attachments || note.attachments.length === 0) return null; return (

Attachments

{note.attachments.map(attachment => (
{attachment.type === 'image' && (
{attachment.name}
)} {attachment.type === 'audio' && (
🎵
)} {attachment.type === 'file' && (
📄
)}

{attachment.name}

{attachment.type === 'audio' && (
{onRemoveAttachment && ( )}
))}
); }; const renderContent = () => { if (isLoading) { return (
Preparing content...

AI is detailing this step, please wait.

); } if (isIdle) { return (
{note.content || "Content will be generated when it's time for this step..."}
) } if (isError) { return (

An error occurred while generating content.

{onRetry && ( )}
); } if (isEditing) { return (