feat: add toast notifications, random font, and font info display

Major UX enhancements for better user feedback and discovery:

**Toast Notification System**
- Beautiful toast notifications with 3 types (success/error/info)
- Auto-dismiss after 3 seconds
- Slide-in animation from right
- Color-coded by type (green/red/blue)
- Dark mode support
- Close button for manual dismiss
- ToastProvider context for global access
- Non-blocking UI overlay

**Random Font Discovery**
- Shuffle button in font selector
- One-click random font selection
- Toast notification shows selected font
- Perfect for discovering new fonts
- Located next to "Select Font" header

**Enhanced Font Preview**
- Font name badge display
- Character count statistics
- Line count statistics
- Better visual hierarchy
- Responsive stat display

**Improved Feedback**
- Toast on copy: "Copied to clipboard!"
- Toast on share: "Shareable URL copied!"
- Toast on random: "Random font: FontName"
- Error toasts for failed operations
- Removed temporary text replacement

**Smooth Animations**
- Slide-in animation for toasts
- Fade-in animation class
- Custom keyframe animations
- CSS utility classes
- Smooth transitions throughout

**Technical Improvements**
- useToast custom hook
- Context-based state management
- Auto-cleanup with setTimeout
- Unique toast IDs
- TypeScript types for toast system
- Proper event propagation

**Better UX**
- No more jarring text replacements
- Non-intrusive notifications
- Professional feedback system
- Discoverable random feature
- Informative preview stats

The app now feels polished and professional with proper user feedback!

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-09 13:21:13 +01:00
parent a83bc5cb7a
commit 704695f14f
6 changed files with 174 additions and 17 deletions

View File

@@ -4,8 +4,9 @@ import * as React from 'react';
import Fuse from 'fuse.js';
import { Input } from '@/components/ui/Input';
import { Card } from '@/components/ui/Card';
import { Search, X, Heart, Clock, List } from 'lucide-react';
import { Search, X, Heart, Clock, List, Shuffle } from 'lucide-react';
import { cn } from '@/lib/utils/cn';
import { Button } from '@/components/ui/Button';
import type { FigletFont } from '@/types/figlet';
import { getFavorites, getRecentFonts, toggleFavorite, isFavorite } from '@/lib/storage/favorites';
@@ -13,12 +14,13 @@ export interface FontSelectorProps {
fonts: FigletFont[];
selectedFont: string;
onSelectFont: (fontName: string) => void;
onRandomFont?: () => void;
className?: string;
}
type FilterType = 'all' | 'favorites' | 'recent';
export function FontSelector({ fonts, selectedFont, onSelectFont, className }: FontSelectorProps) {
export function FontSelector({ fonts, selectedFont, onSelectFont, onRandomFont, className }: FontSelectorProps) {
const [searchQuery, setSearchQuery] = React.useState('');
const [filter, setFilter] = React.useState<FilterType>('all');
const [favorites, setFavorites] = React.useState<string[]>([]);
@@ -99,7 +101,20 @@ export function FontSelector({ fonts, selectedFont, onSelectFont, className }: F
return (
<Card className={className}>
<div className="p-6">
<h3 className="text-sm font-medium mb-4">Select Font</h3>
<div className="flex items-center justify-between mb-4">
<h3 className="text-sm font-medium">Select Font</h3>
{onRandomFont && (
<Button
variant="outline"
size="sm"
onClick={onRandomFont}
title="Random font"
>
<Shuffle className="h-4 w-4" />
Random
</Button>
)}
</div>
{/* Filter Tabs */}
<div className="flex gap-1 mb-4 p-1 bg-muted rounded-lg">