Implemented Phases 5-7 of the implementation plan with major UX enhancements:
**Dark Mode (Phase 9)**
- Added ThemeToggle component with localStorage persistence
- System preference detection
- Smooth theme transitions
- Moon/Sun icon toggle in header
**Fuzzy Search with Fuse.js (Phase 5)**
- Integrated Fuse.js for intelligent font search
- 30% threshold for flexible matching
- Search by font name and filename
- Clear button for search input
- Much better than simple string matching
**Favorites & Recent Fonts System (Phase 7)**
- localStorage-based favorites with heart icon toggle
- Auto-tracking of recently used fonts (max 10)
- Filter tabs: All / Favorites / Recent
- Favorite hearts visible on hover
- Red filled heart for favorited fonts
- Stats showing favorite and recent counts
**Shareable URLs (Phase 6)**
- Encode text + font in URL parameters
- Auto-load from URL on page load
- Share button copies URL to clipboard
- Clean URL updates without page reload
- Perfect for sharing ASCII art creations
**Enhanced Font Selector**
- 3-tab filter system (All/Favorites/Recent)
- Visual feedback for selected fonts
- Empty states for each filter
- Font count statistics
- Heart icon for quick favoriting
- Recent fonts sorted by usage order
**UX Improvements**
- Copy feedback ("Copied to clipboard! ✓")
- Share feedback ("URL copied to clipboard! ✓")
- Responsive button layout
- Better empty states
- Improved accessibility with aria-labels
**Tech Highlights**
- Client-side localStorage management
- URL encoding/decoding utilities
- React hooks for state management
- Fuse.js fuzzy search integration
- Theme persistence across sessions
The app now has professional-grade features rivaling any modern web app!
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
65 lines
2.1 KiB
TypeScript
65 lines
2.1 KiB
TypeScript
'use client';
|
|
|
|
import * as React from 'react';
|
|
import { Card } from '@/components/ui/Card';
|
|
import { Button } from '@/components/ui/Button';
|
|
import { Copy, Download, Share2 } from 'lucide-react';
|
|
import { cn } from '@/lib/utils/cn';
|
|
|
|
export interface FontPreviewProps {
|
|
text: string;
|
|
isLoading?: boolean;
|
|
onCopy?: () => void;
|
|
onDownload?: () => void;
|
|
onShare?: () => void;
|
|
className?: string;
|
|
}
|
|
|
|
export function FontPreview({ text, isLoading, onCopy, onDownload, onShare, className }: FontPreviewProps) {
|
|
return (
|
|
<Card className={cn('relative', className)}>
|
|
<div className="p-6">
|
|
<div className="flex items-center justify-between mb-4">
|
|
<h3 className="text-sm font-medium">Preview</h3>
|
|
<div className="flex gap-2">
|
|
{onCopy && (
|
|
<Button variant="outline" size="sm" onClick={onCopy}>
|
|
<Copy className="h-4 w-4" />
|
|
Copy
|
|
</Button>
|
|
)}
|
|
{onShare && (
|
|
<Button variant="outline" size="sm" onClick={onShare} title="Copy shareable URL">
|
|
<Share2 className="h-4 w-4" />
|
|
Share
|
|
</Button>
|
|
)}
|
|
{onDownload && (
|
|
<Button variant="outline" size="sm" onClick={onDownload}>
|
|
<Download className="h-4 w-4" />
|
|
Download
|
|
</Button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="relative min-h-[200px] bg-muted/50 rounded-lg p-4 overflow-x-auto">
|
|
{isLoading ? (
|
|
<div className="absolute inset-0 flex items-center justify-center">
|
|
<div className="text-sm text-muted-foreground">Generating...</div>
|
|
</div>
|
|
) : text ? (
|
|
<pre className="font-mono text-xs sm:text-sm whitespace-pre overflow-x-auto">
|
|
{text}
|
|
</pre>
|
|
) : (
|
|
<div className="absolute inset-0 flex items-center justify-center">
|
|
<div className="text-sm text-muted-foreground">Your ASCII art will appear here</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
);
|
|
}
|