import { useState, useEffect, useRef, useCallback } from 'react' import { cn } from '@/lib/utils' import { MarkdownContent } from '@/components/ui/MarkdownContent' import { StickyNote, ChevronRight, Eye, Pencil, Loader2 } from 'lucide-react' interface ScratchpadSidebarProps { sessionId: string initialContent: string onSave: (content: string) => Promise } export function ScratchpadSidebar({ sessionId, initialContent, onSave }: ScratchpadSidebarProps) { const [content, setContent] = useState(initialContent) const [lastSaved, setLastSaved] = useState(initialContent) const [isCollapsed, setIsCollapsed] = useState(() => { return localStorage.getItem('scratchpad-collapsed') === 'true' }) const [isSaving, setIsSaving] = useState(false) const [showPreview, setShowPreview] = useState(false) const [saveStatus, setSaveStatus] = useState<'idle' | 'unsaved' | 'saving' | 'saved' | 'error'>('idle') const debounceRef = useRef | null>(null) const fadeTimerRef = useRef | null>(null) const hasUnsavedChanges = content !== lastSaved // Reset content when session changes useEffect(() => { setContent(initialContent) setLastSaved(initialContent) setSaveStatus('idle') }, [sessionId]) // eslint-disable-line react-hooks/exhaustive-deps // Update save status based on state useEffect(() => { if (isSaving) { setSaveStatus('saving') } else if (hasUnsavedChanges) { setSaveStatus('unsaved') } }, [isSaving, hasUnsavedChanges]) // Persist collapse state useEffect(() => { localStorage.setItem('scratchpad-collapsed', String(isCollapsed)) }, [isCollapsed]) // beforeunload warning useEffect(() => { if (!hasUnsavedChanges) return const handler = (e: BeforeUnloadEvent) => { e.preventDefault() } window.addEventListener('beforeunload', handler) return () => window.removeEventListener('beforeunload', handler) }, [hasUnsavedChanges]) const doSave = useCallback(async (text: string) => { setIsSaving(true) try { await onSave(text) setLastSaved(text) setSaveStatus('saved') // Clear "Saved" indicator after 2 seconds if (fadeTimerRef.current) clearTimeout(fadeTimerRef.current) fadeTimerRef.current = setTimeout(() => setSaveStatus('idle'), 2000) } catch { setSaveStatus('error') } finally { setIsSaving(false) } }, [onSave]) const handleChange = (value: string) => { setContent(value) // Cancel any pending debounce if (debounceRef.current) clearTimeout(debounceRef.current) // Schedule save after 1000ms of inactivity debounceRef.current = setTimeout(() => { if (value !== lastSaved) { doSave(value) } }, 1000) } const handleBlur = () => { // Cancel pending debounce and save immediately if (debounceRef.current) clearTimeout(debounceRef.current) if (content !== lastSaved) { doSave(content) } } // Cleanup timers on unmount useEffect(() => { return () => { if (debounceRef.current) clearTimeout(debounceRef.current) if (fadeTimerRef.current) clearTimeout(fadeTimerRef.current) } }, []) if (isCollapsed) { return (
{hasUnsavedChanges && (
)}
) } return (
{/* Header */}
Scratchpad
{/* Content */}
{showPreview ? (
{content.trim() ? ( ) : (

Nothing to preview

)}
) : (