1→'use client'; 2→ 3→import { useState, useCallback, useRef, useEffect } from 'react'; 4→import { motion, AnimatePresence } from 'framer-motion'; 5→import { 6→ Upload, Image as ImageIcon, Sparkles, Download, ArrowRight, 7→ Palette, Camera, Layers, SlidersHorizontal, Loader2, Check, 8→ Trash2, RotateCcw, Eye, Wand2, Sun, Paintbrush, Film, 9→ Pencil, History, ChevronLeft, X, GripVertical, WandSparkles, 10→ Square, RectangleVertical, Monitor 11→} from 'lucide-react'; 12→import { Button } from '@/components/ui/button'; 13→import { Textarea } from '@/components/ui/textarea'; 14→import { Progress } from '@/components/ui/progress'; 15→import { Card, CardContent } from '@/components/ui/card'; 16→import { 17→ Dialog, 18→ DialogContent, 19→ DialogHeader, 20→ DialogTitle, 21→ DialogTrigger, 22→} from '@/components/ui/dialog'; 23→import { useAppStore, type PhotoProject } from '@/store/photo-store'; 24→ 25→/* ─── Edit Tool Definitions ─── */ 26→const EDIT_TOOLS = [ 27→ { id: 'oil-painting', label: 'لوحة زيتية', icon: Paintbrush, prompt: 'Transform this image into an oil painting style, with visible brushstrokes and rich, vibrant colors, maintain the composition and subject', color: 'from-amber-500/20 to-orange-600/20' }, 28→ { id: 'cartoon', label: 'رسم كرتوني', icon: Pencil, prompt: 'Transform this image into a cartoon illustration style, with bold outlines, flat colors, and simplified shapes, keep the main subject recognizable', color: 'from-pink-500/20 to-rose-600/20' }, 29→ { id: 'bw', label: 'أبيض وأسود', icon: Camera, prompt: 'Convert this image to a stunning black and white photograph with high contrast and dramatic lighting, professional photography style', color: 'from-gray-500/20 to-gray-700/20' }, 30→ { id: 'golden-hour', label: 'إضاءة ذهبية', icon: Sun, prompt: 'Apply warm golden hour lighting to this image, with beautiful warm tones, soft shadows, and a magical sunset glow, maintain all subjects', color: 'from-yellow-500/20 to-amber-600/20' }, 31→ { id: 'enhance', label: 'تحسين الجودة', icon: Sparkles, prompt: 'Enhance the quality and sharpness of this image, improve colors, add more detail and clarity, make it look professional and high-resolution', color: 'from-emerald-500/20 to-teal-600/20' }, 32→ { id: 'bg-remove', label: 'خلفية احترافية', icon: Layers, prompt: 'Enhance this photo with a clean neutral studio background while preserving the main subject naturally', color: 'from-cyan-500/20 to-blue-600/20' }, 33→ { id: 'cinematic', label: 'تأثير سينمائي', icon: Film, prompt: 'Apply a cinematic color grading to this image, with deep shadows, teal and orange tones, letterbox style, dramatic and moody atmosphere', color: 'from-violet-500/20 to-purple-600/20' }, 34→ { id: 'illustration', label: 'أسلوب رسومي', icon: Palette, prompt: 'Transform this image into a digital illustration style, clean lines, artistic rendering, vibrant but harmonious colors, maintain the scene composition', color: 'from-fuchsia-500/20 to-pink-600/20' }, 35→]; 36→ 37→/* ─── Header Component ─── */ 38→function Header() { 39→ const { currentView, setView } = useAppStore(); 40→ return ( 41→
42→
43→ 52→ 81→
82→
83→ ); 84→} 85→ 86→/* ─── Footer Component ─── */ 87→function Footer() { 88→ return ( 89→ 95→ ); 96→} 97→ 98→/* ─── Upload Area Component ─── */ 99→function UploadArea() { 100→ const { setOriginalImage, setOriginalFileName, setView, setAnalysis, setEditedImage, setEditError } = useAppStore(); 101→ const [isDragOver, setIsDragOver] = useState(false); 102→ const [isUploading, setIsUploading] = useState(false); 103→ const fileInputRef = useRef(null); 104→ 105→ const handleFile = useCallback(async (file: File) => { 106→ if (!file.type.startsWith('image/')) return; 107→ setIsUploading(true); 108→ setEditError(null); 109→ setEditedImage(null); 110→ setAnalysis(null); 111→ try { 112→ const formData = new FormData(); 113→ formData.append('image', file); 114→ 115→ const res = await fetch('/api/upload', { method: 'POST', body: formData }); 116→ const data = await res.json(); 117→ if (!res.ok) throw new Error(data.error || 'فشل رفع الصورة'); 118→ 119→ setOriginalImage(data.imageUrl); 120→ setOriginalFileName(file.name); 121→ setView('editor'); 122→ } catch (err: unknown) { 123→ const message = err instanceof Error ? err.message : 'حدث خطأ'; 124→ setEditError(message); 125→ } finally { 126→ setIsUploading(false); 127→ } 128→ }, [setOriginalImage, setOriginalFileName, setView, setEditedImage, setAnalysis, setEditError]); 129→ 130→ const onDrop = useCallback((e: React.DragEvent) => { 131→ e.preventDefault(); 132→ setIsDragOver(false); 133→ const file = e.dataTransfer.files[0]; 134→ if (file) handleFile(file); 135→ }, [handleFile]); 136→ 137→ const onDragOver = useCallback((e: React.DragEvent) => { 138→ e.preventDefault(); 139→ setIsDragOver(true); 140→ }, []); 141→ 142→ const onDragLeave = useCallback(() => setIsDragOver(false), []); 143→ 144→ return ( 145→ 151→
fileInputRef.current?.click()} 156→ className={` 157→ relative cursor-pointer rounded-2xl border-2 border-dashed p-12 sm:p-16 158→ transition-all duration-300 text-center 159→ ${isDragOver 160→ ? 'upload-area-active border-primary' 161→ : 'border-muted-foreground/30 hover:border-primary/50 hover:bg-primary/5' 162→ } 163→ `} 164→ > 165→ { 171→ const file = e.target.files?.[0]; 172→ if (file) handleFile(file); 173→ e.target.value = ''; 174→ }} 175→ /> 176→ {isUploading ? ( 177→
178→ 179→

جارٍ رفع الصورة...

180→
181→ ) : ( 182→
183→
184→ 185→
186→
187→

اسحب الصورة هنا أو انقر للاختيار

188→

يدعم: PNG, JPG, WEBP — حتى 10 ميجا

189→
190→
191→ )} 192→
193→
194→ ); 195→} 196→ 197→/* ─── Feature Cards ─── */ 198→function FeatureCards() { 199→ const features = [ 200→ { icon: Sparkles, title: 'تحرير بالذكاء', desc: 'أدوات تعديل ذكية تفهم صورتك وتحسّنها' }, 201→ { icon: Palette, title: 'أنماط فنية', desc: 'حوّل صورك إلى لوحات زيتية أو رسوم كرتونية' }, 202→ { icon: SlidersHorizontal, title: 'مقارنة فورية', desc: 'قارن بين الأصل والنسخة المحرّرة بسهولة' }, 203→ { icon: Download, title: 'تحميل مباشر', desc: 'حمّل الصور المحرّرة بجودة عالية' }, 204→ ]; 205→ return ( 206→
207→ {features.map((f, i) => ( 208→ 214→ 215→ 216→
217→ 218→
219→

{f.title}

220→

{f.desc}

221→
222→
223→
224→ ))} 225→
226→ ); 227→} 228→ 229→/* ─── Home View ─── */ 230→function HomeView() { 231→ return ( 232→
233→ 239→

240→ محرر الصور Fouad AI 241→

242→

243→ ارفع صورتك واختر التأثير المطلوب — وشاهد الذكاء الاصطناعي يُبدع 244→

245→
246→ 247→ 248→
249→ ); 250→} 251→ 252→/* ─── Before/After Comparison Slider ─── */ 253→function ComparisonSlider({ original, edited }: { original: string; edited: string }) { 254→ const containerRef = useRef(null); 255→ const [position, setPosition] = useState(50); 256→ const [containerWidth, setContainerWidth] = useState(0); 257→ const isDragging = useRef(false); 258→ 259→ useEffect(() => { 260→ const el = containerRef.current; 261→ if (!el) return; 262→ const observer = new ResizeObserver((entries) => { 263→ for (const entry of entries) { 264→ setContainerWidth(entry.contentRect.width); 265→ } 266→ }); 267→ observer.observe(el); 268→ return () => observer.disconnect(); 269→ }, []); 270→ 271→ const updatePosition = useCallback((clientX: number) => { 272→ if (!containerRef.current) return; 273→ const rect = containerRef.current.getBoundingClientRect(); 274→ // RTL: position is measured from the right edge 275→ const x = clientX - rect.left; 276→ const pct = Math.min(100, Math.max(0, (x / rect.width) * 100)); 277→ setPosition(pct); 278→ }, []); 279→ 280→ const handlePointerDown = useCallback((e: React.PointerEvent) => { 281→ isDragging.current = true; 282→ (e.target as HTMLElement).setPointerCapture(e.pointerId); 283→ updatePosition(e.clientX); 284→ }, [updatePosition]); 285→ 286→ const handlePointerMove = useCallback((e: React.PointerEvent) => { 287→ if (!isDragging.current) return; 288→ updatePosition(e.clientX); 289→ }, [updatePosition]); 290→ 291→ const handlePointerUp = useCallback(() => { 292→ isDragging.current = false; 293→ }, []); 294→ 295→ return ( 296→
304→ {/* Edited image (full width, behind) */} 305→ After 311→ 312→ {/* Original image (clipped from right in RTL) */} 313→
317→ Before 0 ? `${containerWidth}px` : '100%' }} 322→ draggable={false} 323→ /> 324→
325→ 326→ {/* Slider line */} 327→
331→
332→ 333→
334→
335→ 336→ {/* Labels */} 337→
الأصلي
338→
المحرّر
339→
340→ ); 341→} 342→ 343→/* ─── Editor View ─── */ 344→function EditorView() { 345→ const { 346→ originalImage, editedImage, isEditing, editProgress, editError, 347→ analysis, customPrompt, setCustomPrompt, setEditedImage, 348→ setEditProgress, setIsEditing, setEditError, setAnalysis, 349→ setView, resetEditor, projects, setProjects, originalFileName, 350→ } = useAppStore(); 351→ const [selectedTool, setSelectedTool] = useState(null); 352→ const [lastEditArgs, setLastEditArgs] = useState<{ prompt: string; label: string; toolType: string } | null>(null); 353→ const [editCount, setEditCount] = useState(0); 354→ const [isAnalyzing, setIsAnalyzing] = useState(false); 355→ const fileInputRef = useRef(null); 356→ 357→ const handleAnalyze = useCallback(async () => { 358→ if (!originalImage) return; 359→ setIsAnalyzing(true); 360→ try { 361→ const res = await fetch('/api/analyze', { 362→ method: 'POST', 363→ headers: { 'Content-Type': 'application/json' }, 364→ body: JSON.stringify({ imageUrl: originalImage }), 365→ }); 366→ const data = await res.json(); 367→ if (!res.ok) throw new Error(data.error || 'فشل التحليل'); 368→ setAnalysis(data.analysis); 369→ } catch (err: unknown) { 370→ const message = err instanceof Error ? err.message : 'حدث خطأ'; 371→ setEditError(message); 372→ } finally { 373→ setIsAnalyzing(false); 374→ } 375→ }, [originalImage, setAnalysis, setEditError]); 376→ 377→ const handleEdit = useCallback(async (prompt: string, label: string, toolType: string) => { 378→ // Use the last edited image as base for stacking edits 379→ const baseImage = editedImage || originalImage; 380→ if (!baseImage) return; 381→ setLastEditArgs({ prompt, label, toolType }); 382→ setIsEditing(true); 383→ setEditError(null); 384→ setEditedImage(null); 385→ setEditProgress(0); 386→ 387→ try { 388→ const res = await fetch('/api/edit', { 389→ method: 'POST', 390→ headers: { 'Content-Type': 'application/json' }, 391→ body: JSON.stringify({ 392→ imageUrl: baseImage, 393→ prompt, 394→ label, 395→ toolType, 396→ }), 397→ }); 398→ 399→ if (!res.ok) { 400→ const data = await res.json(); 401→ throw new Error(data.error || 'فشل التعديل'); 402→ } 403→ 404→ const reader = res.body?.getReader(); 405→ if (!reader) throw new Error('لا يمكن قراءة الاستجابة'); 406→ 407→ const decoder = new TextDecoder(); 408→ let buffer = ''; 409→ 410→ while (true) { 411→ const { done, value } = await reader.read(); 412→ if (done) break; 413→ 414→ buffer += decoder.decode(value, { stream: true }); 415→ const lines = buffer.split('\n'); 416→ buffer = lines.pop() || ''; 417→ 418→ for (const line of lines) { 419→ if (line.startsWith('data: ')) { 420→ const data = line.slice(6); 421→ let parsed: Record; 422→ try { 423→ parsed = JSON.parse(data); 424→ } catch { 425→ continue; // Skip malformed JSON lines 426→ } 427→ 428→ if (parsed.type === 'progress') { 429→ setEditProgress(parsed.value as number); 430→ } else if (parsed.type === 'done') { 431→ setEditedImage(parsed.editedImageUrl as string); 432→ setEditProgress(100); 433→ setEditCount((c) => c + 1); 434→ // Refresh gallery 435→ try { 436→ const projectsRes = await fetch('/api/projects'); 437→ if (projectsRes.ok) { 438→ const projectsData = await projectsRes.json(); 439→ setProjects(projectsData); 440→ } 441→ } catch { /* ignore gallery refresh failure */ } 442→ } else if (parsed.type === 'error') { 443→ throw new Error(parsed.message as string); 444→ } 445→ } 446→ } 447→ } 448→ } catch (err: unknown) { 449→ const message = err instanceof Error ? err.message : 'حدث خطأ أثناء التعديل'; 450→ setEditError(message); 451→ } finally { 452→ setIsEditing(false); 453→ setSelectedTool(null); 454→ } 455→ }, [originalImage, editedImage, setIsEditing, setEditError, setEditedImage, setEditProgress, setProjects]); 456→ 457→ const handleCustomEdit = useCallback(() => { 458→ if (!customPrompt.trim()) return; 459→ handleEdit(customPrompt.trim(), 'تعديل مخصص', 'custom'); 460→ }, [customPrompt, handleEdit]); 461→ 462→ const handleNewImage = useCallback(() => { 463→ resetEditor(); 464→ setView('home'); 465→ }, [resetEditor, setView]); 466→ 467→ const handleResetToOriginal = useCallback(() => { 468→ setEditedImage(null); 469→ setEditCount(0); 470→ setEditError(null); 471→ }, [setEditedImage, setEditError]); 472→ 473→ const handleReupload = useCallback(async (e: React.ChangeEvent) => { 474→ const file = e.target.files?.[0]; 475→ if (!file || !file.type.startsWith('image/')) return; 476→ const formData = new FormData(); 477→ formData.append('image', file); 478→ try { 479→ const res = await fetch('/api/upload', { method: 'POST', body: formData }); 480→ const data = await res.json(); 481→ if (!res.ok) throw new Error(data.error); 482→ useAppStore.getState().setOriginalImage(data.imageUrl); 483→ useAppStore.getState().setOriginalFileName(file.name); 484→ useAppStore.getState().setEditedImage(null); 485→ useAppStore.getState().setAnalysis(null); 486→ } catch {} 487→ e.target.value = ''; 488→ }, []); 489→ 490→ const handleDownload = useCallback(() => { 491→ if (!editedImage) return; 492→ const a = document.createElement('a'); 493→ a.href = editedImage; 494→ const ext = originalFileName?.split('.').pop() || 'png'; 495→ const baseName = originalFileName ? originalFileName.replace(/\.[^.]+$/, '') : 'image'; 496→ a.download = `Fouad-AI-${baseName}.${ext}`; 497→ a.click(); 498→ }, [editedImage, originalFileName]); 499→ 500→ return ( 501→
502→ {/* Top bar */} 503→
504→
505→ 509→ {editedImage && ( 510→ 514→ )} 515→ {editCount > 0 && ( 516→ 517→ {editCount} تعديل{editCount > 1 ? 'ات' : ''} 518→ 519→ )} 520→
521→ {editedImage && ( 522→ 526→ )} 527→
528→ 529→
530→ {/* Image Preview (2/3 width on desktop) */} 531→
532→ 533→ 534→ {/* Image display */} 535→
536→ {isEditing && ( 537→
538→ 539→

جارٍ التعديل بالذكاء الاصطناعي...

540→ 541→

{editProgress}%

542→
543→ )} 544→ 545→ {editedImage ? ( 546→ 547→ ) : ( 548→ Original 553→ )} 554→
555→ 556→ {/* Image info bar */} 557→
558→ {originalFileName || 'صورة'} 559→ 563→ 564→
565→
566→
567→ 568→ {/* Analysis section */} 569→
570→ 583→ 584→ {analysis && ( 585→ 590→ 591→ 592→

593→ 594→ نتيجة التحليل 595→

596→

{analysis}

597→
598→
599→
600→ )} 601→
602→
603→
604→ 605→ {/* Tools Panel (1/3 width on desktop) */} 606→
607→ {/* Quick Tools */} 608→ 609→ 610→

611→ 612→ أدوات التحرير السريعة 613→

614→
615→ {EDIT_TOOLS.map((tool) => { 616→ const Icon = tool.icon; 617→ const isLoading = isEditing && selectedTool === tool.id; 618→ return ( 619→ 643→ ); 644→ })} 645→
646→
647→
648→ 649→ {/* Custom Prompt */} 650→ 651→ 652→

653→ 654→ تعديل مخصص 655→

656→