function DocumentView({ note, contentBlocks, onViewInCanvas }) { const hasAISummary = note.note.ai_summary && note.note.ai_summary.trim(); const [summaryExpanded, setSummaryExpanded] = useState(true); // Custom ordering - persist in localStorage per note const [orderedBlocks, setOrderedBlocks] = useState(() => { const savedOrder = localStorage.getItem(`note_order_${note.note.id}`); if (savedOrder) { try { const orderMap = JSON.parse(savedOrder); // Sort blocks based on saved order return [...contentBlocks].sort((a, b) => { const orderA = orderMap[a.id] ?? 999; const orderB = orderMap[b.id] ?? 999; return orderA - orderB; }); } catch { return contentBlocks; } } return contentBlocks; }); // Save order to localStorage whenever it changes const saveOrder = (blocks) => { const orderMap = {}; blocks.forEach((block, index) => { orderMap[block.id] = index; }); localStorage.setItem(`note_order_${note.note.id}`, JSON.stringify(orderMap)); }; // Bookmark management - max 1 of each color per note const [bookmarks, setBookmarks] = useState(() => { const saved = localStorage.getItem(`note_bookmarks_${note.note.id}`); return saved ? JSON.parse(saved) : {}; // Format: { blockId: "gold" | "blue" | "pink" } }); const [showBookmarkPanel, setShowBookmarkPanel] = useState(false); const setBookmark = (blockId, color) => { const newBookmarks = { ...bookmarks }; if (color === null) { // Remove bookmark delete newBookmarks[blockId]; } else { // Check if this color is already used on another block const existingBlockId = Object.keys(newBookmarks).find(id => newBookmarks[id] === color); if (existingBlockId && existingBlockId !== blockId) { // Move the bookmark from old block to new block delete newBookmarks[existingBlockId]; } newBookmarks[blockId] = color; } setBookmarks(newBookmarks); localStorage.setItem(`note_bookmarks_${note.note.id}`, JSON.stringify(newBookmarks)); }; const scrollToBookmark = (blockId) => { const element = document.getElementById(`source-${blockId}`); if (element) { element.scrollIntoView({ behavior: 'smooth', block: 'center' }); // Flash animation element.style.animation = 'none'; setTimeout(() => { element.style.animation = 'highlight-flash 1s ease'; }, 10); } }; const moveUp = (index) => { if (index === 0) return; const newBlocks = [...orderedBlocks]; [newBlocks[index - 1], newBlocks[index]] = [newBlocks[index], newBlocks[index - 1]]; setOrderedBlocks(newBlocks); saveOrder(newBlocks); }; const moveDown = (index) => { if (index === orderedBlocks.length - 1) return; const newBlocks = [...orderedBlocks]; [newBlocks[index], newBlocks[index + 1]] = [newBlocks[index + 1], newBlocks[index]]; setOrderedBlocks(newBlocks); saveOrder(newBlocks); }; const exportAsMarkdown = () => { let markdown = `# ${note.note.title}\n\n`; if (hasAISummary) { markdown += `## AI Summary\n\n${note.note.ai_summary}\n\n`; } markdown += `## Sources\n\n`; orderedBlocks.forEach((block, index) => { const icon = block.content_type === 'text' ? '📝' : block.content_type === 'image' ? '🖼️' : block.content_type === 'pdf' ? '📄' : block.content_type === 'audio' ? '🎤' : block.content_type === 'link' ? '🔗' : '📦'; markdown += `### ${icon} Source ${index + 1}\n\n`; if (block.filename) { markdown += `**File:** ${block.filename}\n\n`; } if (block.content_type === 'link' && block.content_data?.url) { markdown += `**URL:** ${block.content_data.url}\n\n`; } if (block.extracted_text) { markdown += `${block.extracted_text}\n\n`; } markdown += `---\n\n`; }); const blob = new Blob([markdown], { type: 'text/markdown' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `${note.note.title.replace(/[^a-z0-9]/gi, '_')}.md`; a.click(); URL.revokeObjectURL(url); }; const exportAsText = () => { let text = `${note.note.title}\n${'='.repeat(note.note.title.length)}\n\n`; if (hasAISummary) { text += `AI SUMMARY\n----------\n${note.note.ai_summary}\n\n`; } text += `SOURCES\n-------\n\n`; orderedBlocks.forEach((block, index) => { text += `Source ${index + 1}:\n`; if (block.filename) { text += `File: ${block.filename}\n`; } if (block.content_type === 'link' && block.content_data?.url) { text += `URL: ${block.content_data.url}\n`; } if (block.extracted_text) { text += `\n${block.extracted_text}\n`; } text += `\n${'─'.repeat(50)}\n\n`; }); const blob = new Blob([text], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `${note.note.title.replace(/[^a-z0-9]/gi, '_')}.txt`; a.click(); URL.revokeObjectURL(url); }; const copyAllContent = () => { let text = ''; if (hasAISummary) { text += `AI Summary:\n${note.note.ai_summary}\n\n`; } orderedBlocks.forEach((block, index) => { if (block.extracted_text) { text += `Source ${index + 1}:\n${block.extracted_text}\n\n`; } }); navigator.clipboard.writeText(text); alert('All content copied to clipboard!'); }; return (
No content blocks yet
Add content in Canvas mode to see extractions here
Switch to Canvas mode and click "Extract Content" to process your materials