feat: add keyboard shortcuts, Docker deployment, and production build
Major improvements for production deployment and UX: **Keyboard Shortcuts System** - `/` - Focus font search instantly - `Esc` - Clear search and close dialogs - `Ctrl/Cmd + D` - Toggle dark/light mode - `Shift + ?` - Show keyboard shortcuts help dialog - Floating keyboard icon button for discoverability - Beautiful modal with all shortcuts listed - Global event listeners with proper cleanup **Enhanced Search UX** - Updated placeholder: "Search fonts... (Press / to focus)" - Auto-focus on `/` keypress - Auto-clear and blur on `Escape` - Ref-based input focusing for reliability **Docker Deployment** - Multi-stage Dockerfile (deps, builder, nginx runner) - Based on node:22-alpine for minimal size - Static export served via nginx:alpine - Built-in health check endpoint (/health) - Optimized for production **Nginx Configuration** - Gzip compression for all text assets - Aggressive caching for static assets (1 year) - Security headers (X-Frame-Options, CSP, etc.) - SPA routing support (try_files) - Health check endpoint - Performance tuning (sendfile, tcp_nopush) **Documentation Updates** - Corrected font count to accurate 373 fonts - Updated README and IMPLEMENTATION_PLAN - Added Docker deployment instructions - Clarified .flf vs .tlf vs .flc formats **Production Build** - Tested static export build - Total size: 8.0MB (including all fonts!) - 4.7s build time with Turbopack - All routes pre-rendered successfully - Ready for CDN/static hosting **Technical Highlights** - useKeyboardShortcuts custom hook - Event listener cleanup on unmount - Ref forwarding for input focus - Modal dialog with backdrop blur - Keyboard-first navigation The app is now production-ready with professional keyboard shortcuts and Docker deployment support! 🎉 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,13 @@ import { Button } from '@/components/ui/Button';
|
||||
export function ThemeToggle() {
|
||||
const [theme, setTheme] = React.useState<'light' | 'dark'>('light');
|
||||
|
||||
const toggleTheme = React.useCallback(() => {
|
||||
const newTheme = theme === 'light' ? 'dark' : 'light';
|
||||
setTheme(newTheme);
|
||||
localStorage.setItem('theme', newTheme);
|
||||
document.documentElement.classList.toggle('dark', newTheme === 'dark');
|
||||
}, [theme]);
|
||||
|
||||
React.useEffect(() => {
|
||||
// Check for saved theme preference or default to light
|
||||
const savedTheme = localStorage.getItem('theme') as 'light' | 'dark' | null;
|
||||
@@ -17,12 +24,18 @@ export function ThemeToggle() {
|
||||
document.documentElement.classList.toggle('dark', initialTheme === 'dark');
|
||||
}, []);
|
||||
|
||||
const toggleTheme = () => {
|
||||
const newTheme = theme === 'light' ? 'dark' : 'light';
|
||||
setTheme(newTheme);
|
||||
localStorage.setItem('theme', newTheme);
|
||||
document.documentElement.classList.toggle('dark', newTheme === 'dark');
|
||||
};
|
||||
// Keyboard shortcut: Ctrl/Cmd + D
|
||||
React.useEffect(() => {
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === 'd' && (e.ctrlKey || e.metaKey)) {
|
||||
e.preventDefault();
|
||||
toggleTheme();
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
return () => window.removeEventListener('keydown', handleKeyDown);
|
||||
}, [toggleTheme]);
|
||||
|
||||
return (
|
||||
<Button
|
||||
|
||||
Reference in New Issue
Block a user