'use client'; import { useState, useEffect, Suspense } from 'react'; import { useSearchParams, useRouter } from 'next/navigation'; import { ColorPicker } from '@/components/color/ColorPicker'; import { ColorInfo } from '@/components/color/ColorInfo'; import { ManipulationPanel } from '@/components/color/ManipulationPanel'; import { PaletteGrid } from '@/components/color/PaletteGrid'; import { ExportMenu } from '@/components/color/ExportMenu'; import { useColorInfo, useGeneratePalette, useGenerateGradient } from '@/lib/color/api/queries'; import { Loader2, Share2, Plus, X, Palette, Layers } from 'lucide-react'; import { toast } from 'sonner'; import { cn, actionBtn, cardBtn } from '@/lib/utils'; import { MobileTabs } from '@/components/ui/mobile-tabs'; type HarmonyType = 'monochromatic' | 'analogous' | 'complementary' | 'triadic' | 'tetradic'; type RightTab = 'info' | 'adjust' | 'harmony' | 'gradient'; type MobileTab = 'pick' | 'explore'; const HARMONY_OPTS: { value: HarmonyType; label: string; desc: string }[] = [ { value: 'monochromatic', label: 'Mono', desc: 'Single hue, varied lightness' }, { value: 'analogous', label: 'Analogous', desc: 'Adjacent colors ±30°' }, { value: 'complementary', label: 'Complement', desc: 'Opposite on wheel 180°' }, { value: 'triadic', label: 'Triadic', desc: 'Three equal 120° steps' }, { value: 'tetradic', label: 'Tetradic', desc: 'Four equal 90° steps' }, ]; const RIGHT_TABS: { value: RightTab; label: string }[] = [ { value: 'info', label: 'Info' }, { value: 'adjust', label: 'Adjust' }, { value: 'harmony', label: 'Harmony' }, { value: 'gradient', label: 'Gradient' }, ]; function ColorManipulationContent() { const searchParams = useSearchParams(); const router = useRouter(); const [color, setColor] = useState(() => { const urlColor = searchParams.get('color'); return urlColor ? `#${urlColor.replace('#', '')}` : '#ff0099'; }); const [rightTab, setRightTab] = useState('info'); const [mobileTab, setMobileTab] = useState('pick'); // Harmony const [harmonyType, setHarmonyType] = useState('complementary'); const [palette, setPalette] = useState([]); const paletteMutation = useGeneratePalette(); // Gradient const [stops, setStops] = useState(['#ff0099', '#0099ff']); const [gradientCount, setGradientCount] = useState(10); const [gradientResult, setGradientResult] = useState([]); const gradientMutation = useGenerateGradient(); const { data, isLoading } = useColorInfo({ colors: [color] }); const colorInfo = data?.colors[0]; useEffect(() => { const hex = color.replace('#', ''); if (hex.length === 6 || hex.length === 3) { router.push(`/color?color=${hex}`, { scroll: false }); } }, [color, router]); // Sync first gradient stop with active color useEffect(() => { setStops((prev) => [color, ...prev.slice(1)]); }, [color]); const handleShare = () => { navigator.clipboard.writeText(`${window.location.origin}/color?color=${color.replace('#', '')}`); toast.success('Link copied!'); }; const generateHarmony = async () => { try { const result = await paletteMutation.mutateAsync({ base: color, scheme: harmonyType }); setPalette([result.palette.primary, ...result.palette.secondary]); toast.success(`Generated ${harmonyType} palette`); } catch { toast.error('Failed to generate palette'); } }; const generateGradient = async () => { try { const result = await gradientMutation.mutateAsync({ stops, count: gradientCount }); setGradientResult(result.gradient); toast.success(`Generated ${result.gradient.length} colors`); } catch { toast.error('Failed to generate gradient'); } }; const updateStop = (i: number, v: string) => { const next = [...stops]; next[i] = v; setStops(next); if (i === 0) setColor(v); }; return (
setMobileTab(v as MobileTab)} /> {/* ── Main layout ────────────────────────────────────────── */}
{/* Left panel: Picker + ColorInfo */}
{/* Color picker card */}
Color
{/* Color info card */}
Info
{isLoading ? (
) : colorInfo ? ( ) : null}
{/* Right panel: tabbed tools */}
{/* Tab switcher */}
{RIGHT_TABS.map(({ value, label }) => ( ))}
{/* Tab content */}
{/* ── Info tab ─────────────────────────────── */} {rightTab === 'info' && (
{/* Large color preview */}
{isLoading ? (
) : colorInfo ? ( ) : null}
)} {/* ── Adjust tab ───────────────────────────── */} {rightTab === 'adjust' && ( )} {/* ── Harmony tab ──────────────────────────── */} {rightTab === 'harmony' && (
{/* Scheme selector */}
Scheme
{HARMONY_OPTS.map((opt) => ( ))}

{HARMONY_OPTS.find((o) => o.value === harmonyType)?.desc}

{palette.length > 0 && (
)}
)} {/* ── Gradient tab ─────────────────────────── */} {rightTab === 'gradient' && (
{/* Color stops */}
Stops {stops.map((stop, i) => (
updateStop(i, e.target.value)} className="w-8 h-8 rounded-lg cursor-pointer border border-border/40 bg-transparent shrink-0 p-0.5" /> updateStop(i, e.target.value)} className="flex-1 bg-transparent border border-border/40 rounded-lg px-2.5 py-1.5 text-xs font-mono outline-none focus:border-primary/50 transition-colors" /> {i !== 0 && stops.length > 2 && ( )}
))}
{/* Steps */}
Steps setGradientCount(parseInt(e.target.value))} className="w-20 bg-transparent border border-border/40 rounded-lg px-2.5 py-1.5 text-xs font-mono text-center outline-none focus:border-primary/50 transition-colors" />
{gradientResult.length > 0 && (
{/* Gradient preview bar */}
)}
)}
); } export function ColorManipulation() { return (
}> ); }