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 10→} from 'lucide-react'; 11→import { Button } from '@/components/ui/button'; 12→import { Textarea } from '@/components/ui/textarea'; 13→import { Progress } from '@/components/ui/progress'; 14→import { Card, CardContent } from '@/components/ui/card'; 15→import { 16→ Dialog, 17→ DialogContent, 18→ DialogHeader, 19→ DialogTitle, 20→ DialogTrigger, 21→} from '@/components/ui/dialog'; 22→import { useAppStore, type PhotoProject } from '@/store/photo-store'; 23→ 24→/* ─── Edit Tool Definitions ─── */ 25→const EDIT_TOOLS = [ 26→ { 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' }, 27→ { 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' }, 28→ { 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' }, 29→ { 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' }, 30→ { 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' }, 31→ { id: 'bg-remove', label: 'خلفية احترافية', icon: Layers, prompt: 'Replace the background with a clean, modern, professional studio-like background, keep the main subject exactly the same with proper lighting', color: 'from-cyan-500/20 to-blue-600/20' }, 32→ { 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' }, 33→ { 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' }, 34→]; 35→ 36→/* ─── Header Component ─── */ 37→function Header() { 38→ const { currentView, setView } = useAppStore(); 39→ return ( 40→
41→
42→ 51→ 71→
72→
73→ ); 74→} 75→ 76→/* ─── Footer Component ─── */ 77→function Footer() { 78→ return ( 79→ 85→ ); 86→} 87→ 88→/* ─── Upload Area Component ─── */ 89→function UploadArea() { 90→ const { setOriginalImage, setOriginalFileName, setView, setAnalysis, setEditedImage, setEditError } = useAppStore(); 91→ const [isDragOver, setIsDragOver] = useState(false); 92→ const [isUploading, setIsUploading] = useState(false); 93→ const fileInputRef = useRef(null); 94→ 95→ const handleFile = useCallback(async (file: File) => { 96→ if (!file.type.startsWith('image/')) return; 97→ setIsUploading(true); 98→ setEditError(null); 99→ setEditedImage(null); 100→ setAnalysis(null); 101→ try { 102→ const formData = new FormData(); 103→ formData.append('image', file); 104→ 105→ const res = await fetch('/api/upload', { method: 'POST', body: formData }); 106→ const data = await res.json(); 107→ if (!res.ok) throw new Error(data.error || 'فشل رفع الصورة'); 108→ 109→ setOriginalImage(data.imageUrl); 110→ setOriginalFileName(file.name); 111→ setView('editor'); 112→ } catch (err: unknown) { 113→ const message = err instanceof Error ? err.message : 'حدث خطأ'; 114→ setEditError(message); 115→ } finally { 116→ setIsUploading(false); 117→ } 118→ }, [setOriginalImage, setOriginalFileName, setView, setEditedImage, setAnalysis, setEditError]); 119→ 120→ const onDrop = useCallback((e: React.DragEvent) => { 121→ e.preventDefault(); 122→ setIsDragOver(false); 123→ const file = e.dataTransfer.files[0]; 124→ if (file) handleFile(file); 125→ }, [handleFile]); 126→ 127→ const onDragOver = useCallback((e: React.DragEvent) => { 128→ e.preventDefault(); 129→ setIsDragOver(true); 130→ }, []); 131→ 132→ const onDragLeave = useCallback(() => setIsDragOver(false), []); 133→ 134→ return ( 135→ 141→
fileInputRef.current?.click()} 146→ className={` 147→ relative cursor-pointer rounded-2xl border-2 border-dashed p-12 sm:p-16 148→ transition-all duration-300 text-center 149→ ${isDragOver 150→ ? 'upload-area-active border-primary' 151→ : 'border-muted-foreground/30 hover:border-primary/50 hover:bg-primary/5' 152→ } 153→ `} 154→ > 155→ { 161→ const file = e.target.files?.[0]; 162→ if (file) handleFile(file); 163→ e.target.value = ''; 164→ }} 165→ /> 166→ {isUploading ? ( 167→
168→ 169→

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

170→
171→ ) : ( 172→
173→
174→ 175→
176→
177→

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

178→

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

179→
180→
181→ )} 182→
183→
184→ ); 185→} 186→ 187→/* ─── Feature Cards ─── */ 188→function FeatureCards() { 189→ const features = [ 190→ { icon: Sparkles, title: 'تحرير بالذكاء', desc: 'أدوات تعديل ذكية تفهم صورتك وتحسّنها' }, 191→ { icon: Palette, title: 'أنماط فنية', desc: 'حوّل صورك إلى لوحات زيتية أو رسوم كرتونية' }, 192→ { icon: SlidersHorizontal, title: 'مقارنة فورية', desc: 'قارن بين الأصل والنسخة المحرّرة بسهولة' }, 193→ { icon: Download, title: 'تحميل مباشر', desc: 'حمّل الصور المحرّرة بجودة عالية' }, 194→ ]; 195→ return ( 196→
197→ {features.map((f, i) => ( 198→ 204→ 205→ 206→
207→ 208→
209→

{f.title}

210→

{f.desc}

211→
212→
213→
214→ ))} 215→
216→ ); 217→} 218→ 219→/* ─── Home View ─── */ 220→function HomeView() { 221→ return ( 222→
223→ 229→

230→ محرر الصور بالذكاء الاصطناعي 231→

232→

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

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

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

503→ 504→

{editProgress}%

505→
506→ )} 507→ 508→ {editedImage ? ( 509→ 510→ ) : ( 511→ Original 516→ )} 517→
518→ 519→ {/* Image info bar */} 520→
521→ {originalFileName || 'صورة'} 522→ 526→ 527→
528→
529→
530→ 531→ {/* Analysis section */} 532→
533→ 546→ 547→ {analysis && ( 548→ 553→ 554→ 555→

556→ 557→ نتيجة التحليل 558→

559→

{analysis}

560→
561→
562→
563→ )} 564→
565→
566→
567→ 568→ {/* Tools Panel (1/3 width on desktop) */} 569→
570→ {/* Quick Tools */} 571→ 572→ 573→

574→ 575→ أدوات التحرير السريعة 576→

577→
578→ {EDIT_TOOLS.map((tool) => { 579→ const Icon = tool.icon; 580→ const isLoading = isEditing && selectedTool === tool.id; 581→ return ( 582→ 606→ ); 607→ })} 608→
609→
610→
611→ 612→ {/* Custom Prompt */} 613→ 614→ 615→

616→ 617→ تعديل مخصص 618→

619→