import { parse_color, lighten_color, darken_color, saturate_color, desaturate_color, rotate_hue, complement_color, generate_random_colors, generate_gradient, generate_palette, version, } from './color-engine'; import type { ApiResponse, ColorInfoRequest, ColorInfoData, ConvertFormatRequest, ConvertFormatData, ColorManipulationRequest, ColorManipulationData, RandomColorsRequest, RandomColorsData, GradientRequest, GradientData, HealthData, CapabilitiesData, PaletteGenerateRequest, PaletteGenerateData, } from './types'; /** * Color client backed by a pure TypeScript color engine. * Zero network latency, works offline — no WASM required. */ export class ColorWASMClient { private request(fn: () => T): Promise> { try { return Promise.resolve({ success: true, data: fn() }); } catch (error) { return Promise.resolve({ success: false, error: { code: 'COLOR_ERROR', message: error instanceof Error ? error.message : 'Unknown error', }, }); } } // Color Information async getColorInfo(request: ColorInfoRequest): Promise> { return this.request(() => { const colors = request.colors.map((colorStr) => { const info = parse_color(colorStr); return { input: info.input, hex: info.hex, rgb: { r: info.rgb[0], g: info.rgb[1], b: info.rgb[2] }, hsl: { h: info.hsl[0], s: info.hsl[1], l: info.hsl[2] }, hsv: { h: info.hsv[0], s: info.hsv[1], v: info.hsv[2] }, lab: { l: info.lab[0], a: info.lab[1], b: info.lab[2] }, oklab: { l: info.oklab[0], a: info.oklab[1], b: info.oklab[2] }, lch: { l: info.lch[0], c: info.lch[1], h: info.lch[2] }, oklch: { l: info.oklch[0], c: info.oklch[1], h: info.oklch[2] }, cmyk: { c: info.cmyk[0], m: info.cmyk[1], y: info.cmyk[2], k: info.cmyk[3] }, brightness: info.brightness, luminance: info.luminance, is_light: info.is_light, }; }); return { colors }; }); } // Format Conversion async convertFormat(request: ConvertFormatRequest): Promise> { return this.request(() => { const conversions = request.colors.map((colorStr) => { const p = parse_color(colorStr); let output: string; switch (request.format) { case 'hex': output = p.hex; break; case 'rgb': output = `rgb(${p.rgb[0]}, ${p.rgb[1]}, ${p.rgb[2]})`; break; case 'hsl': output = `hsl(${p.hsl[0].toFixed(1)}, ${(p.hsl[1] * 100).toFixed(1)}%, ${(p.hsl[2] * 100).toFixed(1)}%)`; break; case 'hsv': output = `hsv(${p.hsv[0].toFixed(1)}, ${(p.hsv[1] * 100).toFixed(1)}%, ${(p.hsv[2] * 100).toFixed(1)}%)`; break; case 'lab': output = `lab(${p.lab[0].toFixed(2)}, ${p.lab[1].toFixed(2)}, ${p.lab[2].toFixed(2)})`; break; case 'lch': output = `lch(${p.lch[0].toFixed(2)}, ${p.lch[1].toFixed(2)}, ${p.lch[2].toFixed(2)})`; break; case 'oklab': output = `oklab(${(p.oklab[0] * 100).toFixed(1)}% ${p.oklab[1].toFixed(3)} ${p.oklab[2].toFixed(3)})`; break; case 'oklch': output = `oklch(${(p.oklch[0] * 100).toFixed(1)}% ${p.oklch[1].toFixed(3)} ${p.oklch[2].toFixed(2)})`; break; default: output = p.hex; } return { input: colorStr, output }; }); return { conversions }; }); } // Color Manipulation async lighten(request: ColorManipulationRequest): Promise> { return this.request(() => ({ operation: 'lighten', amount: request.amount, colors: request.colors.map((c) => ({ input: c, output: lighten_color(c, request.amount) })), })); } async darken(request: ColorManipulationRequest): Promise> { return this.request(() => ({ operation: 'darken', amount: request.amount, colors: request.colors.map((c) => ({ input: c, output: darken_color(c, request.amount) })), })); } async saturate(request: ColorManipulationRequest): Promise> { return this.request(() => ({ operation: 'saturate', amount: request.amount, colors: request.colors.map((c) => ({ input: c, output: saturate_color(c, request.amount) })), })); } async desaturate(request: ColorManipulationRequest): Promise> { return this.request(() => ({ operation: 'desaturate', amount: request.amount, colors: request.colors.map((c) => ({ input: c, output: desaturate_color(c, request.amount) })), })); } async rotate(request: ColorManipulationRequest): Promise> { return this.request(() => ({ operation: 'rotate', amount: request.amount, colors: request.colors.map((c) => ({ input: c, output: rotate_hue(c, request.amount) })), })); } async complement(colors: string[]): Promise> { return this.request(() => ({ operation: 'complement', colors: colors.map((c) => ({ input: c, output: complement_color(c) })), })); } async grayscale(colors: string[]): Promise> { return this.request(() => ({ operation: 'grayscale', colors: colors.map((c) => ({ input: c, output: desaturate_color(c, 1.0) })), })); } // Color Generation async generateRandom(request: RandomColorsRequest): Promise> { return this.request(() => { const vivid = request.strategy === 'vivid' || request.strategy === 'lch'; return { colors: generate_random_colors(request.count, vivid) }; }); } async generateGradient(request: GradientRequest): Promise> { return this.request(() => { if (request.stops.length < 2) throw new Error('At least 2 color stops are required'); if (request.stops.length === 2) { return { stops: request.stops, count: request.count, gradient: generate_gradient(request.stops[0], request.stops[1], request.count), }; } // Multi-stop: interpolate segment by segment const segments = request.stops.length - 1; const colorsPerSegment = Math.floor(request.count / segments); const gradient: string[] = []; for (let i = 0; i < segments; i++) { const segColors = generate_gradient( request.stops[i], request.stops[i + 1], i === segments - 1 ? request.count - gradient.length : colorsPerSegment, ); gradient.push(...segColors.slice(0, -1)); } gradient.push(request.stops[request.stops.length - 1]); return { stops: request.stops, count: request.count, gradient }; }); } // System async getHealth(): Promise> { return this.request(() => ({ status: 'healthy', version: version() })); } async getCapabilities(): Promise> { return this.request(() => ({ endpoints: [ 'colors/info', 'colors/convert', 'colors/lighten', 'colors/darken', 'colors/saturate', 'colors/desaturate', 'colors/rotate', 'colors/complement', 'colors/grayscale', 'colors/random', 'colors/gradient', 'colors/names', ], formats: ['hex', 'rgb', 'hsl', 'hsv', 'lab', 'lch'], distance_metrics: ['cie76', 'ciede2000'], colorblindness_types: ['protanopia', 'deuteranopia', 'tritanopia'], })); } // Palette Generation async generatePalette(request: PaletteGenerateRequest): Promise> { return this.request(() => { const colors = generate_palette(request.base, request.scheme); return { base: request.base, scheme: request.scheme, palette: { primary: colors[0], secondary: colors.slice(1) }, }; }); } } // Export singleton instance export const colorWASM = new ColorWASMClient();