Files
pastel-ui/lib/api/client.ts

233 lines
6.4 KiB
TypeScript
Raw Normal View History

feat: initial Next.js 15 implementation with TypeScript and Tailwind CSS 4 Add complete project structure and foundation: **Core Setup:** - Next.js 15.5.6 with App Router and React 19 - TypeScript 5.7 with strict mode - Tailwind CSS 4.1 with custom theme configuration - ESLint and Prettier configuration **Dependencies Installed:** - @tanstack/react-query - Server state management - zustand - Client state management - framer-motion - Animations - lucide-react - Icon library - react-colorful - Color picker component - cmdk - Command palette - sonner - Toast notifications - clsx + tailwind-merge - Class name utilities **Project Structure:** - app/ - Next.js App Router pages - components/ - React components (ui, color, tools, layout, providers) - lib/ - Utilities, API client, hooks, stores, constants - tests/ - Unit and E2E test directories **API Integration:** - Type-safe Pastel API client with all 21 endpoints - Complete TypeScript type definitions for requests/responses - Error handling and response types **UI Components:** - Button component with variants (default, outline, ghost, destructive) - Input component with focus states - Providers wrapper (React Query, Toast) - Root layout with Inter font and metadata **Pages:** - Home page with gradient hero and feature cards - Links to playground and palette generation (pages pending) **Configuration:** - Tailwind with HSL color variables for theming - Dark/light mode CSS variables - Custom animations (fade-in, slide-up, slide-down) - @tailwindcss/forms and @tailwindcss/typography plugins Build successful: 102 kB First Load JS, static generation working. Ready for color components and playground implementation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-07 10:55:42 +01:00
import type {
ApiResponse,
ColorInfoRequest,
ColorInfoData,
ConvertFormatRequest,
ConvertFormatData,
ColorManipulationRequest,
ColorManipulationData,
ColorMixRequest,
ColorMixData,
RandomColorsRequest,
RandomColorsData,
DistinctColorsRequest,
DistinctColorsData,
GradientRequest,
GradientData,
ColorDistanceRequest,
ColorDistanceData,
ColorSortRequest,
ColorSortData,
ColorBlindnessRequest,
ColorBlindnessData,
TextColorRequest,
TextColorData,
NamedColorsData,
NamedColorSearchRequest,
NamedColorSearchData,
HealthData,
CapabilitiesData,
} from './types';
export class PastelAPIClient {
private baseURL: string;
constructor(baseURL?: string) {
this.baseURL = baseURL || process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000';
}
private async request<T>(
endpoint: string,
options?: RequestInit
): Promise<ApiResponse<T>> {
const url = `${this.baseURL}/api/v1${endpoint}`;
try {
const response = await fetch(url, {
...options,
headers: {
'Content-Type': 'application/json',
...options?.headers,
},
});
const data = await response.json();
if (!response.ok) {
return {
success: false,
error: data.error || {
code: 'INTERNAL_ERROR',
message: 'An unknown error occurred',
},
};
}
return data;
} catch (error) {
return {
success: false,
error: {
code: 'NETWORK_ERROR',
message: error instanceof Error ? error.message : 'Network request failed',
},
};
}
}
// Color Information
async getColorInfo(request: ColorInfoRequest): Promise<ApiResponse<ColorInfoData>> {
return this.request<ColorInfoData>('/colors/info', {
method: 'POST',
body: JSON.stringify(request),
});
}
// Format Conversion
async convertFormat(request: ConvertFormatRequest): Promise<ApiResponse<ConvertFormatData>> {
return this.request<ConvertFormatData>('/colors/convert', {
method: 'POST',
body: JSON.stringify(request),
});
}
// Color Manipulation
async lighten(request: ColorManipulationRequest): Promise<ApiResponse<ColorManipulationData>> {
return this.request<ColorManipulationData>('/colors/lighten', {
method: 'POST',
body: JSON.stringify(request),
});
}
async darken(request: ColorManipulationRequest): Promise<ApiResponse<ColorManipulationData>> {
return this.request<ColorManipulationData>('/colors/darken', {
method: 'POST',
body: JSON.stringify(request),
});
}
async saturate(request: ColorManipulationRequest): Promise<ApiResponse<ColorManipulationData>> {
return this.request<ColorManipulationData>('/colors/saturate', {
method: 'POST',
body: JSON.stringify(request),
});
}
async desaturate(request: ColorManipulationRequest): Promise<ApiResponse<ColorManipulationData>> {
return this.request<ColorManipulationData>('/colors/desaturate', {
method: 'POST',
body: JSON.stringify(request),
});
}
async rotate(request: ColorManipulationRequest): Promise<ApiResponse<ColorManipulationData>> {
return this.request<ColorManipulationData>('/colors/rotate', {
method: 'POST',
body: JSON.stringify(request),
});
}
async complement(colors: string[]): Promise<ApiResponse<ColorManipulationData>> {
return this.request<ColorManipulationData>('/colors/complement', {
method: 'POST',
body: JSON.stringify({ colors }),
});
}
async grayscale(colors: string[]): Promise<ApiResponse<ColorManipulationData>> {
return this.request<ColorManipulationData>('/colors/grayscale', {
method: 'POST',
body: JSON.stringify({ colors }),
});
}
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', {
method: 'POST',
body: JSON.stringify(request),
});
}
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',
body: JSON.stringify(request),
});
}
// 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', {
method: 'GET',
});
}
async getCapabilities(): Promise<ApiResponse<CapabilitiesData>> {
return this.request<CapabilitiesData>('/capabilities', {
method: 'GET',
});
}
}
// Export singleton instance
export const pastelAPI = new PastelAPIClient();