UNPKG

@tamilvananmurugan/xlibs

Version:

Comprehensive UI component library with Aceternity, MagicUI, and ShadCN components

181 lines (152 loc) 5.55 kB
// LLM-Based Fuzzy Component Name Matcher import { ComponentMetadata, componentRegistry } from './component-registry'; export interface FuzzyMatchResult { component: ComponentMetadata; confidence: number; reason: string; } export class FuzzyComponentMatcher { /** * Find the best matching component for a given name using fuzzy matching * @param componentName The name to match against * @returns The best matching component with confidence score */ static findBestMatch(componentName: string): FuzzyMatchResult | null { // First try exact match const exactMatch = Object.values(componentRegistry).find( c => c.name.toLowerCase() === componentName.toLowerCase() ); if (exactMatch) { return { component: exactMatch, confidence: 1.0, reason: 'Exact match found' }; } // If no exact match, try fuzzy matching const candidates = this.getFuzzyCandidates(componentName); if (candidates.length === 0) { return null; } // Sort by confidence and return the best match candidates.sort((a, b) => b.confidence - a.confidence); return candidates[0]; } /** * Get fuzzy matching candidates for a component name * @param componentName The name to match against * @returns Array of fuzzy match results */ private static getFuzzyCandidates(componentName: string): FuzzyMatchResult[] { const candidates: FuzzyMatchResult[] = []; const lowerComponentName = componentName.toLowerCase(); // Get all components const allComponents = Object.values(componentRegistry); for (const component of allComponents) { const lowerComponent = component.name.toLowerCase(); // Calculate confidence score based on various factors let confidence = 0; const reasons: string[] = []; // Exact substring match if (lowerComponent.includes(lowerComponentName) || lowerComponentName.includes(lowerComponent)) { confidence += 0.4; reasons.push('substring match'); } // Levenshtein distance (simplified) const distance = this.levenshteinDistance(lowerComponentName, lowerComponent); const maxLength = Math.max(lowerComponentName.length, lowerComponent.length); const similarity = 1 - (distance / maxLength); if (similarity > 0.5) { confidence += similarity * 0.3; reasons.push(`similarity: ${(similarity * 100).toFixed(1)}%`); } // Shared prefix/suffix const sharedPrefix = this.getSharedPrefix(lowerComponentName, lowerComponent); const sharedSuffix = this.getSharedSuffix(lowerComponentName, lowerComponent); if (sharedPrefix.length > 0) { const prefixRatio = sharedPrefix.length / Math.min(lowerComponentName.length, lowerComponent.length); confidence += prefixRatio * 0.15; reasons.push(`shared prefix: ${sharedPrefix}`); } if (sharedSuffix.length > 0) { const suffixRatio = sharedSuffix.length / Math.min(lowerComponentName.length, lowerComponent.length); confidence += suffixRatio * 0.15; reasons.push(`shared suffix: ${sharedSuffix}`); } // Only include candidates with reasonable confidence if (confidence > 0.3) { candidates.push({ component, confidence, reason: reasons.join(', ') }); } } return candidates; } /** * Calculate Levenshtein distance between two strings * @param a First string * @param b Second string * @returns The Levenshtein distance */ private static levenshteinDistance(a: string, b: string): number { if (a.length === 0) return b.length; if (b.length === 0) return a.length; const matrix: number[][] = []; // Increment along the first column of each row for (let i = 0; i <= b.length; i++) { matrix[i] = [i]; } // Increment each column in the first row for (let j = 0; j <= a.length; j++) { matrix[0][j] = j; } // Fill in the rest of the matrix for (let i = 1; i <= b.length; i++) { for (let j = 1; j <= a.length; j++) { if (b.charAt(i - 1) === a.charAt(j - 1)) { matrix[i][j] = matrix[i - 1][j - 1]; } else { matrix[i][j] = Math.min( matrix[i - 1][j - 1] + 1, // substitution matrix[i][j - 1] + 1, // insertion matrix[i - 1][j] + 1 // deletion ); } } } return matrix[b.length][a.length]; } /** * Get the shared prefix between two strings * @param a First string * @param b Second string * @returns The shared prefix */ private static getSharedPrefix(a: string, b: string): string { let i = 0; while (i < a.length && i < b.length && a.charAt(i) === b.charAt(i)) { i++; } return a.substring(0, i); } /** * Get the shared suffix between two strings * @param a First string * @param b Second string * @returns The shared suffix */ private static getSharedSuffix(a: string, b: string): string { let i = 0; const minLen = Math.min(a.length, b.length); while (i < minLen && a.charAt(a.length - 1 - i) === b.charAt(b.length - 1 - i)) { i++; } return a.substring(a.length - i); } } // Usage example export const FuzzyMatcher = { findBestMatch: (componentName: string) => FuzzyComponentMatcher.findBestMatch(componentName) };