import React, { useEffect, useRef, useState } from "react"; import { motion, AnimatePresence } from "framer-motion"; import { X, Volume2, VolumeX, Github } from "lucide-react"; import { Card } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { openUrl } from "@tauri-apps/plugin-opener"; import asteriskLogo from "@/assets/nfo/asterisk-logo.png"; import keygennMusic from "@/assets/nfo/claudia-nfo.ogg"; import { useTranslation } from "@/hooks/useTranslation"; interface NFOCreditsProps { /** * Callback when the NFO window is closed */ onClose: () => void; } /** * NFO Credits component - Displays a keygen/crack style credits window * with auto-scrolling text, retro fonts, and background music * * @example * setShowNFO(false)} /> */ export const NFOCredits: React.FC = ({ onClose }) => { const { t } = useTranslation(); const audioRef = useRef(null); const scrollRef = useRef(null); const [isMuted, setIsMuted] = useState(false); const [scrollPosition, setScrollPosition] = useState(0); // Initialize and autoplay audio muted then unmute useEffect(() => { const audio = new Audio(keygennMusic); audio.loop = true; audio.volume = 0.7; // Start muted to satisfy autoplay policy audio.muted = true; audioRef.current = audio; // Attempt to play audio.play().then(() => { // Unmute after autoplay audio.muted = false; }).catch(err => { console.error("Audio autoplay failed:", err); }); return () => { if (audioRef.current) { audioRef.current.pause(); audioRef.current.src = ''; audioRef.current = null; } }; }, []); // Handle mute toggle const toggleMute = () => { if (audioRef.current) { audioRef.current.muted = !isMuted; setIsMuted(!isMuted); } }; // Start auto-scrolling useEffect(() => { const scrollInterval = setInterval(() => { setScrollPosition(prev => prev + 1); }, 30); // Smooth scrolling speed return () => clearInterval(scrollInterval); }, []); // Apply scroll position useEffect(() => { if (scrollRef.current) { const maxScroll = scrollRef.current.scrollHeight - scrollRef.current.clientHeight; if (scrollPosition >= maxScroll) { // Reset to beginning when reaching the end setScrollPosition(0); scrollRef.current.scrollTop = 0; } else { scrollRef.current.scrollTop = scrollPosition; } } }, [scrollPosition]); // Credits content const creditsContent = [ { type: "header", text: "CLAUDIA v0.1.0" }, { type: "subheader", text: "[ A STRATEGIC PROJECT BY ASTERISK ]" }, { type: "spacer" }, { type: "section", title: "━━━ CREDITS ━━━" }, { type: "credit", role: "POWERED BY", name: "Anthropic Claude 4" }, { type: "credit", role: "CLAUDE CODE", name: "The Ultimate Coding Assistant" }, { type: "credit", role: "MCP PROTOCOL", name: "Model Context Protocol" }, { type: "spacer" }, { type: "section", title: "━━━ DEPENDENCIES ━━━" }, { type: "credit", role: "RUNTIME", name: "Tauri Framework" }, { type: "credit", role: "UI FRAMEWORK", name: "React + TypeScript" }, { type: "credit", role: "STYLING", name: "Tailwind CSS + shadcn/ui" }, { type: "credit", role: "ANIMATIONS", name: "Framer Motion" }, { type: "credit", role: "BUILD TOOL", name: "Vite" }, { type: "credit", role: "PACKAGE MANAGER", name: "Bun" }, { type: "spacer" }, { type: "section", title: "━━━ SPECIAL THANKS ━━━" }, { type: "text", content: "To the open source community" }, { type: "text", content: "To all the beta testers" }, { type: "text", content: "To everyone who believed in this project" }, { type: "spacer" }, { type: "ascii", content: ` ▄▄▄· .▄▄ · ▄▄▄▄▄▄▄▄ .▄▄▄ ▪ .▄▄ · ▄ •▄ ▐█ ▀█ ▐█ ▀. •██ ▀▄.▀·▀▄ █·██ ▐█ ▀. █▌▄▌▪ ▄█▀▀█ ▄▀▀▀█▄ ▐█.▪▐▀▀▪▄▐▀▀▄ ▐█·▄▀▀▀█▄▐▀▀▄· ▐█ ▪▐▌▐█▄▪▐█ ▐█▌·▐█▄▄▌▐█•█▌▐█▌▐█▄▪▐█▐█.█▌ ▀ ▀ ▀▀▀▀ ▀▀▀ ▀▀▀ .▀ ▀▀▀▀ ▀▀▀▀ ·▀ ▀ ` }, { type: "spacer" }, { type: "text", content: "Remember: Sharing is caring!" }, { type: "text", content: "Support the developers!" }, { type: "spacer" }, { type: "spacer" }, { type: "spacer" }, ]; return ( {/* Backdrop with blur */}
{/* NFO Window */} {/* Window Header */}
CLAUDIA.NFO
{/* NFO Content */}
{/* Asterisk Logo Section (Fixed at top) */}
A strategic project by Asterisk
{/* Scrolling Credits */}
{creditsContent.map((item, index) => { switch (item.type) { case "header": return (
{item.text}
); case "subheader": return (
{item.text}
); case "section": return (
{item.title}
); case "credit": return (
{item.role}: {item.name}
); case "text": return (
{item.content}
); case "ascii": return (
                            {item.content}
                          
); case "spacer": return
; default: return null; } })}
{/* Subtle Scanlines Effect */}
); };