- {/* Comparison Mode Toggle */}
-
-
-
-
- Comparison Mode
- {isComparisonMode && comparisonFonts.length > 0 && (
-
- {comparisonFonts.length} {comparisonFonts.length === 1 ? 'font' : 'fonts'}
-
- )}
-
-
-
-
-
-
-
-
-
+
- {isComparisonMode ? (
-
- ) : (
-
- )}
+
{/* Right Column - Font Selector */}
-
);
diff --git a/components/figlet/FontPreview.tsx b/components/figlet/FontPreview.tsx
index a4b6ad0..b0338ee 100644
--- a/components/figlet/FontPreview.tsx
+++ b/components/figlet/FontPreview.tsx
@@ -8,7 +8,7 @@ import { Skeleton } from '@/components/ui/Skeleton';
import { EmptyState } from '@/components/ui/EmptyState';
import { Copy, Download, Share2, Image as ImageIcon, AlignLeft, AlignCenter, AlignRight, Type } from 'lucide-react';
import { cn } from '@/lib/utils/cn';
-import { useToast } from '@/components/ui/Toast';
+import { toast } from 'sonner';
export interface FontPreviewProps {
text: string;
@@ -28,7 +28,6 @@ export function FontPreview({ text, font, isLoading, onCopy, onDownload, onShare
const previewRef = React.useRef
(null);
const [textAlign, setTextAlign] = React.useState('left');
const [fontSize, setFontSize] = React.useState<'xs' | 'sm' | 'base'>('sm');
- const { addToast } = useToast();
const handleExportPNG = async () => {
if (!previewRef.current || !text) return;
@@ -44,10 +43,10 @@ export function FontPreview({ text, font, isLoading, onCopy, onDownload, onShare
link.href = dataUrl;
link.click();
- addToast('Exported as PNG!', 'success');
+ toast.success('Exported as PNG!');
} catch (error) {
console.error('Failed to export PNG:', error);
- addToast('Failed to export PNG', 'error');
+ toast.error('Failed to export PNG');
}
};
return (
diff --git a/components/figlet/FontSelector.tsx b/components/figlet/FontSelector.tsx
index c9b04c6..604de0f 100644
--- a/components/figlet/FontSelector.tsx
+++ b/components/figlet/FontSelector.tsx
@@ -5,7 +5,7 @@ import Fuse from 'fuse.js';
import { Input } from '@/components/ui/Input';
import { Card } from '@/components/ui/Card';
import { EmptyState } from '@/components/ui/EmptyState';
-import { Search, X, Heart, Clock, List, Shuffle, Plus, Check } 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';
@@ -16,9 +16,6 @@ export interface FontSelectorProps {
selectedFont: string;
onSelectFont: (fontName: string) => void;
onRandomFont?: () => void;
- isComparisonMode?: boolean;
- comparisonFonts?: string[];
- onAddToComparison?: (fontName: string) => void;
className?: string;
}
@@ -29,9 +26,6 @@ export function FontSelector({
selectedFont,
onSelectFont,
onRandomFont,
- isComparisonMode = false,
- comparisonFonts = [],
- onAddToComparison,
className
}: FontSelectorProps) {
const [searchQuery, setSearchQuery] = React.useState('');
@@ -112,9 +106,9 @@ export function FontSelector({
};
return (
-
-
-
+
+
+
Select Font
{onRandomFont && (
)}
{/* Filter Tabs */}
-
+
{/* Search Input */}
-
+
{/* Font List */}
-
+
{filteredFonts.length === 0 ? (
) : (
- filteredFonts.map((font) => {
- const isInComparison = comparisonFonts.includes(font.name);
- return (
-
(
+
+
- );
- })
+ />
+
+
+ ))
)}
{/* Stats */}
-
+
{filteredFonts.length} font{filteredFonts.length !== 1 ? 's' : ''}
{filter === 'favorites' && ` • ${favorites.length} total favorites`}
{filter === 'recent' && ` • ${recentFonts.length} recent`}
diff --git a/components/figlet/HistoryPanel.tsx b/components/figlet/HistoryPanel.tsx
deleted file mode 100644
index b9ad1da..0000000
--- a/components/figlet/HistoryPanel.tsx
+++ /dev/null
@@ -1,133 +0,0 @@
-'use client';
-
-import * as React from 'react';
-import { Card } from '@/components/ui/Card';
-import { Button } from '@/components/ui/Button';
-import { EmptyState } from '@/components/ui/EmptyState';
-import { History, X, Trash2, ChevronDown, ChevronUp, Clock } from 'lucide-react';
-import { cn } from '@/lib/utils/cn';
-import { getHistory, clearHistory, removeHistoryItem, type HistoryItem } from '@/lib/storage/history';
-
-export interface HistoryPanelProps {
- onSelectHistory: (item: HistoryItem) => void;
- className?: string;
-}
-
-export function HistoryPanel({ onSelectHistory, className }: HistoryPanelProps) {
- const [isExpanded, setIsExpanded] = React.useState(false);
- const [history, setHistory] = React.useState
([]);
-
- const loadHistory = React.useCallback(() => {
- setHistory(getHistory());
- }, []);
-
- React.useEffect(() => {
- loadHistory();
- // Refresh history every 2 seconds when expanded
- if (isExpanded) {
- const interval = setInterval(loadHistory, 2000);
- return () => clearInterval(interval);
- }
- }, [isExpanded, loadHistory]);
-
- const handleClearAll = () => {
- clearHistory();
- loadHistory();
- };
-
- const handleRemove = (id: string, e: React.MouseEvent) => {
- e.stopPropagation();
- removeHistoryItem(id);
- loadHistory();
- };
-
- const formatTime = (timestamp: number) => {
- const now = Date.now();
- const diff = now - timestamp;
- const minutes = Math.floor(diff / 60000);
- const hours = Math.floor(diff / 3600000);
-
- if (minutes < 1) return 'Just now';
- if (minutes < 60) return `${minutes}m ago`;
- if (hours < 24) return `${hours}h ago`;
- return new Date(timestamp).toLocaleDateString();
- };
-
- return (
-
-
-
setIsExpanded(!isExpanded)}
- className="w-full flex items-center justify-between text-sm font-medium hover:text-primary transition-colors"
- >
-
-
- Copy History
- ({history.length})
-
- {isExpanded ? (
-
- ) : (
-
- )}
-
-
- {isExpanded && (
-
- {history.length === 0 ? (
-
- ) : (
- <>
-
-
-
- Clear All
-
-
-
-
- {history.map((item) => (
-
onSelectHistory(item)}
- className="group relative p-3 bg-muted/50 hover:bg-accent hover:scale-[1.02] rounded-md cursor-pointer transition-all"
- >
-
-
-
-
- {item.font}
-
-
- {formatTime(item.timestamp)}
-
-
-
{item.text}
-
-
handleRemove(item.id, e)}
- className="opacity-0 group-hover:opacity-100 transition-opacity p-1 hover:bg-destructive/10 rounded"
- >
-
-
-
-
- ))}
-
- >
- )}
-
- )}
-
-
- );
-}
diff --git a/components/figlet/TextInput.tsx b/components/figlet/TextInput.tsx
index a8b6a26..ab38dcb 100644
--- a/components/figlet/TextInput.tsx
+++ b/components/figlet/TextInput.tsx
@@ -17,7 +17,7 @@ export function TextInput({ value, onChange, placeholder, className }: TextInput
value={value}
onChange={(e) => onChange(e.target.value)}
placeholder={placeholder || 'Type something...'}
- className="w-full h-32 px-4 py-3 text-base border border-input rounded-lg bg-background resize-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 placeholder:text-muted-foreground"
+ className="w-full h-32 px-4 py-3 text-base border border-border rounded-lg bg-input resize-none focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring placeholder:text-muted-foreground transition-all duration-200"
maxLength={100}
/>
diff --git a/components/figlet/TextTemplates.tsx b/components/figlet/TextTemplates.tsx
deleted file mode 100644
index 4696527..0000000
--- a/components/figlet/TextTemplates.tsx
+++ /dev/null
@@ -1,92 +0,0 @@
-'use client';
-
-import * as React from 'react';
-import { Card } from '@/components/ui/Card';
-import { Button } from '@/components/ui/Button';
-import { Sparkles, ChevronDown, ChevronUp } from 'lucide-react';
-import { cn } from '@/lib/utils/cn';
-import { TEXT_TEMPLATES, TEMPLATE_CATEGORIES } from '@/lib/figlet/constants/templates';
-
-export interface TextTemplatesProps {
- onSelectTemplate: (text: string) => void;
- className?: string;
-}
-
-export function TextTemplates({ onSelectTemplate, className }: TextTemplatesProps) {
- const [isExpanded, setIsExpanded] = React.useState(false);
- const [selectedCategory, setSelectedCategory] = React.useState
('all');
-
- const filteredTemplates = React.useMemo(() => {
- if (selectedCategory === 'all') return TEXT_TEMPLATES;
- return TEXT_TEMPLATES.filter(t => t.category === selectedCategory);
- }, [selectedCategory]);
-
- return (
-
-
-
setIsExpanded(!isExpanded)}
- className="w-full flex items-center justify-between text-sm font-medium hover:text-primary transition-colors"
- >
-
-
- Text Templates
- ({TEXT_TEMPLATES.length})
-
- {isExpanded ? (
-
- ) : (
-
- )}
-
-
- {isExpanded && (
-
- {/* Category Filter */}
-
- setSelectedCategory('all')}
- className={cn(
- 'px-2 py-1 text-xs rounded-md transition-colors',
- selectedCategory === 'all'
- ? 'bg-primary text-primary-foreground'
- : 'bg-muted hover:bg-muted/80'
- )}
- >
- All
-
- {TEMPLATE_CATEGORIES.map((cat) => (
- setSelectedCategory(cat.id)}
- className={cn(
- 'px-2 py-1 text-xs rounded-md transition-colors',
- selectedCategory === cat.id
- ? 'bg-primary text-primary-foreground'
- : 'bg-muted hover:bg-muted/80'
- )}
- >
- {cat.icon} {cat.label}
-
- ))}
-
-
- {/* Templates Grid */}
-
- {filteredTemplates.map((template) => (
- onSelectTemplate(template.text)}
- className="px-3 py-2 text-xs bg-muted hover:bg-accent hover:scale-105 rounded-md transition-all text-left truncate"
- title={template.text}
- >
- {template.label}
-
- ))}
-
-
- )}
-
-
- );
-}
diff --git a/components/layout/AppHeader.tsx b/components/layout/AppHeader.tsx
index c426909..b0c324a 100644
--- a/components/layout/AppHeader.tsx
+++ b/components/layout/AppHeader.tsx
@@ -17,7 +17,7 @@ export function AppHeader() {
const pathSegments = pathname.split('/').filter(Boolean);
return (
-
+
setTheme(resolvedTheme === 'dark' ? 'light' : 'dark')}
- className="text-muted-foreground hover:text-foreground hover:bg-white/5"
+ className="text-muted-foreground hover:text-foreground hover:bg-accent/50"
title={`Switch to ${resolvedTheme === 'dark' ? 'light' : 'dark'} mode`}
>
{resolvedTheme === 'dark' ? (
diff --git a/components/layout/AppSidebar.tsx b/components/layout/AppSidebar.tsx
index 8a87dd9..8a4fa61 100644
--- a/components/layout/AppSidebar.tsx
+++ b/components/layout/AppSidebar.tsx
@@ -103,12 +103,12 @@ export function AppSidebar() {
)}