'use client'; import * as React from 'react'; import { TextInput } from './TextInput'; import { FontPreview } from './FontPreview'; import { FontSelector } from './FontSelector'; import { TextTemplates } from './TextTemplates'; import { HistoryPanel } from './HistoryPanel'; import { ComparisonMode } from './ComparisonMode'; import { Button } from '@/components/ui/Button'; import { Card } from '@/components/ui/Card'; import { GitCompare } from 'lucide-react'; import { textToAscii } from '@/lib/figlet/figletService'; import { getFontList } from '@/lib/figlet/fontLoader'; import { debounce } from '@/lib/utils/debounce'; import { addRecentFont } from '@/lib/storage/favorites'; import { addToHistory, type HistoryItem } from '@/lib/storage/history'; import { decodeFromUrl, updateUrl, getShareableUrl } from '@/lib/utils/urlSharing'; import { useToast } from '@/components/ui/Toast'; import { cn } from '@/lib/utils/cn'; import type { FigletFont } from '@/types/figlet'; export function FigletConverter() { const [text, setText] = React.useState('Figlet UI'); const [selectedFont, setSelectedFont] = React.useState('Standard'); const [asciiArt, setAsciiArt] = React.useState(''); const [fonts, setFonts] = React.useState([]); const [isLoading, setIsLoading] = React.useState(false); const [isComparisonMode, setIsComparisonMode] = React.useState(false); const [comparisonFonts, setComparisonFonts] = React.useState([]); const [comparisonResults, setComparisonResults] = React.useState>({}); const { addToast } = useToast(); // Load fonts and check URL params on mount React.useEffect(() => { getFontList().then(setFonts); // Check for URL parameters const urlState = decodeFromUrl(); if (urlState) { if (urlState.text) setText(urlState.text); if (urlState.font) setSelectedFont(urlState.font); } }, []); // Generate ASCII art const generateAsciiArt = React.useCallback( debounce(async (inputText: string, fontName: string) => { if (!inputText) { setAsciiArt(''); setIsLoading(false); return; } setIsLoading(true); try { const result = await textToAscii(inputText, fontName); setAsciiArt(result); } catch (error) { console.error('Error generating ASCII art:', error); setAsciiArt('Error generating ASCII art. Please try a different font.'); } finally { setIsLoading(false); } }, 300), [] ); // Trigger generation when text or font changes React.useEffect(() => { generateAsciiArt(text, selectedFont); // Track recent fonts if (selectedFont) { addRecentFont(selectedFont); } // Update URL updateUrl(text, selectedFont); }, [text, selectedFont, generateAsciiArt]); // Copy to clipboard const handleCopy = async () => { if (!asciiArt) return; try { await navigator.clipboard.writeText(asciiArt); addToHistory(text, selectedFont, asciiArt); addToast('Copied to clipboard!', 'success'); } catch (error) { console.error('Failed to copy:', error); addToast('Failed to copy', 'error'); } }; // Download as text file const handleDownload = () => { if (!asciiArt) return; const blob = new Blob([asciiArt], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `figlet-${selectedFont}-${Date.now()}.txt`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); }; // Share (copy URL to clipboard) const handleShare = async () => { const shareUrl = getShareableUrl(text, selectedFont); try { await navigator.clipboard.writeText(shareUrl); addToast('Shareable URL copied!', 'success'); } catch (error) { console.error('Failed to copy URL:', error); addToast('Failed to copy URL', 'error'); } }; // Random font const handleRandomFont = () => { if (fonts.length === 0) return; const randomIndex = Math.floor(Math.random() * fonts.length); setSelectedFont(fonts[randomIndex].name); addToast(`Random font: ${fonts[randomIndex].name}`, 'info'); }; const handleSelectTemplate = (templateText: string) => { setText(templateText); addToast(`Template applied: ${templateText}`, 'info'); }; const handleSelectHistory = (item: HistoryItem) => { setText(item.text); setSelectedFont(item.font); addToast(`Restored from history`, 'info'); }; // Comparison mode handlers const handleToggleComparisonMode = () => { const newMode = !isComparisonMode; setIsComparisonMode(newMode); if (newMode && comparisonFonts.length === 0) { // Initialize with current font setComparisonFonts([selectedFont]); } addToast(newMode ? 'Comparison mode enabled' : 'Comparison mode disabled', 'info'); }; const handleAddToComparison = (fontName: string) => { if (comparisonFonts.includes(fontName)) { addToast('Font already in comparison', 'info'); return; } if (comparisonFonts.length >= 6) { addToast('Maximum 6 fonts for comparison', 'info'); return; } setComparisonFonts([...comparisonFonts, fontName]); addToast(`Added ${fontName} to comparison`, 'success'); }; const handleRemoveFromComparison = (fontName: string) => { setComparisonFonts(comparisonFonts.filter((f) => f !== fontName)); addToast(`Removed ${fontName} from comparison`, 'info'); }; const handleCopyComparisonFont = async (fontName: string, result: string) => { try { await navigator.clipboard.writeText(result); addToHistory(text, fontName, result); addToast(`Copied ${fontName} to clipboard!`, 'success'); } catch (error) { console.error('Failed to copy:', error); addToast('Failed to copy', 'error'); } }; const handleDownloadComparisonFont = (fontName: string, result: string) => { const blob = new Blob([result], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `figlet-${fontName}-${Date.now()}.txt`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); }; // Generate comparison results React.useEffect(() => { if (!isComparisonMode || comparisonFonts.length === 0 || !text) return; const generateComparisons = async () => { const results: Record = {}; for (const fontName of comparisonFonts) { try { results[fontName] = await textToAscii(text, fontName); } catch (error) { console.error(`Error generating ASCII art for ${fontName}:`, error); results[fontName] = 'Error generating ASCII art'; } } setComparisonResults(results); }; generateComparisons(); }, [isComparisonMode, comparisonFonts, text]); return (
{/* Left Column - Input and Preview */}
{/* Comparison Mode Toggle */}
Comparison Mode {isComparisonMode && comparisonFonts.length > 0 && ( {comparisonFonts.length} {comparisonFonts.length === 1 ? 'font' : 'fonts'} )}
{isComparisonMode ? ( ) : ( )}
{/* Right Column - Font Selector */}
); }