'use client'; import * as React from 'react'; import { toPng } from 'html-to-image'; import { Copy, Download, Share2, Image as ImageIcon, AlignLeft, AlignCenter, AlignRight, MessageSquareCode, Type, } from 'lucide-react'; import { cn } from '@/lib/utils/cn'; import { toast } from 'sonner'; export type CommentStyle = 'none' | '//' | '#' | '--' | ';' | '/* */' | '' | '"""'; const COMMENT_STYLES: { value: CommentStyle; label: string }[] = [ { value: 'none', label: 'None' }, { value: '//', label: '// C / JS / Go' }, { value: '#', label: '# Python / Shell' }, { value: '--', label: '-- SQL / Lua' }, { value: ';', label: '; Lisp / ASM' }, { value: '/* */', label: '/* Block */' }, { value: '', label: '' }, { value: '"""', label: '""" Docstring' }, ]; function applyCommentStyle(text: string, style: CommentStyle): string { if (style === 'none' || !text) return text; const lines = text.split('\n'); switch (style) { case '//': case '#': case '--': case ';': return lines.map((l) => `${style} ${l}`).join('\n'); case '/* */': return ['/*', ...lines.map((l) => ` * ${l}`), ' */'].join('\n'); case '': return [''].join('\n'); case '"""': return ['"""', ...lines, '"""'].join('\n'); } } export interface FontPreviewProps { text: string; font?: string; isLoading?: boolean; onCopy?: () => void; onDownload?: () => void; onShare?: () => void; onCommentedTextChange?: (commentedText: string) => void; className?: string; } type TextAlign = 'left' | 'center' | 'right'; type FontSize = 'xs' | 'sm' | 'base'; const ALIGN_OPTS: { value: TextAlign; icon: React.ElementType; label: string }[] = [ { value: 'left', icon: AlignLeft, label: 'Left' }, { value: 'center', icon: AlignCenter, label: 'Center' }, { value: 'right', icon: AlignRight, label: 'Right' }, ]; const SIZE_OPTS: { value: FontSize; label: string }[] = [ { value: 'xs', label: 'xs' }, { value: 'sm', label: 'sm' }, { value: 'base', label: 'md' }, ]; export function FontPreview({ text, font, isLoading, onCopy, onDownload, onShare, onCommentedTextChange, className, }: FontPreviewProps) { const terminalRef = React.useRef(null); const [textAlign, setTextAlign] = React.useState('left'); const [fontSize, setFontSize] = React.useState('sm'); const [commentStyle, setCommentStyle] = React.useState('none'); const commentedText = React.useMemo( () => applyCommentStyle(text, commentStyle), [text, commentStyle] ); const lineCount = commentedText ? commentedText.split('\n').length : 0; const charCount = commentedText ? commentedText.length : 0; React.useEffect(() => { onCommentedTextChange?.(commentedText); }, [commentedText, onCommentedTextChange]); const handleExportPNG = async () => { if (!terminalRef.current || !text) return; try { const dataUrl = await toPng(terminalRef.current, { backgroundColor: '#06060e', pixelRatio: 2, }); const link = document.createElement('a'); link.download = `ascii-${font || 'export'}-${Date.now()}.png`; link.href = dataUrl; link.click(); toast.success('Exported as PNG!'); } catch { toast.error('Failed to export PNG'); } }; const actionBtn = 'flex items-center gap-1 px-2.5 py-1 text-xs glass rounded-md border border-border/30 text-muted-foreground hover:text-primary hover:border-primary/30 hover:bg-primary/10 transition-all'; return (
{/* ── Header: label + font tag + export actions ─────────── */}
Preview {font && ( {font} )}
{onCopy && ( )} {onShare && ( )} {onDownload && ( )}
{/* ── Controls: alignment · size · comment style ─────────── */}
{/* Alignment */}
{ALIGN_OPTS.map(({ value, icon: Icon, label }) => ( ))}
{/* Font size */}
{SIZE_OPTS.map(({ value, label }) => ( ))}
{/* Comment style */}
{/* Stats */} {!isLoading && text && ( {lineCount}L · {charCount}C )}
{/* ── Terminal window ────────────────────────────────────── */}
{/* Terminal chrome */}
{font && ( {font} )}
{/* Content */}
{isLoading ? (
{[0.7, 1, 0.85, 0.55, 1, 0.9, 0.75].map((w, i) => (
))}
) : text ? (
              {commentedText}
            
) : (

Start typing to see your ASCII art

)}
); }