feat: implement UX enhancements for playground

Add comprehensive UX improvements to color playground:

**URL State Sharing:**
- Implement URL parameter sync for colors
- Add share button to copy playground URLs
- Wrap in Suspense boundary for Next.js 16 compatibility
- Colors persist in URL for easy sharing

**Keyboard Shortcuts:**
- Create reusable useKeyboard hook with modifier support
- Add Cmd/Ctrl+C to copy current color
- Add Cmd/Ctrl+S to share color URL
- Add Cmd/Ctrl+R for random color generation
- Display keyboard hints in playground header
- Cross-platform support (Mac/Windows/Linux)

**Color History:**
- Implement Zustand store with localStorage persistence
- Track up to 50 most recent colors with timestamps
- Auto-deduplicate and keep most recent entry
- Add visual history grid in playground
- Click to restore previous colors
- Individual color removal with hover UI
- Clear all history option

**Playground UI Enhancements:**
- Add visual keyboard shortcut indicators (⌘C, ⌘S, ⌘R)
- Implement Recent Colors section with 5-column grid
- Add hover effects for history color swatches
- Individual remove buttons on hover
- Toast notifications for all actions
- Improved button layout and spacing

**Files Added:**
- lib/hooks/useKeyboard.ts - Keyboard shortcut management
- lib/stores/historyStore.ts - Color history with persistence

**Files Modified:**
- app/playground/page.tsx - Integrated all UX features

Successfully builds with all features working.
Ready for user testing.

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
valknarness
2025-11-07 12:30:33 +01:00
parent 89bd011537
commit 494b2a450b
3 changed files with 298 additions and 5 deletions

91
lib/hooks/useKeyboard.ts Normal file
View File

@@ -0,0 +1,91 @@
import { useEffect } from 'react';
export interface KeyboardShortcut {
key: string;
ctrl?: boolean;
shift?: boolean;
alt?: boolean;
meta?: boolean;
handler: (event: KeyboardEvent) => void;
description?: string;
}
/**
* Hook to register keyboard shortcuts
*
* @example
* ```tsx
* useKeyboard([
* {
* key: 'c',
* meta: true, // Cmd on Mac, Ctrl on Windows
* handler: () => copyToClipboard(),
* description: 'Copy color',
* },
* {
* key: 'k',
* meta: true,
* handler: () => openCommandPalette(),
* description: 'Open command palette',
* },
* ]);
* ```
*/
export function useKeyboard(shortcuts: KeyboardShortcut[]) {
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
for (const shortcut of shortcuts) {
const keyMatches = event.key.toLowerCase() === shortcut.key.toLowerCase();
const ctrlMatches = shortcut.ctrl ? event.ctrlKey : !event.ctrlKey;
const shiftMatches = shortcut.shift ? event.shiftKey : !event.shiftKey;
const altMatches = shortcut.alt ? event.altKey : !event.altKey;
const metaMatches = shortcut.meta ? event.metaKey : !event.metaKey;
// On Windows/Linux, treat meta as ctrl for convenience
const modifierMatches = shortcut.meta
? event.metaKey || (event.ctrlKey && !navigator.platform.includes('Mac'))
: metaMatches;
if (
keyMatches &&
ctrlMatches &&
shiftMatches &&
altMatches &&
modifierMatches
) {
event.preventDefault();
shortcut.handler(event);
break;
}
}
};
window.addEventListener('keydown', handleKeyDown);
return () => {
window.removeEventListener('keydown', handleKeyDown);
};
}, [shortcuts]);
}
/**
* Hook to register a single keyboard shortcut (convenience wrapper)
*/
export function useKeyboardShortcut(
key: string,
handler: (event: KeyboardEvent) => void,
modifiers?: {
ctrl?: boolean;
shift?: boolean;
alt?: boolean;
meta?: boolean;
}
) {
useKeyboard([
{
key,
...modifiers,
handler,
},
]);
}