import React, { useState } from 'react'; import { cn } from '@/lib/utils'; type CypherImageProps = { src?: string; alt: string; className?: string; fallbackClassName?: string; generateUniqueFallback?: boolean; } & Omit< React.ImgHTMLAttributes, 'src' | 'alt' | 'className' >; /** * CypherImage component that renders a cypherpunk-style fallback image * when the actual image cannot be loaded. */ export function CypherImage({ src, alt, className, fallbackClassName, generateUniqueFallback = false, ...props }: CypherImageProps) { const [imageError, setImageError] = useState(false); // Generate a seed based on the alt text or src to create consistent fallbacks for the same resource const seed = generateUniqueFallback ? (alt || src || 'fallback') .split('') .reduce((acc, char) => acc + char.charCodeAt(0), 0) : 0; // Handle image load error const handleError = () => { setImageError(true); }; if (imageError || !src || src.trim() === '') { // Generate some values based on the seed for variety const hue = (seed % 60) + 140; // Cyan-ish colors (140-200) const gridSize = (seed % 8) + 8; // 8-16px const noiseIntensity = (seed % 30) + 5; // 5-35% const scanlineOpacity = ((seed % 4) + 1) / 10; // 0.1-0.5 return (
{/* Noise overlay */}
{/* Scanlines */}
{/* CRT glow effect */}
{/* Glitch effect lines */}
{/* Main content container with glitch effect */}
{/* Glitched text behind the main letter */}
{Array.from({ length: 3 }, (_, i) => { const chars = '01[]{}><#$%&@!?;:+=*/\\|~^'; return chars[(seed + i) % chars.length]; }).join('')}
{/* First letter of alt text in center */}
{alt.charAt(0).toUpperCase()}
{/* Random characters that occasionally "glitch" in */}
{seed.toString(16).substring(0, 4)}
); } return ( {alt} ); }