Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Generate complete design systems with styles, colors, fonts, and UX rules for web and mobile apps
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
scripts/core.py
1#!/usr/bin/env python32# -*- coding: utf-8 -*-3"""4UI/UX Pro Max Core - BM25 search engine for UI/UX style guides5"""67import csv8import re9from pathlib import Path10from math import log11from collections import defaultdict1213# ============ CONFIGURATION ============14DATA_DIR = Path(__file__).parent.parent / "data"15MAX_RESULTS = 31617CSV_CONFIG = {18"style": {19"file": "styles.csv",20"search_cols": ["Style Category", "Keywords", "Best For", "Type", "AI Prompt Keywords"],21"output_cols": ["Style Category", "Type", "Keywords", "Primary Colors", "Effects & Animation", "Best For", "Light Mode ✓", "Dark Mode ✓", "Performance", "Accessibility", "Framework Compatibility", "Complexity", "AI Prompt Keywords", "CSS/Technical Keywords", "Implementation Checklist", "Design System Variables"]22},23"color": {24"file": "colors.csv",25"search_cols": ["Product Type", "Notes"],26"output_cols": ["Product Type", "Primary", "On Primary", "Secondary", "On Secondary", "Accent", "On Accent", "Background", "Foreground", "Card", "Card Foreground", "Muted", "Muted Foreground", "Border", "Destructive", "On Destructive", "Ring", "Notes"]27},28"chart": {29"file": "charts.csv",30"search_cols": ["Data Type", "Keywords", "Best Chart Type", "When to Use", "When NOT to Use", "Accessibility Notes"],31"output_cols": ["Data Type", "Keywords", "Best Chart Type", "Secondary Options", "When to Use", "When NOT to Use", "Data Volume Threshold", "Color Guidance", "Accessibility Grade", "Accessibility Notes", "A11y Fallback", "Library Recommendation", "Interactive Level"]32},33"landing": {34"file": "landing.csv",35"search_cols": ["Pattern Name", "Keywords", "Conversion Optimization", "Section Order"],36"output_cols": ["Pattern Name", "Keywords", "Section Order", "Primary CTA Placement", "Color Strategy", "Conversion Optimization"]37},38"product": {39"file": "products.csv",40"search_cols": ["Product Type", "Keywords", "Primary Style Recommendation", "Key Considerations"],41"output_cols": ["Product Type", "Keywords", "Primary Style Recommendation", "Secondary Styles", "Landing Page Pattern", "Dashboard Style (if applicable)", "Color Palette Focus"]42},43"ux": {44"file": "ux-guidelines.csv",45"search_cols": ["Category", "Issue", "Description", "Platform"],46"output_cols": ["Category", "Issue", "Platform", "Description", "Do", "Don't", "Code Example Good", "Code Example Bad", "Severity"]47},48"typography": {49"file": "typography.csv",50"search_cols": ["Font Pairing Name", "Category", "Mood/Style Keywords", "Best For", "Heading Font", "Body Font"],51"output_cols": ["Font Pairing Name", "Category", "Heading Font", "Body Font", "Mood/Style Keywords", "Best For", "Google Fonts URL", "CSS Import", "Tailwind Config", "Notes"]52},53"icons": {54"file": "icons.csv",55"search_cols": ["Category", "Icon Name", "Keywords", "Best For"],56"output_cols": ["Category", "Icon Name", "Keywords", "Library", "Import Code", "Usage", "Best For", "Style"]57},58"react": {59"file": "react-performance.csv",60"search_cols": ["Category", "Issue", "Keywords", "Description"],61"output_cols": ["Category", "Issue", "Platform", "Description", "Do", "Don't", "Code Example Good", "Code Example Bad", "Severity"]62},63"web": {64"file": "app-interface.csv",65"search_cols": ["Category", "Issue", "Keywords", "Description"],66"output_cols": ["Category", "Issue", "Platform", "Description", "Do", "Don't", "Code Example Good", "Code Example Bad", "Severity"]67},68"google-fonts": {69"file": "google-fonts.csv",70"search_cols": ["Family", "Category", "Stroke", "Classifications", "Keywords", "Subsets", "Designers"],71"output_cols": ["Family", "Category", "Stroke", "Classifications", "Styles", "Variable Axes", "Subsets", "Designers", "Popularity Rank", "Google Fonts URL"]72}73}7475STACK_CONFIG = {76"react": {"file": "stacks/react.csv"},77"nextjs": {"file": "stacks/nextjs.csv"},78"vue": {"file": "stacks/vue.csv"},79"svelte": {"file": "stacks/svelte.csv"},80"astro": {"file": "stacks/astro.csv"},81"swiftui": {"file": "stacks/swiftui.csv"},82"react-native": {"file": "stacks/react-native.csv"},83"flutter": {"file": "stacks/flutter.csv"},84"nuxtjs": {"file": "stacks/nuxtjs.csv"},85"nuxt-ui": {"file": "stacks/nuxt-ui.csv"},86"html-tailwind": {"file": "stacks/html-tailwind.csv"},87"shadcn": {"file": "stacks/shadcn.csv"},88"jetpack-compose": {"file": "stacks/jetpack-compose.csv"},89"threejs": {"file": "stacks/threejs.csv"},90"angular": {"file": "stacks/angular.csv"},91"laravel": {"file": "stacks/laravel.csv"},92}9394# Common columns for all stacks95_STACK_COLS = {96"search_cols": ["Category", "Guideline", "Description", "Do", "Don't"],97"output_cols": ["Category", "Guideline", "Description", "Do", "Don't", "Code Good", "Code Bad", "Severity", "Docs URL"]98}99100AVAILABLE_STACKS = list(STACK_CONFIG.keys())101102103# ============ BM25 IMPLEMENTATION ============104class BM25:105"""BM25 ranking algorithm for text search"""106107def __init__(self, k1=1.5, b=0.75):108self.k1 = k1109self.b = b110self.corpus = []111self.doc_lengths = []112self.avgdl = 0113self.idf = {}114self.doc_freqs = defaultdict(int)115self.N = 0116117def tokenize(self, text):118"""Lowercase, split, remove punctuation, filter short words"""119text = re.sub(r'[^\w\s]', ' ', str(text).lower())120return [w for w in text.split() if len(w) > 2]121122def fit(self, documents):123"""Build BM25 index from documents"""124self.corpus = [self.tokenize(doc) for doc in documents]125self.N = len(self.corpus)126if self.N == 0:127return128self.doc_lengths = [len(doc) for doc in self.corpus]129self.avgdl = sum(self.doc_lengths) / self.N130131for doc in self.corpus:132seen = set()133for word in doc:134if word not in seen:135self.doc_freqs[word] += 1136seen.add(word)137138for word, freq in self.doc_freqs.items():139self.idf[word] = log((self.N - freq + 0.5) / (freq + 0.5) + 1)140141def score(self, query):142"""Score all documents against query"""143query_tokens = self.tokenize(query)144scores = []145146for idx, doc in enumerate(self.corpus):147score = 0148doc_len = self.doc_lengths[idx]149term_freqs = defaultdict(int)150for word in doc:151term_freqs[word] += 1152153for token in query_tokens:154if token in self.idf:155tf = term_freqs[token]156idf = self.idf[token]157numerator = tf * (self.k1 + 1)158denominator = tf + self.k1 * (1 - self.b + self.b * doc_len / self.avgdl)159score += idf * numerator / denominator160161scores.append((idx, score))162163return sorted(scores, key=lambda x: x[1], reverse=True)164165166# ============ SEARCH FUNCTIONS ============167def _load_csv(filepath):168"""Load CSV and return list of dicts"""169with open(filepath, 'r', encoding='utf-8') as f:170return list(csv.DictReader(f))171172173def _search_csv(filepath, search_cols, output_cols, query, max_results):174"""Core search function using BM25"""175if not filepath.exists():176return []177178data = _load_csv(filepath)179180# Build documents from search columns181documents = [" ".join(str(row.get(col, "")) for col in search_cols) for row in data]182183# BM25 search184bm25 = BM25()185bm25.fit(documents)186ranked = bm25.score(query)187188# Get top results with score > 0189results = []190for idx, score in ranked[:max_results]:191if score > 0:192row = data[idx]193results.append({col: row.get(col, "") for col in output_cols if col in row})194195return results196197198def detect_domain(query):199"""Auto-detect the most relevant domain from query"""200query_lower = query.lower()201202domain_keywords = {203"color": ["color", "palette", "hex", "#", "rgb", "token", "semantic", "accent", "destructive", "muted", "foreground"],204"chart": ["chart", "graph", "visualization", "trend", "bar", "pie", "scatter", "heatmap", "funnel"],205"landing": ["landing", "page", "cta", "conversion", "hero", "testimonial", "pricing", "section"],206"product": ["saas", "ecommerce", "e-commerce", "fintech", "healthcare", "gaming", "portfolio", "crypto", "dashboard", "fitness", "restaurant", "hotel", "travel", "music", "education", "learning", "legal", "insurance", "medical", "beauty", "pharmacy", "dental", "pet", "dating", "wedding", "recipe", "delivery", "ride", "booking", "calendar", "timer", "tracker", "diary", "note", "chat", "messenger", "crm", "invoice", "parking", "transit", "vpn", "alarm", "weather", "sleep", "meditation", "fasting", "habit", "grocery", "meme", "wardrobe", "plant care", "reading", "flashcard", "puzzle", "trivia", "arcade", "photography", "streaming", "podcast", "newsletter", "marketplace", "freelancer", "coworking", "airline", "museum", "theater", "church", "non-profit", "charity", "kindergarten", "daycare", "senior care", "veterinary", "florist", "bakery", "brewery", "construction", "automotive", "real estate", "logistics", "agriculture", "coding bootcamp"],207"style": ["style", "design", "ui", "minimalism", "glassmorphism", "neumorphism", "brutalism", "dark mode", "flat", "aurora", "prompt", "css", "implementation", "variable", "checklist", "tailwind"],208"ux": ["ux", "usability", "accessibility", "wcag", "touch", "scroll", "animation", "keyboard", "navigation", "mobile"],209"typography": ["font pairing", "typography pairing", "heading font", "body font"],210"google-fonts": ["google font", "font family", "font weight", "font style", "variable font", "noto", "font for", "find font", "font subset", "font language", "monospace font", "serif font", "sans serif font", "display font", "handwriting font", "font", "typography", "serif", "sans"],211"icons": ["icon", "icons", "lucide", "heroicons", "symbol", "glyph", "pictogram", "svg icon"],212"react": ["react", "next.js", "nextjs", "suspense", "memo", "usecallback", "useeffect", "rerender", "bundle", "waterfall", "barrel", "dynamic import", "rsc", "server component"],213"web": ["aria", "focus", "outline", "semantic", "virtualize", "autocomplete", "form", "input type", "preconnect"]214}215216scores = {domain: sum(1 for kw in keywords if re.search(r'\b' + re.escape(kw) + r'\b', query_lower)) for domain, keywords in domain_keywords.items()}217best = max(scores, key=scores.get)218return best if scores[best] > 0 else "style"219220221def search(query, domain=None, max_results=MAX_RESULTS):222"""Main search function with auto-domain detection"""223if domain is None:224domain = detect_domain(query)225226config = CSV_CONFIG.get(domain, CSV_CONFIG["style"])227filepath = DATA_DIR / config["file"]228229if not filepath.exists():230return {"error": f"File not found: {filepath}", "domain": domain}231232results = _search_csv(filepath, config["search_cols"], config["output_cols"], query, max_results)233234return {235"domain": domain,236"query": query,237"file": config["file"],238"count": len(results),239"results": results240}241242243def search_stack(query, stack, max_results=MAX_RESULTS):244"""Search stack-specific guidelines"""245if stack not in STACK_CONFIG:246return {"error": f"Unknown stack: {stack}. Available: {', '.join(AVAILABLE_STACKS)}"}247248filepath = DATA_DIR / STACK_CONFIG[stack]["file"]249250if not filepath.exists():251return {"error": f"Stack file not found: {filepath}", "stack": stack}252253results = _search_csv(filepath, _STACK_COLS["search_cols"], _STACK_COLS["output_cols"], query, max_results)254255return {256"domain": "stack",257"stack": stack,258"query": query,259"file": STACK_CONFIG[stack]["file"],260"count": len(results),261"results": results262}263