'use client'; import React, { useState, useRef, useEffect, useCallback } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { Save, X, Eye, EyeOff, Settings, Link as LinkIcon, Tag, Calendar, Globe, Github, Image as ImageIcon, Bold, Italic, List, Hash, Quote, Code, Zap, Type, Columns, PanelLeft, PanelRight, Monitor, Smartphone, Tablet, Undo, Redo, AlignLeft, AlignCenter, AlignRight, Link2, ListOrdered, Underline, Strikethrough, GripVertical } from 'lucide-react'; interface Project { id: string; title: string; description: string; content?: string; category: string; difficulty?: string; tags?: string[]; featured: boolean; published: boolean; github?: string; live?: string; image?: string; createdAt: string; updatedAt: string; } interface ResizableGhostEditorProps { project?: Project | null; onSave: (projectData: any) => void; onClose: () => void; isCreating: boolean; } export const ResizableGhostEditor: React.FC = ({ project, onSave, onClose, isCreating }) => { const [title, setTitle] = useState(''); const [description, setDescription] = useState(''); const [content, setContent] = useState(''); const [category, setCategory] = useState('Web Development'); const [tags, setTags] = useState([]); const [github, setGithub] = useState(''); const [live, setLive] = useState(''); const [featured, setFeatured] = useState(false); const [published, setPublished] = useState(false); const [difficulty, setDifficulty] = useState('Intermediate'); // Editor UI state const [showPreview, setShowPreview] = useState(true); const [showSettings, setShowSettings] = useState(false); const [previewWidth, setPreviewWidth] = useState(50); // Percentage const [wordCount, setWordCount] = useState(0); const [readingTime, setReadingTime] = useState(0); const [isResizing, setIsResizing] = useState(false); const titleRef = useRef(null); const contentRef = useRef(null); const previewRef = useRef(null); const resizeRef = useRef(null); const categories = ['Web Development', 'Full-Stack', 'Web Application', 'Mobile App', 'Design']; const difficulties = ['Beginner', 'Intermediate', 'Advanced', 'Expert']; useEffect(() => { if (project && !isCreating) { setTitle(project.title); setDescription(project.description); setContent(project.content || ''); setCategory(project.category); setTags(project.tags || []); setGithub(project.github || ''); setLive(project.live || ''); setFeatured(project.featured); setPublished(project.published); setDifficulty(project.difficulty || 'Intermediate'); } else { // Reset for new project setTitle(''); setDescription(''); setContent(''); setCategory('Web Development'); setTags([]); setGithub(''); setLive(''); setFeatured(false); setPublished(false); setDifficulty('Intermediate'); } }, [project, isCreating]); // Calculate word count and reading time useEffect(() => { const words = content.trim().split(/\s+/).filter(word => word.length > 0).length; setWordCount(words); setReadingTime(Math.ceil(words / 200)); // Average reading speed: 200 words/minute }, [content]); // Handle resizing useEffect(() => { const handleMouseMove = (e: MouseEvent) => { if (!isResizing) return; const containerWidth = window.innerWidth - (showSettings ? 320 : 0); // Account for settings sidebar const newWidth = Math.max(20, Math.min(80, (e.clientX / containerWidth) * 100)); setPreviewWidth(100 - newWidth); // Invert since we're setting editor width }; const handleMouseUp = () => { setIsResizing(false); }; if (isResizing) { document.addEventListener('mousemove', handleMouseMove); document.addEventListener('mouseup', handleMouseUp); } return () => { document.removeEventListener('mousemove', handleMouseMove); document.removeEventListener('mouseup', handleMouseUp); }; }, [isResizing, showSettings]); const handleSave = () => { const projectData = { title, description, content, category, tags, github, live, featured, published, difficulty }; onSave(projectData); }; const addTag = (tag: string) => { if (tag.trim() && !tags.includes(tag.trim())) { setTags([...tags, tag.trim()]); } }; const removeTag = (tagToRemove: string) => { setTags(tags.filter(tag => tag !== tagToRemove)); }; const insertMarkdown = useCallback((syntax: string, selectedText: string = '') => { if (!contentRef.current) return; const textarea = contentRef.current; const start = textarea.selectionStart; const end = textarea.selectionEnd; const selection = selectedText || content.substring(start, end); let newText = ''; let cursorOffset = 0; switch (syntax) { case 'bold': newText = `**${selection || 'bold text'}**`; cursorOffset = selection ? newText.length : 2; break; case 'italic': newText = `*${selection || 'italic text'}*`; cursorOffset = selection ? newText.length : 1; break; case 'underline': newText = `${selection || 'underlined text'}`; cursorOffset = selection ? newText.length : 3; break; case 'strikethrough': newText = `~~${selection || 'strikethrough text'}~~`; cursorOffset = selection ? newText.length : 2; break; case 'heading1': newText = `# ${selection || 'Heading 1'}`; cursorOffset = selection ? newText.length : 2; break; case 'heading2': newText = `## ${selection || 'Heading 2'}`; cursorOffset = selection ? newText.length : 3; break; case 'heading3': newText = `### ${selection || 'Heading 3'}`; cursorOffset = selection ? newText.length : 4; break; case 'list': newText = `- ${selection || 'List item'}`; cursorOffset = selection ? newText.length : 2; break; case 'list-ordered': newText = `1. ${selection || 'List item'}`; cursorOffset = selection ? newText.length : 3; break; case 'quote': newText = `> ${selection || 'Quote'}`; cursorOffset = selection ? newText.length : 2; break; case 'code': if (selection.includes('\n')) { newText = `\`\`\`\n${selection || 'code block'}\n\`\`\``; cursorOffset = selection ? newText.length : 4; } else { newText = `\`${selection || 'code'}\``; cursorOffset = selection ? newText.length : 1; } break; case 'link': newText = `[${selection || 'link text'}](url)`; cursorOffset = selection ? newText.length - 4 : newText.length - 4; break; case 'image': newText = `![${selection || 'alt text'}](image-url)`; cursorOffset = selection ? newText.length - 11 : newText.length - 11; break; case 'divider': newText = '\n---\n'; cursorOffset = newText.length; break; default: return; } const newContent = content.substring(0, start) + newText + content.substring(end); setContent(newContent); // Focus and set cursor position setTimeout(() => { textarea.focus(); const newPosition = start + cursorOffset; textarea.setSelectionRange(newPosition, newPosition); }, 0); }, [content]); const autoResizeTextarea = (element: HTMLTextAreaElement) => { element.style.height = 'auto'; element.style.height = element.scrollHeight + 'px'; }; // Enhanced markdown renderer with proper white text const renderMarkdownPreview = (markdown: string) => { let html = markdown // Headers - WHITE TEXT .replace(/^### (.*$)/gim, '

$1

') .replace(/^## (.*$)/gim, '

$1

') .replace(/^# (.*$)/gim, '

$1

') // Bold and Italic - WHITE TEXT .replace(/\*\*(.*?)\*\*/g, '$1') .replace(/\*(.*?)\*/g, '$1') // Underline and Strikethrough - WHITE TEXT .replace(/(.*?)<\/u>/g, '$1') .replace(/~~(.*?)~~/g, '$1') // Code .replace(/```([^`]+)```/g, '
$1
') .replace(/`([^`]+)`/g, '$1') // Lists - WHITE TEXT .replace(/^\- (.*$)/gim, '
  • • $1
  • ') .replace(/^\d+\. (.*$)/gim, '
  • $1
  • ') // Links .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1') // Images .replace(/!\[([^\]]*)\]\(([^)]+)\)/g, '$1') // Quotes - WHITE TEXT .replace(/^> (.*$)/gim, '
    $1
    ') // Dividers .replace(/^---$/gim, '
    ') // Paragraphs - WHITE TEXT .replace(/\n\n/g, '

    ') .replace(/\n/g, '
    '); return `

    ${html}

    `; }; return (
    {/* Professional Ghost Editor */}
    {/* Top Navigation Bar */}
    {isCreating ? 'New Project' : 'Editing Project'}
    {published ? ( Published ) : ( Draft )} {featured && ( Featured )}
    {/* Controls */}
    {/* Preview Toggle */}
    {/* Rich Text Toolbar */}
    {/* Text Formatting */}
    {/* Headers */}
    {/* Lists */}
    {/* Insert Elements */}
    {/* Stats */}
    {wordCount} words {readingTime} min read {showPreview && ( Preview: {previewWidth}% )}
    {/* Main Editor Area */}
    {/* Content Area */}
    {/* Editor Pane */}
    {/* Title & Description */}