feat: unify pastel application into single playground and remove standalone pages
This commit is contained in:
@@ -6,25 +6,10 @@ import type {
|
||||
ConvertFormatData,
|
||||
ColorManipulationRequest,
|
||||
ColorManipulationData,
|
||||
ColorMixRequest,
|
||||
ColorMixData,
|
||||
RandomColorsRequest,
|
||||
RandomColorsData,
|
||||
DistinctColorsRequest,
|
||||
DistinctColorsData,
|
||||
GradientRequest,
|
||||
GradientData,
|
||||
ColorDistanceRequest,
|
||||
ColorDistanceData,
|
||||
ColorSortRequest,
|
||||
ColorSortData,
|
||||
ColorBlindnessRequest,
|
||||
ColorBlindnessData,
|
||||
TextColorRequest,
|
||||
TextColorData,
|
||||
NamedColorsData,
|
||||
NamedColorSearchRequest,
|
||||
NamedColorSearchData,
|
||||
HealthData,
|
||||
CapabilitiesData,
|
||||
PaletteGenerateRequest,
|
||||
@@ -148,13 +133,6 @@ export class PastelAPIClient {
|
||||
});
|
||||
}
|
||||
|
||||
async mix(request: ColorMixRequest): Promise<ApiResponse<ColorMixData>> {
|
||||
return this.request<ColorMixData>('/colors/mix', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(request),
|
||||
});
|
||||
}
|
||||
|
||||
// Color Generation
|
||||
async generateRandom(request: RandomColorsRequest): Promise<ApiResponse<RandomColorsData>> {
|
||||
return this.request<RandomColorsData>('/colors/random', {
|
||||
@@ -163,13 +141,6 @@ export class PastelAPIClient {
|
||||
});
|
||||
}
|
||||
|
||||
async generateDistinct(request: DistinctColorsRequest): Promise<ApiResponse<DistinctColorsData>> {
|
||||
return this.request<DistinctColorsData>('/colors/distinct', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(request),
|
||||
});
|
||||
}
|
||||
|
||||
async generateGradient(request: GradientRequest): Promise<ApiResponse<GradientData>> {
|
||||
return this.request<GradientData>('/colors/gradient', {
|
||||
method: 'POST',
|
||||
@@ -177,50 +148,6 @@ export class PastelAPIClient {
|
||||
});
|
||||
}
|
||||
|
||||
// Color Analysis
|
||||
async calculateDistance(request: ColorDistanceRequest): Promise<ApiResponse<ColorDistanceData>> {
|
||||
return this.request<ColorDistanceData>('/colors/distance', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(request),
|
||||
});
|
||||
}
|
||||
|
||||
async sortColors(request: ColorSortRequest): Promise<ApiResponse<ColorSortData>> {
|
||||
return this.request<ColorSortData>('/colors/sort', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(request),
|
||||
});
|
||||
}
|
||||
|
||||
// Accessibility
|
||||
async simulateColorBlindness(request: ColorBlindnessRequest): Promise<ApiResponse<ColorBlindnessData>> {
|
||||
return this.request<ColorBlindnessData>('/colors/colorblind', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(request),
|
||||
});
|
||||
}
|
||||
|
||||
async getTextColor(request: TextColorRequest): Promise<ApiResponse<TextColorData>> {
|
||||
return this.request<TextColorData>('/colors/textcolor', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(request),
|
||||
});
|
||||
}
|
||||
|
||||
// Named Colors
|
||||
async getNamedColors(): Promise<ApiResponse<NamedColorsData>> {
|
||||
return this.request<NamedColorsData>('/colors/names', {
|
||||
method: 'GET',
|
||||
});
|
||||
}
|
||||
|
||||
async searchNamedColors(request: NamedColorSearchRequest): Promise<ApiResponse<NamedColorSearchData>> {
|
||||
return this.request<NamedColorSearchData>('/colors/names/search', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(request),
|
||||
});
|
||||
}
|
||||
|
||||
// System
|
||||
async getHealth(): Promise<ApiResponse<HealthData>> {
|
||||
return this.request<HealthData>('/health', {
|
||||
|
||||
@@ -2,28 +2,19 @@
|
||||
|
||||
import { useQuery, useMutation, UseQueryOptions } from '@tanstack/react-query';
|
||||
import { pastelAPI } from './client';
|
||||
import type {
|
||||
import {
|
||||
ColorInfoRequest,
|
||||
ColorInfoData,
|
||||
ConvertFormatRequest,
|
||||
ConvertFormatData,
|
||||
ColorManipulationRequest,
|
||||
ColorManipulationData,
|
||||
ColorMixRequest,
|
||||
ColorMixData,
|
||||
RandomColorsRequest,
|
||||
RandomColorsData,
|
||||
DistinctColorsRequest,
|
||||
DistinctColorsData,
|
||||
GradientRequest,
|
||||
GradientData,
|
||||
ColorBlindnessRequest,
|
||||
PaletteGenerateRequest,
|
||||
PaletteGenerateData,
|
||||
ColorBlindnessData,
|
||||
TextColorRequest,
|
||||
TextColorData,
|
||||
NamedColorsData,
|
||||
HealthData,
|
||||
} from './types';
|
||||
|
||||
@@ -132,18 +123,6 @@ export const useComplement = () => {
|
||||
});
|
||||
};
|
||||
|
||||
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({
|
||||
@@ -157,18 +136,6 @@ export const useGenerateRandom = () => {
|
||||
});
|
||||
};
|
||||
|
||||
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) => {
|
||||
@@ -181,47 +148,6 @@ export const useGenerateGradient = () => {
|
||||
});
|
||||
};
|
||||
|
||||
// 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;
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 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({
|
||||
|
||||
@@ -122,19 +122,6 @@ export interface ColorManipulationData {
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface ColorMixRequest {
|
||||
colors: string[];
|
||||
fraction: number;
|
||||
}
|
||||
|
||||
export interface ColorMixData {
|
||||
results: Array<{
|
||||
color1: string;
|
||||
color2: string;
|
||||
mixed: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface RandomColorsRequest {
|
||||
count: number;
|
||||
strategy?: 'vivid' | 'rgb' | 'gray' | 'lch';
|
||||
@@ -144,16 +131,6 @@ export interface RandomColorsData {
|
||||
colors: string[];
|
||||
}
|
||||
|
||||
export interface DistinctColorsRequest {
|
||||
count: number;
|
||||
metric?: 'cie76' | 'ciede2000';
|
||||
fixed_colors?: string[];
|
||||
}
|
||||
|
||||
export interface DistinctColorsData {
|
||||
colors: string[];
|
||||
}
|
||||
|
||||
export interface GradientRequest {
|
||||
stops: string[];
|
||||
count: number;
|
||||
@@ -165,73 +142,6 @@ export interface GradientData {
|
||||
gradient: string[];
|
||||
}
|
||||
|
||||
export interface ColorDistanceRequest {
|
||||
color1: string;
|
||||
color2: string;
|
||||
metric: 'cie76' | 'ciede2000';
|
||||
}
|
||||
|
||||
export interface ColorDistanceData {
|
||||
color1: string;
|
||||
color2: string;
|
||||
distance: number;
|
||||
metric: string;
|
||||
}
|
||||
|
||||
export interface ColorSortRequest {
|
||||
colors: string[];
|
||||
order: 'hue' | 'brightness' | 'luminance' | 'chroma';
|
||||
}
|
||||
|
||||
export interface ColorSortData {
|
||||
sorted: string[];
|
||||
}
|
||||
|
||||
export interface ColorBlindnessRequest {
|
||||
colors: string[];
|
||||
type: 'protanopia' | 'deuteranopia' | 'tritanopia';
|
||||
}
|
||||
|
||||
export interface ColorBlindnessData {
|
||||
type: string;
|
||||
colors: Array<{
|
||||
input: string;
|
||||
output: string;
|
||||
difference_percentage: number;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface TextColorRequest {
|
||||
backgrounds: string[];
|
||||
}
|
||||
|
||||
export interface TextColorData {
|
||||
colors: Array<{
|
||||
background: string;
|
||||
textcolor: string;
|
||||
contrast_ratio: number;
|
||||
wcag_aa: boolean;
|
||||
wcag_aaa: boolean;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface NamedColor {
|
||||
name: string;
|
||||
hex: string;
|
||||
}
|
||||
|
||||
export interface NamedColorsData {
|
||||
colors: NamedColor[];
|
||||
}
|
||||
|
||||
export interface NamedColorSearchRequest {
|
||||
query: string;
|
||||
}
|
||||
|
||||
export interface NamedColorSearchData {
|
||||
results: NamedColor[];
|
||||
}
|
||||
|
||||
export interface HealthData {
|
||||
status: string;
|
||||
version: string;
|
||||
@@ -246,7 +156,7 @@ export interface CapabilitiesData {
|
||||
|
||||
export interface PaletteGenerateRequest {
|
||||
base: string;
|
||||
scheme: 'monochromatic' | 'analogous' | 'complementary' | 'split-complementary' | 'triadic' | 'tetradic';
|
||||
scheme: 'monochromatic' | 'analogous' | 'complementary' | 'triadic' | 'tetradic';
|
||||
}
|
||||
|
||||
export interface PaletteGenerateData {
|
||||
|
||||
@@ -7,18 +7,9 @@ import {
|
||||
desaturate_color,
|
||||
rotate_hue,
|
||||
complement_color,
|
||||
mix_colors,
|
||||
get_text_color,
|
||||
calculate_contrast,
|
||||
simulate_protanopia,
|
||||
simulate_deuteranopia,
|
||||
simulate_tritanopia,
|
||||
color_distance,
|
||||
generate_random_colors,
|
||||
generate_gradient,
|
||||
generate_palette,
|
||||
get_all_named_colors,
|
||||
search_named_colors,
|
||||
version,
|
||||
} from '@valknarthing/pastel-wasm';
|
||||
import type {
|
||||
@@ -29,25 +20,10 @@ import type {
|
||||
ConvertFormatData,
|
||||
ColorManipulationRequest,
|
||||
ColorManipulationData,
|
||||
ColorMixRequest,
|
||||
ColorMixData,
|
||||
RandomColorsRequest,
|
||||
RandomColorsData,
|
||||
DistinctColorsRequest,
|
||||
DistinctColorsData,
|
||||
GradientRequest,
|
||||
GradientData,
|
||||
ColorDistanceRequest,
|
||||
ColorDistanceData,
|
||||
ColorSortRequest,
|
||||
ColorSortData,
|
||||
ColorBlindnessRequest,
|
||||
ColorBlindnessData,
|
||||
TextColorRequest,
|
||||
TextColorData,
|
||||
NamedColorsData,
|
||||
NamedColorSearchRequest,
|
||||
NamedColorSearchData,
|
||||
HealthData,
|
||||
CapabilitiesData,
|
||||
PaletteGenerateRequest,
|
||||
@@ -98,7 +74,7 @@ export class PastelWASMClient {
|
||||
async getColorInfo(request: ColorInfoRequest): Promise<ApiResponse<ColorInfoData>> {
|
||||
return this.request(() => {
|
||||
const colors = request.colors.map((colorStr) => {
|
||||
const info = parse_color(colorStr);
|
||||
const info = parse_color(colorStr) as any;
|
||||
return {
|
||||
input: info.input,
|
||||
hex: info.hex,
|
||||
@@ -123,9 +99,9 @@ export class PastelWASMClient {
|
||||
b: info.lab[2],
|
||||
},
|
||||
oklab: {
|
||||
l: info.lab[0] / 100.0,
|
||||
a: info.lab[1] / 100.0,
|
||||
b: info.lab[2] / 100.0,
|
||||
l: info.oklab ? info.oklab[0] : info.lab[0] / 100.0,
|
||||
a: info.oklab ? info.oklab[1] : info.lab[1] / 100.0,
|
||||
b: info.oklab ? info.oklab[2] : info.lab[2] / 100.0,
|
||||
},
|
||||
lch: {
|
||||
l: info.lch[0],
|
||||
@@ -133,9 +109,9 @@ export class PastelWASMClient {
|
||||
h: info.lch[2],
|
||||
},
|
||||
oklch: {
|
||||
l: info.lch[0] / 100.0,
|
||||
c: info.lch[1] / 100.0,
|
||||
h: info.lch[2],
|
||||
l: info.oklch ? info.oklch[0] : info.lch[0] / 100.0,
|
||||
c: info.oklch ? info.oklch[1] : info.lch[1] / 100.0,
|
||||
h: info.oklch ? info.oklch[2] : info.lch[2],
|
||||
},
|
||||
cmyk: {
|
||||
c: 0,
|
||||
@@ -156,7 +132,7 @@ export class PastelWASMClient {
|
||||
async convertFormat(request: ConvertFormatRequest): Promise<ApiResponse<ConvertFormatData>> {
|
||||
return this.request(() => {
|
||||
const conversions = request.colors.map((colorStr) => {
|
||||
const parsed = parse_color(colorStr);
|
||||
const parsed = parse_color(colorStr) as any;
|
||||
let output: string;
|
||||
|
||||
switch (request.format) {
|
||||
@@ -178,6 +154,20 @@ export class PastelWASMClient {
|
||||
case 'lch':
|
||||
output = `lch(${parsed.lch[0].toFixed(2)}, ${parsed.lch[1].toFixed(2)}, ${parsed.lch[2].toFixed(2)})`;
|
||||
break;
|
||||
case 'oklab': {
|
||||
const l = parsed.oklab ? parsed.oklab[0] : parsed.lab[0] / 100.0;
|
||||
const a = parsed.oklab ? parsed.oklab[1] : parsed.lab[1] / 100.0;
|
||||
const b = parsed.oklab ? parsed.oklab[2] : parsed.lab[2] / 100.0;
|
||||
output = `oklab(${(l * 100).toFixed(1)}% ${a.toFixed(3)} ${b.toFixed(3)})`;
|
||||
break;
|
||||
}
|
||||
case 'oklch': {
|
||||
const l = parsed.oklch ? parsed.oklch[0] : parsed.lch[0] / 100.0;
|
||||
const c = parsed.oklch ? parsed.oklch[1] : parsed.lch[1] / 100.0;
|
||||
const h = parsed.oklch ? parsed.oklch[2] : parsed.lch[2];
|
||||
output = `oklch(${(l * 100).toFixed(1)}% ${c.toFixed(3)} ${h.toFixed(2)})`;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
output = parsed.hex;
|
||||
}
|
||||
@@ -262,20 +252,6 @@ export class PastelWASMClient {
|
||||
});
|
||||
}
|
||||
|
||||
async mix(request: ColorMixRequest): Promise<ApiResponse<ColorMixData>> {
|
||||
return this.request(() => {
|
||||
// Mix pairs of colors
|
||||
const results = [];
|
||||
for (let i = 0; i < request.colors.length - 1; i += 2) {
|
||||
const color1 = request.colors[i];
|
||||
const color2 = request.colors[i + 1];
|
||||
const mixed = mix_colors(color1, color2, request.fraction);
|
||||
results.push({ color1, color2, mixed });
|
||||
}
|
||||
return { results };
|
||||
});
|
||||
}
|
||||
|
||||
// Color Generation
|
||||
async generateRandom(request: RandomColorsRequest): Promise<ApiResponse<RandomColorsData>> {
|
||||
return this.request(() => {
|
||||
@@ -285,17 +261,6 @@ export class PastelWASMClient {
|
||||
});
|
||||
}
|
||||
|
||||
async generateDistinct(request: DistinctColorsRequest): Promise<ApiResponse<DistinctColorsData>> {
|
||||
return this.request(() => {
|
||||
// Note: WASM version doesn't support distinct colors with simulated annealing yet
|
||||
// Fall back to vivid random colors
|
||||
const colors = generate_random_colors(request.count, true);
|
||||
return {
|
||||
colors,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async generateGradient(request: GradientRequest): Promise<ApiResponse<GradientData>> {
|
||||
return this.request(() => {
|
||||
if (request.stops.length < 2) {
|
||||
@@ -335,93 +300,6 @@ export class PastelWASMClient {
|
||||
});
|
||||
}
|
||||
|
||||
// Color Analysis
|
||||
async calculateDistance(request: ColorDistanceRequest): Promise<ApiResponse<ColorDistanceData>> {
|
||||
return this.request(() => {
|
||||
const useCiede2000 = request.metric === 'ciede2000';
|
||||
const distance = color_distance(request.color1, request.color2, useCiede2000);
|
||||
return {
|
||||
color1: request.color1,
|
||||
color2: request.color2,
|
||||
distance,
|
||||
metric: request.metric,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async sortColors(request: ColorSortRequest): Promise<ApiResponse<ColorSortData>> {
|
||||
return this.request(() => {
|
||||
// Note: WASM version doesn't support sorting yet
|
||||
// Return colors as-is for now
|
||||
return { sorted: request.colors };
|
||||
});
|
||||
}
|
||||
|
||||
// Accessibility
|
||||
async simulateColorBlindness(request: ColorBlindnessRequest): Promise<ApiResponse<ColorBlindnessData>> {
|
||||
return this.request(() => {
|
||||
const colors = request.colors.map((colorStr) => {
|
||||
let output: string;
|
||||
switch (request.type) {
|
||||
case 'protanopia':
|
||||
output = simulate_protanopia(colorStr);
|
||||
break;
|
||||
case 'deuteranopia':
|
||||
output = simulate_deuteranopia(colorStr);
|
||||
break;
|
||||
case 'tritanopia':
|
||||
output = simulate_tritanopia(colorStr);
|
||||
break;
|
||||
default:
|
||||
output = colorStr;
|
||||
}
|
||||
|
||||
const distance = color_distance(colorStr, output, true);
|
||||
return {
|
||||
input: colorStr,
|
||||
output,
|
||||
difference_percentage: (distance / 100.0) * 100.0,
|
||||
};
|
||||
});
|
||||
|
||||
return { type: request.type, colors };
|
||||
});
|
||||
}
|
||||
|
||||
async getTextColor(request: TextColorRequest): Promise<ApiResponse<TextColorData>> {
|
||||
return this.request(() => {
|
||||
const colors = request.backgrounds.map((bg) => {
|
||||
const textColor = get_text_color(bg);
|
||||
const contrastRatio = calculate_contrast(bg, textColor);
|
||||
|
||||
return {
|
||||
background: bg,
|
||||
textcolor: textColor,
|
||||
contrast_ratio: contrastRatio,
|
||||
wcag_aa: contrastRatio >= 4.5,
|
||||
wcag_aaa: contrastRatio >= 7.0,
|
||||
};
|
||||
});
|
||||
|
||||
return { colors };
|
||||
});
|
||||
}
|
||||
|
||||
// Named Colors
|
||||
async getNamedColors(): Promise<ApiResponse<NamedColorsData>> {
|
||||
return this.request(() => {
|
||||
const colors = get_all_named_colors();
|
||||
return { colors };
|
||||
});
|
||||
}
|
||||
|
||||
async searchNamedColors(request: NamedColorSearchRequest): Promise<ApiResponse<NamedColorSearchData>> {
|
||||
return this.request(() => {
|
||||
const results = search_named_colors(request.query);
|
||||
return { results };
|
||||
});
|
||||
}
|
||||
|
||||
// System
|
||||
async getHealth(): Promise<ApiResponse<HealthData>> {
|
||||
return this.request(() => ({
|
||||
@@ -442,12 +320,8 @@ export class PastelWASMClient {
|
||||
'colors/rotate',
|
||||
'colors/complement',
|
||||
'colors/grayscale',
|
||||
'colors/mix',
|
||||
'colors/random',
|
||||
'colors/gradient',
|
||||
'colors/colorblind',
|
||||
'colors/textcolor',
|
||||
'colors/distance',
|
||||
'colors/names',
|
||||
],
|
||||
formats: ['hex', 'rgb', 'hsl', 'hsv', 'lab', 'lch'],
|
||||
|
||||
Reference in New Issue
Block a user