Set up the complete project foundation for ClawX, a graphical AI assistant: - Electron main process with IPC handlers, menu, tray, and gateway management - React renderer with routing, layout components, and page scaffolding - Zustand state management for gateway, settings, channels, skills, chat, and cron - shadcn/ui components with Tailwind CSS and CSS variable theming - Build tooling with Vite, electron-builder, and TypeScript configuration - Testing setup with Vitest and Playwright - Development configurations (ESLint, Prettier, gitignore, env example)
73 lines
1.8 KiB
TypeScript
73 lines
1.8 KiB
TypeScript
/**
|
|
* Utility Functions
|
|
* Common utility functions for the application
|
|
*/
|
|
import { type ClassValue, clsx } from 'clsx';
|
|
import { twMerge } from 'tailwind-merge';
|
|
|
|
/**
|
|
* Merge class names with Tailwind CSS classes
|
|
*/
|
|
export function cn(...inputs: ClassValue[]) {
|
|
return twMerge(clsx(inputs));
|
|
}
|
|
|
|
/**
|
|
* Format relative time (e.g., "2 minutes ago")
|
|
*/
|
|
export function formatRelativeTime(date: string | Date): string {
|
|
const now = new Date();
|
|
const then = new Date(date);
|
|
const diffMs = now.getTime() - then.getTime();
|
|
const diffSec = Math.floor(diffMs / 1000);
|
|
const diffMin = Math.floor(diffSec / 60);
|
|
const diffHour = Math.floor(diffMin / 60);
|
|
const diffDay = Math.floor(diffHour / 24);
|
|
|
|
if (diffSec < 60) {
|
|
return 'just now';
|
|
} else if (diffMin < 60) {
|
|
return `${diffMin} minute${diffMin > 1 ? 's' : ''} ago`;
|
|
} else if (diffHour < 24) {
|
|
return `${diffHour} hour${diffHour > 1 ? 's' : ''} ago`;
|
|
} else if (diffDay < 7) {
|
|
return `${diffDay} day${diffDay > 1 ? 's' : ''} ago`;
|
|
} else {
|
|
return then.toLocaleDateString();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Format duration in seconds to human-readable string
|
|
*/
|
|
export function formatDuration(seconds: number): string {
|
|
const hours = Math.floor(seconds / 3600);
|
|
const minutes = Math.floor((seconds % 3600) / 60);
|
|
const secs = seconds % 60;
|
|
|
|
if (hours > 0) {
|
|
return `${hours}h ${minutes}m`;
|
|
} else if (minutes > 0) {
|
|
return `${minutes}m ${secs}s`;
|
|
} else {
|
|
return `${secs}s`;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Delay for a specified number of milliseconds
|
|
*/
|
|
export function delay(ms: number): Promise<void> {
|
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
}
|
|
|
|
/**
|
|
* Truncate text with ellipsis
|
|
*/
|
|
export function truncate(text: string, maxLength: number): string {
|
|
if (text.length <= maxLength) {
|
|
return text;
|
|
}
|
|
return text.slice(0, maxLength - 3) + '...';
|
|
}
|