Skip to main content
Glama
index.ts3.19 kB
/** * Shared utilities for MCP UI components. */ /** * Debounce a function to only execute after a delay since the last call. */ export function debounce<T extends (...args: unknown[]) => void>( fn: T, delay: number ): (...args: Parameters<T>) => void { let timeoutId: ReturnType<typeof setTimeout>; return (...args: Parameters<T>) => { clearTimeout(timeoutId); timeoutId = setTimeout(() => fn(...args), delay); }; } /** * Throttle a function to execute at most once per interval. */ export function throttle<T extends (...args: unknown[]) => void>( fn: T, interval: number ): (...args: Parameters<T>) => void { let lastTime = 0; let timeoutId: ReturnType<typeof setTimeout> | null = null; return (...args: Parameters<T>) => { const now = Date.now(); const remaining = interval - (now - lastTime); if (remaining <= 0) { if (timeoutId) { clearTimeout(timeoutId); timeoutId = null; } lastTime = now; fn(...args); } else if (!timeoutId) { timeoutId = setTimeout(() => { lastTime = Date.now(); timeoutId = null; fn(...args); }, remaining); } }; } /** * Generate a unique ID with optional prefix. */ export function uniqueId(prefix = 'mcp'): string { return `${prefix}-${Math.random().toString(36).slice(2, 9)}`; } /** * Clamp a number between min and max values. */ export function clamp(value: number, min: number, max: number): number { return Math.min(Math.max(value, min), max); } /** * Format a number with thousands separators. */ export function formatNumber(num: number): string { return num.toLocaleString(); } /** * Copy text to clipboard with fallback. */ export async function copyToClipboard(text: string): Promise<boolean> { try { if (navigator.clipboard && navigator.clipboard.writeText) { await navigator.clipboard.writeText(text); return true; } // Fallback for older browsers const textarea = document.createElement('textarea'); textarea.value = text; textarea.style.position = 'fixed'; textarea.style.left = '-9999px'; document.body.appendChild(textarea); textarea.select(); const success = document.execCommand('copy'); document.body.removeChild(textarea); return success; } catch { return false; } } /** * Escape HTML special characters. */ export function escapeHtml(text: string): string { const map: Record<string, string> = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#039;', }; return text.replace(/[&<>"']/g, (char) => map[char]); } /** * Check if the user prefers reduced motion. */ export function prefersReducedMotion(): boolean { return window.matchMedia('(prefers-reduced-motion: reduce)').matches; } /** * Check if the user prefers dark mode via media query. */ export function prefersDarkMode(): boolean { return window.matchMedia('(prefers-color-scheme: dark)').matches; } /** * Simple class names utility - filters falsy values and joins. */ export function cx( ...classes: (string | boolean | undefined | null)[] ): string { return classes.filter(Boolean).join(' '); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/heyadam/mcpsystemdesign'

If you have feedback or need assistance with the MCP directory API, please join our Discord server