import React, { useState, useRef, useEffect } from 'react'; import { Button } from './ui/button'; import { Input } from './ui/input'; import { Label } from './ui/label'; import { Card } from './ui/card'; import { Separator } from './ui/separator'; import { Textarea } from './ui/textarea'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select'; import { LogIn, LogOut, Download, Sparkles, Bookmark, Copy } from 'lucide-react'; import { Document, HeadingLevel, Packer, Paragraph, TextRun } from 'docx'; import type { User, SavedItem } from '../App'; import { toast } from 'sonner'; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, DialogFooter, } from './ui/dialog'; interface RightPanelProps { user: User | null; onLogin: (user: User) => void; onLogout: () => void; isLoggedIn: boolean; onClose?: () => void; exportResult: string; setExportResult: (result: string) => void; resultType: 'export' | 'quiz' | 'summary' | null; setResultType: (type: 'export' | 'quiz' | 'summary' | null) => void; onExport: () => void; onSummary: () => void; onSave: (content: string, type: 'export' | 'quiz' | 'summary') => void; savedItems: SavedItem[]; } export function RightPanel({ user, onLogin, onLogout, isLoggedIn, onClose, exportResult, setExportResult, resultType, setResultType, onExport, onSummary, onSave, savedItems }: RightPanelProps) { const [showLoginForm, setShowLoginForm] = useState(false); const [name, setName] = useState(''); const [email, setEmail] = useState(''); const [isExpanded, setIsExpanded] = useState(true); const [isDownloading, setIsDownloading] = useState(false); const [copied, setCopied] = useState(false); // Check if current result is already saved const isSaved = exportResult && resultType ? savedItems.some(item => item.content === exportResult && item.type === resultType) : false; const handleLogin = () => { if (!name.trim() || !email.trim()) { toast.error('Please fill in all fields'); return; } onLogin({ name: name.trim(), email: email.trim() }); setShowLoginForm(false); setName(''); setEmail(''); toast.success(`Welcome, ${name}!`); }; const handleLogout = () => { onLogout(); setShowLoginForm(false); toast.success('Logged out successfully'); }; const scrollContainerRef = useRef(null); // Use native event listeners to prevent scroll propagation useEffect(() => { const container = scrollContainerRef.current; if (!container) return; const handleWheel = (e: WheelEvent) => { // Always stop propagation to prevent scrolling other panels e.stopPropagation(); e.stopImmediatePropagation(); // Only prevent default if we're at the boundaries const { scrollTop, scrollHeight, clientHeight } = container; const isScrollable = scrollHeight > clientHeight; const isAtTop = scrollTop === 0; const isAtBottom = scrollTop + clientHeight >= scrollHeight - 1; // If scrolling up at top or down at bottom, prevent default to stop propagation if (isScrollable && ((isAtTop && e.deltaY < 0) || (isAtBottom && e.deltaY > 0))) { e.preventDefault(); } }; container.addEventListener('wheel', handleWheel, { passive: false, capture: true }); return () => { container.removeEventListener('wheel', handleWheel, { capture: true }); }; }, []); const downloadBlob = (blob: Blob, filename: string) => { const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); a.remove(); URL.revokeObjectURL(url); }; const formatDateStamp = () => { const d = new Date(); const yyyy = d.getFullYear(); const mm = String(d.getMonth() + 1).padStart(2, '0'); const dd = String(d.getDate()).padStart(2, '0'); return `${yyyy}-${mm}-${dd}`; }; const getDefaultFilenameBase = () => { const kind = resultType === 'export' ? 'export' : resultType === 'summary' ? 'summary' : 'result'; return `clare-${kind}-${formatDateStamp()}`; }; const handleDownloadMd = async () => { if (!exportResult) return; try { setIsDownloading(true); toast.message('Preparing .md…'); const blob = new Blob([exportResult], { type: 'text/markdown;charset=utf-8' }); downloadBlob(blob, `${getDefaultFilenameBase()}.md`); toast.success('Downloaded .md'); } catch (e) { console.error(e); toast.error('Failed to download .md'); } finally { setIsDownloading(false); } }; const handleDownloadDocx = async () => { if (!exportResult) return; try { setIsDownloading(true); toast.message('Preparing .docx…'); const lines = exportResult.split('\n'); const paragraphs: Paragraph[] = lines.map((line) => { const trimmed = line.trim(); if (!trimmed) return new Paragraph({ text: '' }); // Basic markdown-ish heading support if (trimmed.startsWith('### ')) { return new Paragraph({ text: trimmed.replace(/^###\s+/, ''), heading: HeadingLevel.HEADING_3 }); } if (trimmed.startsWith('## ')) { return new Paragraph({ text: trimmed.replace(/^##\s+/, ''), heading: HeadingLevel.HEADING_2 }); } if (trimmed.startsWith('# ')) { return new Paragraph({ text: trimmed.replace(/^#\s+/, ''), heading: HeadingLevel.HEADING_1 }); } return new Paragraph({ children: [new TextRun({ text: line })] }); }); const doc = new Document({ sections: [{ properties: {}, children: paragraphs }], }); const blob = await Packer.toBlob(doc); downloadBlob(blob, `${getDefaultFilenameBase()}.docx`); toast.success('Downloaded .docx'); } catch (e) { console.error(e); toast.error('Failed to download .docx'); } finally { setIsDownloading(false); } }; return (
{isExpanded && ( <> {/* Actions Section with Results */}

Export / Summarize Conversation

{/* Results - Expanded from buttons */} {exportResult && ( <>

{resultType === 'export' && 'Exported Conversation'} {resultType === 'quiz' && 'Micro-Quiz'} {resultType === 'summary' && 'Summarization'}

{resultType && ( )}
{exportResult}
)}
)}
); }