UNPKG

lightswind

Version:

A collection of beautifully crafted React Components, Blocks & Templates built with Tailwind CSS. Create stunning web applications effortlessly by using our 100+ professional and animated react components.

1,039 lines (903 loc) 31.6 kB
#!/usr/bin/env node const fs = require("fs-extra"); const path = require("path"); const { execSync } = require("child_process"); const readline = require("readline"); // --- Configuration --- const COMPONENT_DEPS_MAP = require("./component-deps.json"); const PACKAGE_ROOT = path.join(__dirname, ".."); const DIST_COMPONENTS_DIR = path.join(PACKAGE_ROOT, "dist", "components"); // Source paths from the package const ALL_UI_FROM = path.join(DIST_COMPONENTS_DIR, "ui"); const LIB_FROM = path.join(DIST_COMPONENTS_DIR, "lib"); const HOOKS_FROM = path.join(DIST_COMPONENTS_DIR, "hooks"); const STYLES_FROM = path.join(DIST_COMPONENTS_DIR, "styles", "lightswind.css"); // User's current working directory const USER_CWD = process.cwd(); // --- Helper Functions --- /** * Read user's package.json */ function readUserPackageJson() { const pkgPath = path.join(USER_CWD, "package.json"); if (!fs.existsSync(pkgPath)) { console.error("❌ No package.json found in current directory"); console.error("Please run this command from your project root."); process.exit(1); } return fs.readJsonSync(pkgPath); } /** * Detect the framework being used */ function detectFramework(packageJson) { const deps = { ...(packageJson.dependencies || {}), ...(packageJson.devDependencies || {}) }; if (deps['next']) return { name: 'Next.js', type: 'nextjs' }; if (deps['vite']) return { name: 'Vite', type: 'vite' }; if (deps['react-scripts']) return { name: 'Create React App', type: 'cra' }; return { name: 'React', type: 'react' }; } /** * Detect the best components directory for this project */ function detectComponentsPath() { // Check existing directories first const possiblePaths = [ path.join(USER_CWD, 'src', 'components'), path.join(USER_CWD, 'components'), path.join(USER_CWD, 'app', 'components'), ]; // Return first existing path for (const p of possiblePaths) { if (fs.existsSync(p)) { return { components: p, lib: path.join(path.dirname(p), 'lib'), hooks: path.join(path.dirname(p), 'hooks'), styles: path.join(path.dirname(p), 'lightswind.css'), framework: null // Will be set by caller }; } } // No existing components directory, choose based on framework const packageJson = readUserPackageJson(); const framework = detectFramework(packageJson); let basePath; if (framework.type === 'nextjs') { // Check if Next.js uses src/ directory const hasSrc = fs.existsSync(path.join(USER_CWD, 'src')); basePath = hasSrc ? path.join(USER_CWD, 'src', 'components') : path.join(USER_CWD, 'components'); } else { // Vite, CRA, generic React - use src/components basePath = path.join(USER_CWD, 'src', 'components'); } return { components: basePath, lib: path.join(path.dirname(basePath), 'lib'), hooks: path.join(path.dirname(basePath), 'hooks'), styles: path.join(path.dirname(basePath), 'lightswind.css'), framework: framework }; } /** * Get all destination paths for the current project */ function getPaths() { const detected = detectComponentsPath(); const packageJson = readUserPackageJson(); const framework = detected.framework || detectFramework(packageJson); return { ALL_UI_TO: path.join(detected.components, 'lightswind'), LIB_TO: detected.lib, HOOKS_TO: detected.hooks, STYLES_TO: detected.styles, COMPONENTS_DIR: detected.components, FRAMEWORK: framework }; } /** * Get missing dependencies */ function getMissingDependencies(required, userPkg) { const installed = { ...(userPkg.dependencies || {}), ...(userPkg.devDependencies || {}) }; return required.filter(dep => !installed[dep]); } /** * Prompt user for yes/no input */ function promptUser(question) { return new Promise((resolve) => { const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); rl.question(question, (answer) => { rl.close(); const normalized = answer.toLowerCase().trim(); resolve(normalized === 'y' || normalized === 'yes' || normalized === ''); }); }); } /** * Install npm dependencies */ function installDependencies(deps) { if (deps.length === 0) return; console.log(`\n⏳ Installing ${deps.join(", ")}...`); const cmd = `npm install ${deps.join(" ")}`; try { execSync(cmd, { stdio: "inherit", cwd: USER_CWD }); console.log("✅ Dependencies installed successfully\n"); } catch (error) { console.error("❌ Failed to install dependencies"); console.error("You can install them manually:", deps.join(" ")); } } /** * Copy shared utilities (lib, hooks, styles) */ function copySharedUtils() { const paths = getPaths(); let utilsInstalled = false; // Copy lib folder if (fs.existsSync(LIB_FROM)) { fs.ensureDirSync(paths.LIB_TO); fs.copySync(LIB_FROM, paths.LIB_TO, { overwrite: true }); utilsInstalled = true; } // Copy hooks folder if (fs.existsSync(HOOKS_FROM)) { fs.ensureDirSync(paths.HOOKS_TO); fs.copySync(HOOKS_FROM, paths.HOOKS_TO, { overwrite: true }); utilsInstalled = true; } // Copy styles file if (fs.existsSync(STYLES_FROM)) { fs.ensureDirSync(path.dirname(paths.STYLES_TO)); fs.copySync(STYLES_FROM, paths.STYLES_TO, { overwrite: true }); utilsInstalled = true; } if (utilsInstalled) { console.log("✅ Installed shared utilities (lib, hooks, styles)"); } } /** * Copy a single component */ function copyComponent(componentName) { const paths = getPaths(); const fileName = `${componentName}.tsx`; const fromPath = path.join(ALL_UI_FROM, fileName); const toPath = path.join(paths.ALL_UI_TO, fileName); if (!fs.existsSync(fromPath)) { return false; } fs.ensureDirSync(path.dirname(toPath)); fs.copySync(fromPath, toPath, { overwrite: true }); return true; } /** * Detect Tailwind CSS version */ function detectTailwindVersion() { try { const userPkg = readUserPackageJson(); const deps = { ...(userPkg.dependencies || {}), ...(userPkg.devDependencies || {}) }; const tailwindVersion = deps['tailwindcss']; if (!tailwindVersion) { return null; } // Extract major version number // Match first digit(s), handling ^4, ~3.0.0, 4.0.0 etc const versionMatch = tailwindVersion.match(/(\d+)/); if (versionMatch) { return parseInt(versionMatch[1]); } return null; } catch (error) { return null; } } /** * Find Tailwind config file */ function findTailwindConfig() { const possibleConfigs = [ 'tailwind.config.js', 'tailwind.config.ts', 'tailwind.config.mjs', 'tailwind.config.cjs' ]; for (const config of possibleConfigs) { const configPath = path.join(USER_CWD, config); if (fs.existsSync(configPath)) { return configPath; } } return null; } /** * Find main CSS file (for Tailwind v4) */ function findMainCSSFile() { const possiblePaths = [ 'src/app/globals.css', 'src/globals.css', 'app/globals.css', 'src/styles/globals.css', 'src/index.css', 'src/App.css', 'styles/globals.css' ]; for (const cssPath of possiblePaths) { const fullPath = path.join(USER_CWD, cssPath); if (fs.existsSync(fullPath)) { return fullPath; } } // Fallback: Search for any CSS file in src/ that might be the main one // We prioritize files that likely contain Tailwind directives const srcDir = path.join(USER_CWD, 'src'); if (fs.existsSync(srcDir)) { try { const files = fs.readdirSync(srcDir); // First pass: look for index.css, style.css, main.css const priorityNames = ['index.css', 'style.css', 'main.css', 'app.css']; for (const file of files) { if (priorityNames.includes(file.toLowerCase())) { return path.join(srcDir, file); } } // Second pass: return the first CSS file found for (const file of files) { if (file.endsWith('.css')) { return path.join(srcDir, file); } } } catch (e) { // Ignore errors accessing src directory } } return null; } /** * Configure Tailwind plugin for v3 (tailwind.config.js) */ function configureTailwindV3Plugin() { const configPath = findTailwindConfig(); if (!configPath) { console.log("\n⚠️ Tailwind config not found"); console.log("💡 Add Lightswind plugin manually to tailwind.config.js:"); console.log(" plugins: [require('lightswind/plugin')]"); return false; } try { let content = fs.readFileSync(configPath, 'utf-8'); // Check if already configured if (content.includes("lightswind/plugin")) { console.log("✅ Lightswind plugin already in tailwind.config"); return true; } // Add plugin to config if (content.includes('plugins:')) { // Plugins array exists, add to it content = content.replace( /plugins:\s*\[/, "plugins: [\n require('lightswind/plugin')," ); } else { // No plugins array, add it content = content.replace( /module\.exports\s*=\s*{/, "module.exports = {\n plugins: [require('lightswind/plugin')]," ); } fs.writeFileSync(configPath, content, 'utf-8'); console.log("✅ Added Lightswind plugin to tailwind.config"); return true; } catch (error) { console.log("\n⚠️ Could not auto-configure Tailwind plugin"); console.log("💡 Add manually to tailwind.config.js:"); console.log(" plugins: [require('lightswind/plugin')]"); return false; } } /** * Configure Tailwind plugin for v4 (CSS file) */ function configureTailwindV4Plugin() { const cssPath = findMainCSSFile(); if (!cssPath) { console.log("\n⚠️ Main CSS file not found"); console.log("💡 Add Lightswind plugin manually to your CSS file:"); console.log(" @import 'tailwindcss';"); console.log(" @plugin 'lightswind/plugin';"); return false; } try { let content = fs.readFileSync(cssPath, 'utf-8'); // Check if already configured if (content.includes("lightswind/plugin")) { console.log("✅ Lightswind plugin already in CSS"); return true; } // Add @plugin after @import 'tailwindcss' if (content.includes("@import 'tailwindcss'") || content.includes('@import "tailwindcss"')) { content = content.replace( /@import ['"]tailwindcss['"];?\s*/, "@import 'tailwindcss';\n@plugin 'lightswind/plugin';\n" ); } else { // Tailwind not imported yet, add both content = "@import 'tailwindcss';\n@plugin 'lightswind/plugin';\n\n" + content; } fs.writeFileSync(cssPath, content, 'utf-8'); console.log(`✅ Added Lightswind plugin to ${path.basename(cssPath)}`); return true; } catch (error) { console.log("\n⚠️ Could not auto-configure Tailwind plugin"); console.log("💡 Add manually to your main CSS file:"); console.log(" @plugin 'lightswind/plugin';"); return false; } } /** * Auto-configure Tailwind plugin based on version */ function autoConfigureTailwindPlugin() { const tailwindVersion = detectTailwindVersion(); if (!tailwindVersion) { console.log("\n⚠️ Tailwind CSS not found in package.json"); console.log("💡 Install Tailwind CSS first:"); console.log(" npm install -D tailwindcss"); return; } console.log(`\n🔧 Configuring Lightswind for Tailwind CSS v${tailwindVersion}...`); if (tailwindVersion >= 4) { // Tailwind v4 - add to CSS file if (!configureTailwindV4Plugin()) { appendCssFallback(); } } else { // Tailwind v3 or earlier - add to config file if (!configureTailwindV3Plugin()) { appendCssFallback(); } } } /** * Append Lightswind CSS content to main CSS file as fallback */ function appendCssFallback() { console.log("\n⚠️ Falling back to manual CSS injection..."); const cssPath = findMainCSSFile(); if (!cssPath) { console.error("❌ Could not find main CSS file to inject styles."); return; } const paths = getPaths(); // We can read from the installed location since we copied it there const stylesPath = paths.STYLES_TO; if (!fs.existsSync(stylesPath)) { console.error("❌ Could not find source styles file."); return; } try { const stylesContent = fs.readFileSync(stylesPath, 'utf-8'); let mainCssContent = fs.readFileSync(cssPath, 'utf-8'); if (mainCssContent.includes("/* lightswind.css */")) { console.log("✅ Lightswind styles already present in CSS"); return; } // Append styles mainCssContent += "\n\n/* lightswind.css */\n" + stylesContent; fs.writeFileSync(cssPath, mainCssContent, 'utf-8'); console.log(`✅ Appended Lightswind styles to ${path.basename(cssPath)}`); } catch (error) { console.error("❌ Failed to append styles:", error.message); } } // --- Commands --- /** * Install all components */ async function installAll() { const paths = getPaths(); console.log("\n🚀 Installing all Lightswind components..."); console.log(`📦 Detected: ${paths.FRAMEWORK.name}`); console.log(`📁 Installing to: ${paths.ALL_UI_TO}\n`); // Get all unique dependencies const allDeps = new Set(); Object.values(COMPONENT_DEPS_MAP).forEach(deps => { deps.forEach(dep => allDeps.add(dep)); }); const requiredDeps = Array.from(allDeps); const userPkg = readUserPackageJson(); const missingDeps = getMissingDependencies(requiredDeps, userPkg); // Prompt for dependencies if (missingDeps.length > 0) { console.log("📦 The following dependencies are required:"); console.log(missingDeps.map(d => ` • ${d}`).join("\n")); console.log(`\nTotal: ${missingDeps.length} packages\n`); const shouldInstall = await promptUser("Install all dependencies? (Y/n): "); if (shouldInstall) { installDependencies(missingDeps); } else { console.log("⚠️ Skipping dependency installation."); console.log("Some components may not work without their dependencies.\n"); } } // Copy all components if (fs.existsSync(ALL_UI_FROM)) { fs.ensureDirSync(paths.ALL_UI_TO); fs.copySync(ALL_UI_FROM, paths.ALL_UI_TO, { overwrite: true }); console.log("✅ Installed all components"); } else { console.error("❌ UI components source directory not found."); process.exit(1); } // Copy shared utilities copySharedUtils(); // Auto-configure Tailwind plugin autoConfigureTailwindPlugin(); console.log("\n🎉 Success! All Lightswind components installed."); console.log("\nNext steps:"); console.log(" 1. Import components: import { Button } from '@/components/lightswind/button'"); console.log(" 2. Start building! 🚀\n"); } /** * Install a specific component */ async function installComponent(componentName) { if (!componentName) { console.error("❌ Please specify a component name"); console.error("Usage: npx lightswind@latest add <component-name>"); console.error("Example: npx lightswind@latest add button\n"); process.exit(1); } const paths = getPaths(); console.log(`\n📦 Installing ${componentName}...`); console.log(`📦 Detected: ${paths.FRAMEWORK.name}`); console.log(`📁 Installing to: ${paths.ALL_UI_TO}\n`); // Check if component exists const componentFile = `${componentName}.tsx`; const sourceExists = fs.existsSync(path.join(ALL_UI_FROM, componentFile)); if (!sourceExists) { console.error(`❌ Component '${componentName}' not found.`); console.error("\nRun 'npx lightswind@latest list' to see available components.\n"); process.exit(1); } // Get required dependencies const requiredDeps = COMPONENT_DEPS_MAP[componentName] || []; // Check missing dependencies if (requiredDeps.length > 0) { const userPkg = readUserPackageJson(); const missingDeps = getMissingDependencies(requiredDeps, userPkg); if (missingDeps.length > 0) { console.log(`📦 ${componentName} requires: ${missingDeps.join(", ")}`); const shouldInstall = await promptUser("Install dependencies? (Y/n): "); if (shouldInstall) { installDependencies(missingDeps); } else { console.log("⚠️ Skipping dependency installation."); console.log("This component may not work without these dependencies.\n"); } } } // Copy component if (copyComponent(componentName)) { console.log(`✅ Installed ${componentName} component`); } // Copy shared utilities copySharedUtils(); // Auto-configure Tailwind plugin autoConfigureTailwindPlugin(); console.log(`\n🎉 Success! ${componentName} is ready to use.`); console.log(`\nImport it: import { ${toPascalCase(componentName)} } from '@/components/lightswind/${componentName}'\n`); } /** * Group components by category */ /** * Group components by category */ function groupComponentsByCategory() { // Map of components to their categories based on component-categories.ts const COMPONENT_CATEGORY_MAP = { // Utilities "toggle-theme": "utility", // Background "animated-wave": "background", "animated-bubble-particles": "background", "aurora-shader": "background", "beam-grid-background": "background", "fall-beam-background": "background", "grid-dot-backgrounds": "background", "gradient-background": "background", "hell-background": "background", "interactive-grid-background": "background", "particles-background": "background", "rays-background": "background", "reflect-background": "background", "smokey-background": "background", "shader-background": "background", "sparkle-particles": "background", "stripes-background": "background", "wave-background": "background", // Button "border-beam": "button", "confetti-button": "button", "gradient-button": "button", "ripple-button": "button", "shine-button": "button", "trial-button": "button", // Text "aurora-text-effect": "text", "scroll-reveal": "text", "shiny-text": "text", "text-scroll-marquee": "text", "typewriter-input": "text", "typing-text": "text", "video-text": "text", // 3D Elements "3d-image-ring": "3d", "3d-image-carousel": "3d", "3d-carousel": "3d", "3d-hover-gallery": "3d", "3d-marquee": "3d", "3d-model-viewer": "3d", "3d-perspective-card": "3d", "3d-scroll-trigger": "3d", "3d-slider": "3d", "beam-circle": "3d", "chain-carousel": "3d", "plasma-globe": "3d", "scroll-carousel": "3d", "sparkle-navbar": "3d", // Cursor "canvas-confetti-cursor": "cursor", "particle-orbit-effect": "cursor", "smokey-cursor": "cursor", "smooth-cursor": "cursor", // Components (Animated) "animated-notification": "components", "bento-grid": "components", "code-hover-cards": "components", "count-up": "components", "dock": "components", "drag-order-list": "components", "dynamic-navigation": "components", "electro-border": "components", "glass-folder": "components", "globe": "components", "glowing-cards": "components", "hamburger-menu-overlay": "components", "image-reveal": "components", "image-trail-effect": "components", "interactive-card": "components", "interactive-gradient-card": "components", "iphone16-pro": "components", "lens": "components", "magic-loader": "components", "morphing-navigation": "components", "orbit-card": "components", "password-strength-indicator": "components", "scroll-list": "components", "scroll-stack": "components", "scroll-timeline": "components", "seasonal-hover-cards": "components", "sliding-cards": "components", "sliding-logo-marquee": "components", "stack-list": "components", "team-carousel": "components", "terminal-card": "components", "top-loader": "components", "top-sticky-bar": "components", "trusted-users": "components", "ripple-loader": "components", "woofy-hover-image": "components", // Layout "accordion": "layout", "aspect-ratio": "layout", "resizable": "layout", "scroll-area": "layout", "separator": "layout", "tabs": "layout", // UI Elements "alert": "ui", "alert-dialog": "ui", "avatar": "ui", "badge": "ui", "button": "ui", "card": "ui", "carousel": "ui", "chart": "ui", "collapsible": "ui", "context-menu": "ui", "dialog": "ui", "drawer": "ui", "dropdown-menu": "ui", "hover-card": "ui", "popover": "ui", "progress": "ui", "sheet": "ui", "skeleton": "ui", "table": "ui", "toast": "ui", "tooltip": "ui", // Form Controls "calendar": "form", "checkbox": "form", "command": "form", "form": "form", "input": "form", "input-otp": "form", "label": "form", "radio-group": "form", "select": "form", "slider": "form", "switch": "form", "textarea": "form", "toggle": "form", "toggle-group": "form", // Navigation "breadcrumb": "navigation", "navigation-menu": "navigation", "pagination": "navigation", "sidebar": "navigation" }; const groups = { utility: { name: 'Utilities', emoji: '🛠️', components: [] }, background: { name: 'Background', emoji: '🌅', components: [] }, button: { name: 'Button', emoji: '🔘', components: [] }, text: { name: 'Text', emoji: '📝', components: [] }, '3d': { name: '3D Elements', emoji: '🧊', components: [] }, cursor: { name: 'Cursor', emoji: '🖱️', components: [] }, components: { name: 'Components', emoji: '🧩', components: [] }, layout: { name: 'Layout', emoji: '📐', components: [] }, ui: { name: 'UI Elements', emoji: '🎨', components: [] }, form: { name: 'Form Controls', emoji: '📝', components: [] }, navigation: { name: 'Navigation', emoji: '🧭', components: [] }, // Keep basic/specialized as fallbacks or aliases if needed, but primarily use the new ones basic: { name: 'Basic UI', emoji: '✨', components: [] }, charts: { name: 'Chart Components', emoji: '📊', components: [] }, specialized: { name: 'Specialized Components', emoji: '🔮', components: [] } }; Object.keys(COMPONENT_DEPS_MAP).forEach((component) => { const category = COMPONENT_CATEGORY_MAP[component]; if (category && groups[category]) { groups[category].components.push(component); } else { // Fallback logic for components not in the map const deps = COMPONENT_DEPS_MAP[component]; if (deps.length === 0) { groups.basic.components.push(component); } else if (deps.includes("recharts")) { groups.charts.components.push(component); } else { groups.specialized.components.push(component); } } }); return groups; } /** * Install all components from a specific category */ async function installCategory(categoryName) { const paths = getPaths(); const groups = groupComponentsByCategory(); // Normalize category name const normalizedCategory = categoryName.toLowerCase(); // Find matching category const category = groups[normalizedCategory]; if (!category || category.components.length === 0) { console.error(`\n❌ Category '${categoryName}' not found or empty.\n`); console.log("Available categories:"); Object.entries(groups).forEach(([key, cat]) => { if (cat.components.length > 0) { console.log(` • ${key} - ${cat.name} (${cat.components.length} components)`); } }); console.log(""); process.exit(1); } console.log(`\n${category.emoji} Installing ${category.name}...`); console.log(`📦 Detected: ${paths.FRAMEWORK.name}`); console.log(`📁 Installing to: ${paths.ALL_UI_TO}`); console.log(`📊 Total components: ${category.components.length}\n`); // Collect all unique dependencies from category const allDeps = new Set(); category.components.forEach(comp => { const deps = COMPONENT_DEPS_MAP[comp] || []; deps.forEach(dep => allDeps.add(dep)); }); const requiredDeps = Array.from(allDeps); const userPkg = readUserPackageJson(); const missingDeps = getMissingDependencies(requiredDeps, userPkg); // Prompt for dependencies if (missingDeps.length > 0) { console.log("📦 The following dependencies are required:"); console.log(missingDeps.map(d => ` • ${d}`).join("\n")); console.log(`\nTotal: ${missingDeps.length} packages\n`); const shouldInstall = await promptUser("Install dependencies? (Y/n): "); if (shouldInstall) { installDependencies(missingDeps); } else { console.log("⚠️ Skipping dependency installation."); console.log("Some components may not work without their dependencies.\n"); } } // Copy all components in category let installedCount = 0; category.components.forEach(comp => { if (copyComponent(comp)) { installedCount++; } }); // Copy shared utilities copySharedUtils(); // Auto-configure Tailwind plugin autoConfigureTailwindPlugin(); console.log(`\n✅ Installed ${installedCount} components from ${category.name}`); console.log(`\n🎉 Success! ${category.name} ready to use.`); console.log(`\nComponents installed:`); console.log(category.components.slice(0, 5).map(c => ` • ${c}`).join("\n")); if (category.components.length > 5) { console.log(` ... and ${category.components.length - 5} more\n`); } else { console.log(""); } } /** * List all available components */ function listComponents() { console.log("\n📋 Available Lightswind Components:\n"); // Group components by dependency type const groups = { noDeps: [], lucideOnly: [], framerMotion: [], threeDLibs: [], charts: [], specialized: [] }; Object.entries(COMPONENT_DEPS_MAP).forEach(([component, deps]) => { if (deps.length === 0) { groups.noDeps.push(component); } else if (deps.includes("recharts")) { groups.charts.push(component); } else if (deps.includes("@react-three/fiber") || deps.includes("@react-three/drei")) { groups.threeDLibs.push(component); } else if (deps.includes("framer-motion") && deps.length > 1) { groups.specialized.push(component); } else if (deps.includes("framer-motion")) { groups.framerMotion.push(component); } else if (deps.length === 1 && deps[0] === "lucide-react") { groups.lucideOnly.push(component); } else { groups.specialized.push(component); } }); // Display groups if (groups.noDeps.length > 0) { console.log("✨ Basic UI (no external dependencies):"); groups.noDeps.forEach(c => console.log(` • ${c}`)); console.log(""); } if (groups.lucideOnly.length > 0) { console.log("🎯 UI Components (requires lucide-react only):"); groups.lucideOnly.forEach(c => console.log(` • ${c}`)); console.log(""); } if (groups.framerMotion.length > 0) { console.log("🎨 Animated Components (requires framer-motion):"); groups.framerMotion.forEach(c => console.log(` • ${c}`)); console.log(""); } if (groups.threeDLibs.length > 0) { console.log("🌐 3D Components (requires @react-three/fiber, @react-three/drei):"); groups.threeDLibs.forEach(c => console.log(` • ${c}`)); console.log(""); } if (groups.charts.length > 0) { console.log("📊 Chart Components (requires recharts):"); groups.charts.forEach(c => console.log(` • ${c}`)); console.log(""); } if (groups.specialized.length > 0) { console.log("🔮 Specialized Components (various dependencies):"); groups.specialized.forEach(c => console.log(` • ${c}`)); console.log(""); } const total = Object.values(groups).reduce((sum, arr) => sum + arr.length, 0); console.log(`Total: ${total} components available\n`); console.log("Usage:"); console.log(" npx lightswind@latest add <component-name>"); console.log(" npx lightswind@latest init (install all)\n"); } /** * Show help message */ function showHelp() { console.log("\n⚡ Lightswind UI CLI\n"); console.log("Usage:"); console.log(" npx lightswind@latest <command> [options]\n"); console.log("Commands:"); console.log(" init Install all components and utilities"); console.log(" add <component-name> Install a specific component"); console.log(" add --category <name> Install all components from category"); console.log(" list Show all available components\n"); console.log("Categories:"); console.log(" basic, ui, animated, 3d, charts, specialized\n"); console.log("Examples:"); console.log(" npx lightswind@latest init"); console.log(" npx lightswind@latest add button"); console.log(" npx lightswind@latest add globe"); console.log(" npx lightswind@latest add --category animated"); console.log(" npx lightswind@latest add --category 3d"); console.log(" npx lightswind@latest list\n"); console.log("Learn more: https://lightswind.com\n"); } /** * Convert kebab-case to PascalCase */ function toPascalCase(str) { return str .split('-') .map(word => word.charAt(0).toUpperCase() + word.slice(1)) .join(''); } // --- Main --- const command = process.argv[2]; const arg = process.argv[3]; (async () => { switch (command) { case "init": await installAll(); break; case "add": // Check if it's a category installation if (arg === "--category" || arg === "-c") { const categoryName = process.argv[4]; if (!categoryName) { console.error("\n❌ Please specify a category name"); console.error("Usage: npx lightswind@latest add --category <name>"); console.error("\nAvailable categories: basic, ui, animated, 3d, charts, specialized\n"); process.exit(1); } await installCategory(categoryName); } else { await installComponent(arg); } break; case "list": listComponents(); break; case "--help": case "-h": case "help": showHelp(); break; default: if (!command) { showHelp(); } else { console.error(`\n❌ Unknown command: ${command}\n`); showHelp(); } process.exit(1); } })();