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:
68
lib/stores/historyStore.ts
Normal file
68
lib/stores/historyStore.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { create } from 'zustand';
|
||||
import { persist, createJSONStorage } from 'zustand/middleware';
|
||||
|
||||
export interface ColorHistoryEntry {
|
||||
color: string;
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
interface ColorHistoryState {
|
||||
history: ColorHistoryEntry[];
|
||||
addColor: (color: string) => void;
|
||||
removeColor: (color: string) => void;
|
||||
clearHistory: () => void;
|
||||
getRecent: (limit?: number) => ColorHistoryEntry[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Color history store with localStorage persistence
|
||||
*
|
||||
* Tracks up to 50 most recent colors with timestamps
|
||||
* Automatically removes duplicates (keeps most recent)
|
||||
* Persists across browser sessions
|
||||
*/
|
||||
export const useColorHistory = create<ColorHistoryState>()(
|
||||
persist(
|
||||
(set, get) => ({
|
||||
history: [],
|
||||
|
||||
addColor: (color) => {
|
||||
const normalizedColor = color.toLowerCase();
|
||||
set((state) => {
|
||||
// Remove existing entry if present
|
||||
const filtered = state.history.filter(
|
||||
(entry) => entry.color.toLowerCase() !== normalizedColor
|
||||
);
|
||||
|
||||
// Add new entry at the beginning
|
||||
const newHistory = [
|
||||
{ color: normalizedColor, timestamp: Date.now() },
|
||||
...filtered,
|
||||
].slice(0, 50); // Keep only 50 most recent
|
||||
|
||||
return { history: newHistory };
|
||||
});
|
||||
},
|
||||
|
||||
removeColor: (color) => {
|
||||
const normalizedColor = color.toLowerCase();
|
||||
set((state) => ({
|
||||
history: state.history.filter(
|
||||
(entry) => entry.color.toLowerCase() !== normalizedColor
|
||||
),
|
||||
}));
|
||||
},
|
||||
|
||||
clearHistory: () => set({ history: [] }),
|
||||
|
||||
getRecent: (limit = 10) => {
|
||||
const { history } = get();
|
||||
return history.slice(0, limit);
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: 'pastel-color-history',
|
||||
storage: createJSONStorage(() => localStorage),
|
||||
}
|
||||
)
|
||||
);
|
||||
Reference in New Issue
Block a user