feat: implement color playground with interactive picker and info display
Add complete color manipulation interface:
**Color Components:**
- ColorPicker - Interactive hex color picker with text input
- Uses react-colorful for visual selection
- Manual input support for any color format
- Real-time updates
- ColorDisplay - Large color preview swatch
- Configurable sizes (sm, md, lg, xl)
- Optional border styling
- Accessible with ARIA labels
- ColorInfo - Comprehensive color information display
- Shows all formats: Hex, RGB, HSL, Lab, OkLab
- Copy to clipboard functionality for each format
- Displays brightness, luminance, light/dark type
- Shows named color matches
**API Integration:**
- React Query hooks for all Pastel API endpoints
- useColorInfo - Get color information
- useConvertFormat - Format conversion
- useLighten, useDarken, useSaturate, etc. - Manipulations
- useGenerateRandom, useGenerateDistinct, useGenerateGradient
- useNamedColors - Cached named colors list
- Automatic error handling and loading states
**Playground Page (/playground):**
- Two-column layout: picker + info
- Live color preview with large swatch
- Real-time API integration with loading states
- Error handling with user-friendly messages
- Quick action buttons (ready for implementation)
- Responsive grid layout
**Features:**
- Toast notifications for clipboard copy
- Loading spinners during API calls
- Error display with helpful messages
- Accessible keyboard navigation
- Smooth transitions and animations
Build successful with playground page rendering! Ready for color manipulation actions.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-07 11:05:23 +01:00
|
|
|
'use client';
|
|
|
|
|
|
|
|
|
|
import { useQuery, useMutation, UseQueryOptions } from '@tanstack/react-query';
|
|
|
|
|
import { pastelAPI } from './client';
|
|
|
|
|
import type {
|
|
|
|
|
ColorInfoRequest,
|
|
|
|
|
ColorInfoData,
|
|
|
|
|
ConvertFormatRequest,
|
|
|
|
|
ConvertFormatData,
|
|
|
|
|
ColorManipulationRequest,
|
|
|
|
|
ColorManipulationData,
|
|
|
|
|
ColorMixRequest,
|
|
|
|
|
ColorMixData,
|
|
|
|
|
RandomColorsRequest,
|
|
|
|
|
RandomColorsData,
|
|
|
|
|
DistinctColorsRequest,
|
|
|
|
|
DistinctColorsData,
|
|
|
|
|
GradientRequest,
|
|
|
|
|
GradientData,
|
2025-11-07 13:47:16 +01:00
|
|
|
ColorBlindnessRequest,
|
2025-11-07 14:33:38 +01:00
|
|
|
PaletteGenerateRequest,
|
|
|
|
|
PaletteGenerateData,
|
2025-11-07 13:47:16 +01:00
|
|
|
ColorBlindnessData,
|
|
|
|
|
TextColorRequest,
|
|
|
|
|
TextColorData,
|
feat: implement color playground with interactive picker and info display
Add complete color manipulation interface:
**Color Components:**
- ColorPicker - Interactive hex color picker with text input
- Uses react-colorful for visual selection
- Manual input support for any color format
- Real-time updates
- ColorDisplay - Large color preview swatch
- Configurable sizes (sm, md, lg, xl)
- Optional border styling
- Accessible with ARIA labels
- ColorInfo - Comprehensive color information display
- Shows all formats: Hex, RGB, HSL, Lab, OkLab
- Copy to clipboard functionality for each format
- Displays brightness, luminance, light/dark type
- Shows named color matches
**API Integration:**
- React Query hooks for all Pastel API endpoints
- useColorInfo - Get color information
- useConvertFormat - Format conversion
- useLighten, useDarken, useSaturate, etc. - Manipulations
- useGenerateRandom, useGenerateDistinct, useGenerateGradient
- useNamedColors - Cached named colors list
- Automatic error handling and loading states
**Playground Page (/playground):**
- Two-column layout: picker + info
- Live color preview with large swatch
- Real-time API integration with loading states
- Error handling with user-friendly messages
- Quick action buttons (ready for implementation)
- Responsive grid layout
**Features:**
- Toast notifications for clipboard copy
- Loading spinners during API calls
- Error display with helpful messages
- Accessible keyboard navigation
- Smooth transitions and animations
Build successful with playground page rendering! Ready for color manipulation actions.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-07 11:05:23 +01:00
|
|
|
NamedColorsData,
|
|
|
|
|
HealthData,
|
|
|
|
|
} from './types';
|
|
|
|
|
|
|
|
|
|
// Color Information
|
|
|
|
|
export const useColorInfo = (
|
|
|
|
|
request: ColorInfoRequest,
|
|
|
|
|
options?: Omit<UseQueryOptions<ColorInfoData>, 'queryKey' | 'queryFn'>
|
|
|
|
|
) => {
|
|
|
|
|
return useQuery({
|
|
|
|
|
queryKey: ['colorInfo', request.colors],
|
|
|
|
|
queryFn: async () => {
|
|
|
|
|
const response = await pastelAPI.getColorInfo(request);
|
|
|
|
|
if (!response.success) {
|
|
|
|
|
throw new Error(response.error.message);
|
|
|
|
|
}
|
|
|
|
|
return response.data;
|
|
|
|
|
},
|
|
|
|
|
enabled: request.colors.length > 0 && request.colors.every((c) => c.length > 0),
|
|
|
|
|
...options,
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Format Conversion
|
|
|
|
|
export const useConvertFormat = () => {
|
|
|
|
|
return useMutation({
|
|
|
|
|
mutationFn: async (request: ConvertFormatRequest) => {
|
|
|
|
|
const response = await pastelAPI.convertFormat(request);
|
|
|
|
|
if (!response.success) {
|
|
|
|
|
throw new Error(response.error.message);
|
|
|
|
|
}
|
|
|
|
|
return response.data;
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Color Manipulation
|
|
|
|
|
export const useLighten = () => {
|
|
|
|
|
return useMutation({
|
|
|
|
|
mutationFn: async (request: ColorManipulationRequest) => {
|
|
|
|
|
const response = await pastelAPI.lighten(request);
|
|
|
|
|
if (!response.success) {
|
|
|
|
|
throw new Error(response.error.message);
|
|
|
|
|
}
|
|
|
|
|
return response.data;
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const useDarken = () => {
|
|
|
|
|
return useMutation({
|
|
|
|
|
mutationFn: async (request: ColorManipulationRequest) => {
|
|
|
|
|
const response = await pastelAPI.darken(request);
|
|
|
|
|
if (!response.success) {
|
|
|
|
|
throw new Error(response.error.message);
|
|
|
|
|
}
|
|
|
|
|
return response.data;
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const useSaturate = () => {
|
|
|
|
|
return useMutation({
|
|
|
|
|
mutationFn: async (request: ColorManipulationRequest) => {
|
|
|
|
|
const response = await pastelAPI.saturate(request);
|
|
|
|
|
if (!response.success) {
|
|
|
|
|
throw new Error(response.error.message);
|
|
|
|
|
}
|
|
|
|
|
return response.data;
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const useDesaturate = () => {
|
|
|
|
|
return useMutation({
|
|
|
|
|
mutationFn: async (request: ColorManipulationRequest) => {
|
|
|
|
|
const response = await pastelAPI.desaturate(request);
|
|
|
|
|
if (!response.success) {
|
|
|
|
|
throw new Error(response.error.message);
|
|
|
|
|
}
|
|
|
|
|
return response.data;
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const useRotate = () => {
|
|
|
|
|
return useMutation({
|
|
|
|
|
mutationFn: async (request: ColorManipulationRequest) => {
|
|
|
|
|
const response = await pastelAPI.rotate(request);
|
|
|
|
|
if (!response.success) {
|
|
|
|
|
throw new Error(response.error.message);
|
|
|
|
|
}
|
|
|
|
|
return response.data;
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const useComplement = () => {
|
|
|
|
|
return useMutation({
|
|
|
|
|
mutationFn: async (colors: string[]) => {
|
|
|
|
|
const response = await pastelAPI.complement(colors);
|
|
|
|
|
if (!response.success) {
|
|
|
|
|
throw new Error(response.error.message);
|
|
|
|
|
}
|
|
|
|
|
return response.data;
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const useMixColors = () => {
|
|
|
|
|
return useMutation({
|
|
|
|
|
mutationFn: async (request: ColorMixRequest) => {
|
|
|
|
|
const response = await pastelAPI.mix(request);
|
|
|
|
|
if (!response.success) {
|
|
|
|
|
throw new Error(response.error.message);
|
|
|
|
|
}
|
|
|
|
|
return response.data;
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Color Generation
|
|
|
|
|
export const useGenerateRandom = () => {
|
|
|
|
|
return useMutation({
|
|
|
|
|
mutationFn: async (request: RandomColorsRequest) => {
|
|
|
|
|
const response = await pastelAPI.generateRandom(request);
|
|
|
|
|
if (!response.success) {
|
|
|
|
|
throw new Error(response.error.message);
|
|
|
|
|
}
|
|
|
|
|
return response.data;
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const useGenerateDistinct = () => {
|
|
|
|
|
return useMutation({
|
|
|
|
|
mutationFn: async (request: DistinctColorsRequest) => {
|
|
|
|
|
const response = await pastelAPI.generateDistinct(request);
|
|
|
|
|
if (!response.success) {
|
|
|
|
|
throw new Error(response.error.message);
|
|
|
|
|
}
|
|
|
|
|
return response.data;
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const useGenerateGradient = () => {
|
|
|
|
|
return useMutation({
|
|
|
|
|
mutationFn: async (request: GradientRequest) => {
|
|
|
|
|
const response = await pastelAPI.generateGradient(request);
|
|
|
|
|
if (!response.success) {
|
|
|
|
|
throw new Error(response.error.message);
|
|
|
|
|
}
|
|
|
|
|
return response.data;
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2025-11-07 13:47:16 +01:00
|
|
|
// Color Blindness Simulation
|
|
|
|
|
export const useSimulateColorBlindness = () => {
|
|
|
|
|
return useMutation({
|
|
|
|
|
mutationFn: async (request: ColorBlindnessRequest) => {
|
|
|
|
|
const response = await pastelAPI.simulateColorBlindness(request);
|
|
|
|
|
if (!response.success) {
|
|
|
|
|
throw new Error(response.error.message);
|
|
|
|
|
}
|
|
|
|
|
return response.data;
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Text Color Optimizer
|
|
|
|
|
export const useTextColor = () => {
|
|
|
|
|
return useMutation({
|
|
|
|
|
mutationFn: async (request: TextColorRequest) => {
|
|
|
|
|
const response = await pastelAPI.getTextColor(request);
|
|
|
|
|
if (!response.success) {
|
|
|
|
|
throw new Error(response.error.message);
|
|
|
|
|
}
|
|
|
|
|
return response.data;
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
feat: implement color playground with interactive picker and info display
Add complete color manipulation interface:
**Color Components:**
- ColorPicker - Interactive hex color picker with text input
- Uses react-colorful for visual selection
- Manual input support for any color format
- Real-time updates
- ColorDisplay - Large color preview swatch
- Configurable sizes (sm, md, lg, xl)
- Optional border styling
- Accessible with ARIA labels
- ColorInfo - Comprehensive color information display
- Shows all formats: Hex, RGB, HSL, Lab, OkLab
- Copy to clipboard functionality for each format
- Displays brightness, luminance, light/dark type
- Shows named color matches
**API Integration:**
- React Query hooks for all Pastel API endpoints
- useColorInfo - Get color information
- useConvertFormat - Format conversion
- useLighten, useDarken, useSaturate, etc. - Manipulations
- useGenerateRandom, useGenerateDistinct, useGenerateGradient
- useNamedColors - Cached named colors list
- Automatic error handling and loading states
**Playground Page (/playground):**
- Two-column layout: picker + info
- Live color preview with large swatch
- Real-time API integration with loading states
- Error handling with user-friendly messages
- Quick action buttons (ready for implementation)
- Responsive grid layout
**Features:**
- Toast notifications for clipboard copy
- Loading spinners during API calls
- Error display with helpful messages
- Accessible keyboard navigation
- Smooth transitions and animations
Build successful with playground page rendering! Ready for color manipulation actions.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-07 11:05:23 +01:00
|
|
|
// Named Colors
|
|
|
|
|
export const useNamedColors = () => {
|
|
|
|
|
return useQuery({
|
|
|
|
|
queryKey: ['namedColors'],
|
|
|
|
|
queryFn: async () => {
|
|
|
|
|
const response = await pastelAPI.getNamedColors();
|
|
|
|
|
if (!response.success) {
|
|
|
|
|
throw new Error(response.error.message);
|
|
|
|
|
}
|
|
|
|
|
return response.data;
|
|
|
|
|
},
|
|
|
|
|
staleTime: Infinity, // Named colors never change
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Health Check
|
|
|
|
|
export const useHealth = () => {
|
|
|
|
|
return useQuery({
|
|
|
|
|
queryKey: ['health'],
|
|
|
|
|
queryFn: async () => {
|
|
|
|
|
const response = await pastelAPI.getHealth();
|
|
|
|
|
if (!response.success) {
|
|
|
|
|
throw new Error(response.error.message);
|
|
|
|
|
}
|
|
|
|
|
return response.data;
|
|
|
|
|
},
|
|
|
|
|
refetchInterval: 60000, // Check every minute
|
|
|
|
|
});
|
|
|
|
|
};
|
2025-11-07 14:33:38 +01:00
|
|
|
|
|
|
|
|
// Palette Generation
|
|
|
|
|
export const useGeneratePalette = () => {
|
|
|
|
|
return useMutation({
|
|
|
|
|
mutationFn: async (request: PaletteGenerateRequest) => {
|
|
|
|
|
const response = await pastelAPI.generatePalette(request);
|
|
|
|
|
if (!response.success) {
|
|
|
|
|
throw new Error(response.error.message);
|
|
|
|
|
}
|
|
|
|
|
return response.data;
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
};
|