'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 } 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 GhostEditorProps { isOpen: boolean; onClose: () => void; project?: Project | null; onSave: (projectData: any) => void; isCreating: boolean; } export const GhostEditor: React.FC = ({ isOpen, onClose, project, onSave, 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 [viewMode, setViewMode] = useState<'edit' | 'preview' | 'split'>('split'); const [showSettings, setShowSettings] = useState(false); const [wordCount, setWordCount] = useState(0); const [readingTime, setReadingTime] = useState(0); const titleRef = useRef(null); const contentRef = useRef(null); const previewRef = 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, isOpen]); // 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]); 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'; }; // Render markdown preview const renderMarkdownPreview = (markdown: string) => { // Simple markdown renderer for preview let html = markdown // Headers .replace(/^### (.*$)/gim, '

$1

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

$1

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

$1

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

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

    ${html}

    `; }; if (!isOpen) return null; return ( {/* Professional Ghost Editor */}
    {/* Top Navigation Bar */}
    {isCreating ? 'New Project' : 'Editing Project'}
    {published ? ( Published ) : ( Draft )} {featured && ( Featured )}
    {/* View Mode Toggle */}
    {/* Rich Text Toolbar */}
    {/* Text Formatting */}
    {/* Headers */}
    {/* Lists */}
    {/* Insert Elements */}
    {/* Stats */}
    {wordCount} words {readingTime} min read
    {/* Main Editor Area */}
    {/* Content Area */}
    {/* Editor Pane */} {(viewMode === 'edit' || viewMode === 'split') && (
    {/* Title & Description */}