smoothui-cli
Advanced tools
+1
-1
| #!/usr/bin/env node | ||
| import{confirm as De,isCancel as se,select as Fe,spinner as Ue}from"@clack/prompts";var _="https://smoothui.dev",M={"Basic UI":["accordion","animated-input","animated-progress-bar","animated-tabs","animated-toggle","basic-dropdown","basic-modal","basic-toast","notification-badge","searchable-dropdown","skeleton-loader","tweet-card"],Buttons:["button-copy","clip-corners-button","dot-morph-button","magnetic-button"],"Text Effects":["wave-text","reveal-text","typewriter-text","scramble-hover","scroll-reveal-paragraph"],AI:["ai-branch","ai-input"],"Cards & Layouts":["expandable-cards","glow-hover-card","scrollable-card-stack","switchboard-card","phototab","job-listing-component"],"Loaders & Effects":["grid-loader","siri-orb","cursor-follow","github-stars-animation"],Interactive:["animated-o-t-p-input","animated-tags","app-download-stack","apple-invites","contribution-graph","dynamic-island","figma-comment","image-metadata-preview","infinite-slider","interactive-image-selector","number-flow","power-off-slide","price-flow","rich-popover","reviews-carousel","social-selector","user-account-avatar"]},K=["src/components/ui","components/ui","src/components","components","app/components/ui"],Y=[{file:"bun.lockb",cmd:"bun"},{file:"pnpm-lock.yaml",cmd:"pnpm"},{file:"yarn.lock",cmd:"yarn"},{file:"package-lock.json",cmd:"npm"}],m={active:"\u25C6",done:"\u25C7",selected:"\u25CF",unselected:"\u25CB",cursor:"\u276F",success:"\u2713",error:"\u2717",bar:"\u2502"};import{createInterface as de,emitKeypressEvents as me}from"readline";import d from"picocolors";var fe=t=>{let e=new Map;for(let o of t){let n=o.category||"Other";e.has(n)||e.set(n,[]),e.get(n)?.push(o)}return e},ge=(t,e)=>{console.log(`${d.cyan(m.active)} ${t}`),console.log(`${d.dim(m.bar)}`),console.log(`${d.dim(m.bar)} Search: ${e}${d.dim("\u258C")}`),console.log(`${d.dim(m.bar)}`)},ue=(t,e,o)=>{let n=e?d.green(m.selected):d.dim(m.unselected),s=o?d.cyan(m.cursor):" ",r=o?d.cyan(t.label):t.label;console.log(`${d.dim(m.bar)} ${s} ${n} ${r}`)},he=t=>{console.log(`${d.dim(m.bar)}`),console.log(`${d.dim(m.bar)} ${d.dim(`${t} selected \u2502 \u2191\u2193 navigate \u2502 space select \u2502 enter confirm \u2502 esc cancel`)}`)},ye=(t,e,o)=>{let n=0,s=e.scrollOffset,r=e.scrollOffset+o;for(let[i,c]of t){if(n>=r)break;n>=s&&console.log(`${d.dim(m.bar)} ${d.bold(d.dim(i))}`),n++;for(let p of c){if(n>=r)break;if(n>=s){let a=e.selected.has(p.value),l=n-1===e.cursorIndex;ue(p,a,l)}n++}}},be=(t,e,o,n)=>()=>{let s=B(e,o.searchQuery);process.stdout.write("\x1B[2J\x1B[H"),ge(t,o.searchQuery);let r=fe(s);ye(r,o,n),he(o.selected.size)},B=(t,e)=>{if(!e)return t;let o=e.toLowerCase();return t.filter(n=>n.label.toLowerCase().includes(o)||n.category?.toLowerCase().includes(o))},xe=(t,e,o,n)=>{if(o<=0){e.cursorIndex=0,e.scrollOffset=0;return}t.name==="up"?(e.cursorIndex=Math.max(0,e.cursorIndex-1),e.cursorIndex<e.scrollOffset&&(e.scrollOffset=e.cursorIndex)):t.name==="down"&&(e.cursorIndex=Math.min(o-1,e.cursorIndex+1),e.cursorIndex>=e.scrollOffset+n&&(e.scrollOffset=e.cursorIndex-n+1))},ve=(t,e)=>{let o=e[t.cursorIndex];o&&(t.selected.has(o.value)?t.selected.delete(o.value):t.selected.add(o.value))},we=(t,e,o)=>{e.name==="backspace"?(o.searchQuery=o.searchQuery.slice(0,-1),o.cursorIndex=0,o.scrollOffset=0):t&&t.length===1&&!e.ctrl&&!e.meta&&(o.searchQuery+=t,o.cursorIndex=0,o.scrollOffset=0)},Q=(t,e)=>{e&&process.stdin.removeListener("keypress",e),process.stdin.isTTY&&process.stdin.setRawMode(!1),t.close()},H=t=>{let{message:e,items:o,maxVisible:n=10}=t;return new Promise(s=>{let r={selected:new Set,searchQuery:"",cursorIndex:0,scrollOffset:0},i=de({input:process.stdin,output:process.stdout});process.stdin.isTTY&&process.stdin.setRawMode(!0),me(process.stdin,i);let c=be(e,o,r,n),p=(a,l)=>{let x=B(o,r.searchQuery),v=x.length;if(l.name==="escape"||l.ctrl&&l.name==="c"){Q(i,p),s(null);return}if(l.name==="return"){Q(i,p),s([...r.selected]);return}l.name==="up"||l.name==="down"?xe(l,r,v,n):l.name==="space"?ve(r,x):we(a,l,r),c()};process.stdin.on("keypress",p),c()})};import w from"picocolors";var C=m,P=w.dim,Se=w.cyan,$e=w.green,Ie=w.red,We=w.yellow,N=w.bold,D=w.gray;function T(t,e){console.log(`${t} ${e}`)}function R(t){T(Se(C.active),t)}function b(t){T(P(C.done),t)}function J(t){T($e(C.success),t)}function j(t){T(Ie(C.error),t)}function f(t=""){console.log(`${P(C.bar)} ${t}`)}function I(){console.log(),R(N("SmoothUI")),console.log()}import{existsSync as E,readFileSync as W}from"fs";import{join as A}from"path";var Oe=/^(npm|pnpm|yarn|bun)@/,Ce=()=>{let t=process.cwd();for(let{file:o,cmd:n}of Y)if(E(A(t,o)))return n;let e=A(t,"package.json");if(E(e))try{let o=JSON.parse(W(e,"utf-8"));if(o.packageManager){let n=o.packageManager.match(Oe);if(n)return n[1]}}catch{}return"npm"},Pe=()=>{let t=process.cwd();for(let e of K)if(E(A(t,e)))return e;return null},Re=()=>{let t=process.cwd(),e=["tsconfig.json","jsconfig.json"];for(let o of e){let n=A(t,o);if(E(n))try{let r=W(n,"utf-8").replace(/\/\*[\s\S]*?\*\/|\/\/.*/g,""),c=JSON.parse(r).compilerOptions?.paths;if(c){for(let[p,a]of Object.entries(c))if(p.endsWith("/*")&&Array.isArray(a))return p.slice(0,-2)}}catch{}}return"@"},q=()=>({componentPath:Pe()||"components/ui",alias:Re(),packageManager:Ce()});import{spawnSync as z}from"child_process";import{existsSync as V,mkdirSync as je,readFileSync as ot,writeFileSync as ke}from"fs";import{dirname as Me,isAbsolute as Te,relative as Ee,resolve as X}from"path";var Ae=(t,e)=>e==="@"?t:t.replace(/@\/components\//g,`${e}/components/`);var Z=async(t,e,o,n)=>{let s=[],r=[],i=o,c=X(process.cwd(),e.componentPath);for(let p of t.files){let a=p.target||p.path;a.startsWith("components/")&&(a=a.slice(11));let l=X(c,a),x=Ee(c,l);if(x.startsWith("..")||Te(x))throw new Error(`Invalid registry file path: ${a}`);let v=Me(l);if(V(v)||je(v,{recursive:!0}),V(l)&&!i){let g=await n(a);if(g==="skip"){r.push(a);continue}g==="all"&&(i=!0)}let k=Ae(p.content,e.alias);ke(l,k,"utf-8"),s.push(a)}return{written:s,skipped:r}},ee=(t,e,o)=>{let n=!0;if(t.length>0){let s={npm:["npm","install",...t],pnpm:["pnpm","add",...t],yarn:["yarn","add",...t],bun:["bun","add",...t]},[r,...i]=s[o];z(r,i,{stdio:"pipe"}).status!==0&&(n=!1)}if(e.length>0){let s={npm:["npm","install","-D",...e],pnpm:["pnpm","add","-D",...e],yarn:["yarn","add","-D",...e],bun:["bun","add","-d",...e]},[r,...i]=s[o];z(r,i,{stdio:"pipe"}).status!==0&&(n=!1)}return n};var te=process.env.SMOOTHUI_REGISTRY_URL||_,Le=/\/r\/([^/]+)\.json$/;async function Ne(){let t=`${te}/r/registry.json`,e=await fetch(t);if(!e.ok)throw new Error(`Failed to fetch registry: ${e.status}`);return e.json()}async function oe(t){let e=`${te}/r/${t}.json`,o=await fetch(e);if(!o.ok)throw new Error(`Component "${t}" not found`);return o.json()}function ne(t){let e=t.match(Le);return e?e[1]:t}async function L(){return(await Ne()).items.map(e=>e.name)}async function F(t,e=new Set){let o=await oe(t);e.add(t);let n=[];if(o.registryDependencies)for(let s of o.registryDependencies){let r=ne(s);if(e.has(r)||!s.includes("smoothui.dev"))continue;let i=await F(r,e);n.push(i)}return{component:o,children:n}}function U(t){let e=[t.component];for(let o of t.children)e.push(...U(o));return e}function re(t){let e=new Set,o=new Set,n=new Set(["react","react-dom","next","tailwindcss","@types/react","@types/react-dom","@types/node","typescript"]);for(let s of t){for(let r of s.dependencies||[])n.has(r)||e.add(r);for(let r of s.devDependencies||[])n.has(r)||o.add(r)}return{dependencies:[...e],devDependencies:[...o]}}function G(t,e="",o=!0){f(`${e}${o?"\u2514\u2500":"\u251C\u2500"} ${t.component.name}`);let s=e+(o?" ":"\u2502 "),r=[...t.component.dependencies||[]].filter(i=>!["react","react-dom","next"].includes(i));r.length>0&&t.children.length===0&&f(`${s}\u2514\u2500 ${D(`npm: ${r.join(", ")}`)}`);for(let i=0;i<t.children.length;i++){let c=t.children[i],p=i===t.children.length-1&&r.length===0;G(c,s,p)}r.length>0&&t.children.length>0&&f(`${s}\u2514\u2500 ${D(`npm: ${r.join(", ")}`)}`)}async function ie(t,e){I();let o=q();e.path&&(o.componentPath=e.path),b(`Detected: ${o.componentPath}/ (${o.packageManager})`),console.log();let n=t;if(n.length===0){let h=(await L()).map(u=>{let $="Other";for(let[le,pe]of Object.entries(M))if(pe.includes(u)){$=le;break}return{value:u,label:u,category:$}}),y=await H({message:"Select components to install:",items:h});if(!y||y.length===0){console.log(),b("No components selected.");return}n=y,b(`Selected: ${n.join(", ")}`),console.log()}R("Resolving dependencies..."),f();let s=[],r=new Set;for(let g of n)if(!r.has(g))try{let h=await F(g),y=U(h);for(let u of y)r.has(u.name)||(r.add(u.name),s.push(u));G(h)}catch(h){j(`Failed to resolve ${g}: ${h.message}`);return}f();let{dependencies:i,devDependencies:c}=re(s),p=[...i,...c],a=s.length,l=i.length+c.length,x=l>0?`Install ${a} component${a>1?"s":""} + ${l} npm package${l>1?"s":""}?`:`Install ${a} component${a>1?"s":""}?`,v=await De({message:x});if(se(v)||!v){b("Installation cancelled.");return}console.log();let k=e.force??!1;for(let g of s){let{written:h,skipped:y}=await Z(g,o,k,async u=>{let $=await Fe({message:`File exists: ${u}`,options:[{value:"overwrite",label:"Overwrite"},{value:"skip",label:"Skip"},{value:"all",label:"Overwrite all"}]});return se($)?"skip":($==="all"&&(k=!0),$)});for(let u of h)b(`Written: ${o.componentPath}/${u}`);for(let u of y)b(`Skipped: ${u}`)}if(i.length>0||c.length>0){let g=Ue();if(g.start(`Installing ${p.join(", ")}`),ee(i,c,o.packageManager))g.stop(`Installed: ${p.join(", ")}`);else if(g.stop("Failed to install dependencies"),i.length>0&&f(`Run manually: ${o.packageManager} add ${i.join(" ")}`),c.length>0){let y=o.packageManager==="bun"?"-d":"-D";f(`Run manually: ${o.packageManager} add ${y} ${c.join(" ")}`)}}console.log(),J(`Done! ${a} component${a>1?"s":""} installed.`)}async function ce(t){let e=await L();if(t.json){console.log(JSON.stringify(e,null,2));return}I(),R(`Available components (${e.length})`),f();let o=new Map;for(let n of e){let s="Other";for(let[i,c]of Object.entries(M))if(c.includes(n)){s=i;break}let r=o.get(s);r?r.push(n):o.set(s,[n])}for(let[n,s]of o){f(N(P(n)));for(let r of s.sort())f(` ${r}`);f()}b(`Use ${P("npx smoothui add <component>")} to install`)}var O=process.argv.slice(2),S=O[0];function ae(){I(),console.log("Usage: npx smoothui <command> [options]"),console.log(),console.log("Commands:"),console.log(" add [components...] Add components to your project"),console.log(" list List available components"),console.log(),console.log("Options:"),console.log(" --path <path> Custom component install path"),console.log(" --force Overwrite existing files without asking"),console.log(" --json Output as JSON (list command)"),console.log(" --help Show this help message"),console.log(),console.log("Examples:"),console.log(" npx smoothui add siri-orb"),console.log(" npx smoothui add siri-orb grid-loader"),console.log(" npx smoothui add # Interactive mode"),console.log(" npx smoothui list")}async function Ge(){try{if(!S||S==="--help"||S==="-h"){ae();return}if(S==="add"){let t=[],e,o=!1;for(let n=1;n<O.length;n++){let s=O[n];s==="--path"&&O[n+1]?(e=O[n+1],n++):s==="--force"||s==="-f"?o=!0:s.startsWith("-")||t.push(s)}await ie(t,{path:e,force:o});return}if(S==="list"||S==="ls"){let t=O.includes("--json");await ce({json:t});return}j(`Unknown command: ${S}`),ae(),process.exit(1)}catch(t){let e=t instanceof Error?t.message:String(t);j(e||"An unknown error occurred"),process.exit(1)}}Ge(); | ||
| import{confirm as Le,isCancel as ae,select as Ne,spinner as De}from"@clack/prompts";var _="https://smoothui.dev",O={"Basic UI":["accordion","animated-input","animated-progress-bar","animated-tabs","animated-toggle","basic-dropdown","basic-modal","basic-toast","notification-badge","searchable-dropdown","skeleton-loader","tweet-card"],Buttons:["button-copy","clip-corners-button","dot-morph-button","magnetic-button"],"Text Effects":["wave-text","reveal-text","typewriter-text","scramble-hover","scroll-reveal-paragraph"],AI:["ai-branch","ai-input"],"Cards & Layouts":["expandable-cards","glow-hover-card","scrollable-card-stack","switchboard-card","phototab","job-listing-component"],"Loaders & Effects":["grid-loader","siri-orb","cursor-follow","github-stars-animation"],Interactive:["animated-o-t-p-input","animated-tags","app-download-stack","apple-invites","contribution-graph","dynamic-island","figma-comment","image-metadata-preview","infinite-slider","interactive-image-selector","number-flow","power-off-slide","price-flow","rich-popover","reviews-carousel","social-selector","user-account-avatar"]},Y=["src/components/ui","components/ui","src/components","components","app/components/ui"],H=[{file:"bun.lockb",cmd:"bun"},{file:"pnpm-lock.yaml",cmd:"pnpm"},{file:"yarn.lock",cmd:"yarn"},{file:"package-lock.json",cmd:"npm"}],J={active:"\u25C6",done:"\u25C7",selected:"\u25CF",unselected:"\u25CB",cursor:"\u276F",success:"\u2713",error:"\u2717",bar:"\u2502"};import{createInterface as ge,emitKeypressEvents as fe}from"readline";import a from"picocolors";var h={active:"\u25C6",done:"\u25C7",selected:"\u25CF",unselected:"\u25CB",pointer:"\u276F",bar:"\u2502"},Q=(e,t)=>{if(!t)return e;let o=t.toLowerCase();return e.filter(s=>s.label.toLowerCase().includes(o)||s.value.toLowerCase().includes(o)||s.category?.toLowerCase().includes(o))},ue=(e,t=3)=>{let o=[...e];return o.length===0?"":o.length<=t?o.join(", "):`${o.slice(0,t).join(", ")} +${o.length-t} more`},he=(e,t,o,s)=>{let r=Q(t,o.searchQuery),n=r.length;process.stdout.write("\x1B[2J\x1B[H"),console.log(`${a.green(h.active)} ${e} ${a.dim("(space to toggle)")}`),console.log(`${a.dim(h.bar)}`);let i=o.searchQuery||a.dim("Type to search...");console.log(`${a.dim(h.bar)} ${a.cyan("Search:")} ${i}${a.inverse(" ")}`),console.log(`${a.dim(h.bar)}`);let l=Math.floor(s/2),p=Math.max(0,o.cursorIndex-l),c=Math.min(n,p+s);c-p<s&&p>0&&(p=Math.max(0,c-s)),p>0&&console.log(`${a.dim(h.bar)} ${a.dim(`\u2191 ${p} more`)}`);for(let m=p;m<c;m++){let g=r[m],f=o.selected.has(g.value),b=m===o.cursorIndex,x=b?a.cyan(h.pointer):" ",u=f?a.green(h.selected):a.dim(h.unselected),v=b?a.cyan(g.label):g.label,N=g.category?a.dim(` (${g.category})`):"";console.log(`${a.dim(h.bar)} ${x} ${u} ${v}${N}`)}let d=n-c;d>0&&console.log(`${a.dim(h.bar)} ${a.dim(`\u2193 ${d} more`)}`),n===0&&console.log(`${a.dim(h.bar)} ${a.dim("No components found")}`),console.log(`${a.dim(h.bar)}`);let $=ue(o.selected);$&&(console.log(`${a.dim(h.bar)} ${a.green("Selected:")} ${$}`),console.log(`${a.dim(h.bar)}`)),console.log(`${a.dim(h.bar)} ${a.dim("\u2191\u2193 navigate \u2022 space select \u2022 enter confirm \u2022 esc cancel")}`)},K=(e,t)=>{t&&process.stdin.removeListener("keypress",t),process.stdin.isTTY&&process.stdin.setRawMode(!1),e.close(),process.stdout.write("\x1B[?25h")},W=e=>{let{message:t,items:o,maxVisible:s=8}=e;return new Promise(r=>{let n={selected:new Set,searchQuery:"",cursorIndex:0},i=ge({input:process.stdin,output:process.stdout});process.stdin.isTTY&&(process.stdin.setRawMode(!0),process.stdout.write("\x1B[?25l")),fe(process.stdin,i);let l=()=>he(t,o,n,s),p=(c,d)=>{let $=Q(o,n.searchQuery),m=$.length;if(d.name==="escape"||d.ctrl&&d.name==="c"){K(i,p),process.stdout.write("\x1B[2J\x1B[H"),r(null);return}if(d.name==="return"){K(i,p),process.stdout.write("\x1B[2J\x1B[H"),r([...n.selected]);return}if(d.name==="up")n.cursorIndex=Math.max(0,n.cursorIndex-1);else if(d.name==="down")n.cursorIndex=Math.min(m-1,n.cursorIndex+1);else if(d.name==="space"){let g=$[n.cursorIndex];g&&(n.selected.has(g.value)?n.selected.delete(g.value):n.selected.add(g.value))}else d.name==="backspace"?(n.searchQuery=n.searchQuery.slice(0,-1),n.cursorIndex=0):c&&c.length===1&&!d.ctrl&&!d.meta&&(n.searchQuery+=c,n.cursorIndex=0);m===0?n.cursorIndex=0:n.cursorIndex>=m&&(n.cursorIndex=m-1),l()};process.stdin.on("keypress",p),l()})};import S from"picocolors";var k=J,R=S.dim,ye=S.cyan,be=S.green,xe=S.red,Ke=S.yellow,q=S.bold,D=S.gray,$e=["\x1B[38;5;252m","\x1B[38;5;249m","\x1B[38;5;246m","\x1B[38;5;243m","\x1B[38;5;240m","\x1B[38;5;237m"],we="\x1B[0m",B=["\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557","\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551","\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2554\u2588\u2588\u2588\u2588\u2554\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551","\u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2554\u255D\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551","\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u255A\u2550\u255D \u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551","\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D"],ve="1.1.0";function T(e,t){console.log(`${e} ${t}`)}function E(e){T(ye(k.active),e)}function w(e){T(R(k.done),e)}function V(e){T(be(k.success),e)}function P(e){T(xe(k.error),e)}function y(e=""){console.log(`${R(k.bar)} ${e}`)}function j(){console.log();for(let e=0;e<B.length;e++)console.log(`${$e[e]}${B[e]}${we}`);console.log(),console.log(` ${R(`v${ve}`)}`),console.log()}import{existsSync as M,readFileSync as X}from"fs";import{join as A}from"path";var Se=/^(npm|pnpm|yarn|bun)@/,Ie=()=>{let e=process.cwd();for(let{file:o,cmd:s}of H)if(M(A(e,o)))return s;let t=A(e,"package.json");if(M(t))try{let o=JSON.parse(X(t,"utf-8"));if(o.packageManager){let s=o.packageManager.match(Se);if(s)return s[1]}}catch{}return"npm"},Re=()=>{let e=process.cwd();for(let t of Y)if(M(A(e,t)))return t;return null},je=()=>{let e=process.cwd(),t=["tsconfig.json","jsconfig.json"];for(let o of t){let s=A(e,o);if(M(s))try{let n=X(s,"utf-8").replace(/\/\*[\s\S]*?\*\/|\/\/.*/g,""),l=JSON.parse(n).compilerOptions?.paths;if(l){for(let[p,c]of Object.entries(l))if(p.endsWith("/*")&&Array.isArray(c))return p.slice(0,-2)}}catch{}}return"@"},z=()=>({componentPath:Re()||"components/ui",alias:je(),packageManager:Ie()});import{spawnSync as Z}from"child_process";import{existsSync as ee,mkdirSync as Ce,readFileSync as Ze,writeFileSync as ke}from"fs";import{dirname as Pe,isAbsolute as Oe,relative as Te,resolve as te}from"path";var Ee=(e,t)=>t==="@"?e:e.replace(/@\/components\//g,`${t}/components/`);var oe=async(e,t,o,s)=>{let r=[],n=[],i=o,l=te(process.cwd(),t.componentPath);for(let p of e.files){let c=p.target||p.path;c.startsWith("components/")&&(c=c.slice(11));let d=te(l,c),$=Te(l,d);if($.startsWith("..")||Oe($))throw new Error(`Invalid registry file path: ${c}`);let m=Pe(d);if(ee(m)||Ce(m,{recursive:!0}),ee(d)&&!i){let f=await s(c);if(f==="skip"){n.push(c);continue}f==="all"&&(i=!0)}let g=Ee(p.content,t.alias);ke(d,g,"utf-8"),r.push(c)}return{written:r,skipped:n}},ne=(e,t,o)=>{let s=!0;if(e.length>0){let r={npm:["npm","install",...e],pnpm:["pnpm","add",...e],yarn:["yarn","add",...e],bun:["bun","add",...e]},[n,...i]=r[o];Z(n,i,{stdio:"pipe"}).status!==0&&(s=!1)}if(t.length>0){let r={npm:["npm","install","-D",...t],pnpm:["pnpm","add","-D",...t],yarn:["yarn","add","-D",...t],bun:["bun","add","-d",...t]},[n,...i]=r[o];Z(n,i,{stdio:"pipe"}).status!==0&&(s=!1)}return s};var se=process.env.SMOOTHUI_REGISTRY_URL||_,Me=/\/r\/([^/]+)\.json$/;async function Ae(){let e=`${se}/r/registry.json`,t=await fetch(e);if(!t.ok)throw new Error(`Failed to fetch registry: ${t.status}`);return t.json()}async function re(e){let t=`${se}/r/${e}.json`,o=await fetch(t);if(!o.ok)throw new Error(`Component "${e}" not found`);return o.json()}function ie(e){let t=e.match(Me);return t?t[1]:e}async function L(){return(await Ae()).items.map(t=>t.name)}async function F(e,t=new Set){let o=await re(e);t.add(e);let s=[];if(o.registryDependencies)for(let r of o.registryDependencies){let n=ie(r);if(t.has(n)||!r.includes("smoothui.dev"))continue;let i=await F(n,t);s.push(i)}return{component:o,children:s}}function U(e){let t=[e.component];for(let o of e.children)t.push(...U(o));return t}function ce(e){let t=new Set,o=new Set,s=new Set(["react","react-dom","next","tailwindcss","@types/react","@types/react-dom","@types/node","typescript"]);for(let r of e){for(let n of r.dependencies||[])s.has(n)||t.add(n);for(let n of r.devDependencies||[])s.has(n)||o.add(n)}return{dependencies:[...t],devDependencies:[...o]}}function G(e,t="",o=!0){y(`${t}${o?"\u2514\u2500":"\u251C\u2500"} ${e.component.name}`);let r=t+(o?" ":"\u2502 "),n=[...e.component.dependencies||[]].filter(i=>!["react","react-dom","next"].includes(i));n.length>0&&e.children.length===0&&y(`${r}\u2514\u2500 ${D(`npm: ${n.join(", ")}`)}`);for(let i=0;i<e.children.length;i++){let l=e.children[i],p=i===e.children.length-1&&n.length===0;G(l,r,p)}n.length>0&&e.children.length>0&&y(`${r}\u2514\u2500 ${D(`npm: ${n.join(", ")}`)}`)}async function le(e,t){j();let o=z();t.path&&(o.componentPath=t.path),w(`Detected: ${o.componentPath}/ (${o.packageManager})`),console.log();let s=e;if(s.length===0){let b=(await L()).map(u=>{let v="Other";for(let[N,me]of Object.entries(O))if(me.includes(u)){v=N;break}return{value:u,label:u,category:v}}),x=await W({message:"Select components to install:",items:b});if(!x||x.length===0){console.log(),w("No components selected.");return}s=x,w(`Selected: ${s.join(", ")}`),console.log()}E("Resolving dependencies..."),y();let r=[],n=new Set;for(let f of s)if(!n.has(f))try{let b=await F(f),x=U(b);for(let u of x)n.has(u.name)||(n.add(u.name),r.push(u));G(b)}catch(b){P(`Failed to resolve ${f}: ${b.message}`);return}y();let{dependencies:i,devDependencies:l}=ce(r),p=[...i,...l],c=r.length,d=i.length+l.length,$=d>0?`Install ${c} component${c>1?"s":""} + ${d} npm package${d>1?"s":""}?`:`Install ${c} component${c>1?"s":""}?`,m=await Le({message:$});if(ae(m)||!m){w("Installation cancelled.");return}console.log();let g=t.force??!1;for(let f of r){let{written:b,skipped:x}=await oe(f,o,g,async u=>{let v=await Ne({message:`File exists: ${u}`,options:[{value:"overwrite",label:"Overwrite"},{value:"skip",label:"Skip"},{value:"all",label:"Overwrite all"}]});return ae(v)?"skip":(v==="all"&&(g=!0),v)});for(let u of b)w(`Written: ${o.componentPath}/${u}`);for(let u of x)w(`Skipped: ${u}`)}if(i.length>0||l.length>0){let f=De();if(f.start(`Installing ${p.join(", ")}`),ne(i,l,o.packageManager))f.stop(`Installed: ${p.join(", ")}`);else if(f.stop("Failed to install dependencies"),i.length>0&&y(`Run manually: ${o.packageManager} add ${i.join(" ")}`),l.length>0){let x=o.packageManager==="bun"?"-d":"-D";y(`Run manually: ${o.packageManager} add ${x} ${l.join(" ")}`)}}console.log(),V(`Done! ${c} component${c>1?"s":""} installed.`)}async function pe(e){let t=await L();if(e.json){console.log(JSON.stringify(t,null,2));return}j(),E(`Available components (${t.length})`),y();let o=new Map;for(let s of t){let r="Other";for(let[i,l]of Object.entries(O))if(l.includes(s)){r=i;break}let n=o.get(r);n?n.push(s):o.set(r,[s])}for(let[s,r]of o){y(q(R(s)));for(let n of r.sort())y(` ${n}`);y()}w(`Use ${R("npx smoothui add <component>")} to install`)}var C=process.argv.slice(2),I=C[0];function de(){j(),console.log("Usage: npx smoothui <command> [options]"),console.log(),console.log("Commands:"),console.log(" add [components...] Add components to your project"),console.log(" list List available components"),console.log(),console.log("Options:"),console.log(" --path <path> Custom component install path"),console.log(" --force Overwrite existing files without asking"),console.log(" --json Output as JSON (list command)"),console.log(" --help Show this help message"),console.log(),console.log("Examples:"),console.log(" npx smoothui add siri-orb"),console.log(" npx smoothui add siri-orb grid-loader"),console.log(" npx smoothui add # Interactive mode"),console.log(" npx smoothui list")}async function Fe(){try{if(!I||I==="--help"||I==="-h"){de();return}if(I==="add"){let e=[],t,o=!1;for(let s=1;s<C.length;s++){let r=C[s];r==="--path"&&C[s+1]?(t=C[s+1],s++):r==="--force"||r==="-f"?o=!0:r.startsWith("-")||e.push(r)}await le(e,{path:t,force:o});return}if(I==="list"||I==="ls"){let e=C.includes("--json");await pe({json:e});return}P(`Unknown command: ${I}`),de(),process.exit(1)}catch(e){let t=e instanceof Error?e.message:String(e);P(t||"An unknown error occurred"),process.exit(1)}}Fe(); |
+1
-1
| { | ||
| "name": "smoothui-cli", | ||
| "version": "1.0.6", | ||
| "version": "1.1.0", | ||
| "author": "educalvolpz", | ||
@@ -5,0 +5,0 @@ "description": "CLI to install SmoothUI components - beautifully designed React components with smooth animations", |
+373
-31
@@ -1,15 +0,99 @@ | ||
| # smoothui-cli | ||
| # SmoothUI | ||
| CLI to install [SmoothUI](https://smoothui.dev) components - beautifully designed React components with smooth animations. | ||
|  | ||
| ## Installation | ||
| <div align="center"> | ||
|  | ||
|  | ||
|  | ||
| [](https://actions-badge.atrox.dev/educlopez/smoothui/goto?ref=main) | ||
|  | ||
|  | ||
|  | ||
|  | ||
| [](https://smoothui.dev) | ||
| </div> | ||
| SmoothUI is a collection of beautifully designed components with smooth animations built with React, Tailwind CSS, and Motion. This project aims to provide developers with a set of reusable UI components that enhance user experience through delightful animations and modern design patterns. | ||
| ## Table of Contents | ||
| - [SmoothUI](#smoothui) | ||
| - [Table of Contents](#table-of-contents) | ||
| - [Features](#features) | ||
| - [Quick Start](#quick-start) | ||
| - [Installation](#installation) | ||
| - [Using SmoothUI CLI](#using-smoothui-cli) | ||
| - [Using shadcn CLI](#using-shadcn-cli) | ||
| - [Manual Installation](#manual-installation) | ||
| - [Usage](#usage) | ||
| - [Basic Usage](#basic-usage) | ||
| - [Advanced Usage](#advanced-usage) | ||
| - [Available Components](#available-components) | ||
| - [UI Components](#ui-components) | ||
| - [Interactive Components](#interactive-components) | ||
| - [Layout Components](#layout-components) | ||
| - [Utility Components](#utility-components) | ||
| - [MCP Support](#mcp-support) | ||
| - [🤖 AI Assistant Integration](#-ai-assistant-integration) | ||
| - [Quick MCP Setup](#quick-mcp-setup) | ||
| - [Registry System](#registry-system) | ||
| - [Automatic Dependencies](#automatic-dependencies) | ||
| - [Component Structure](#component-structure) | ||
| - [Registry Features](#registry-features) | ||
| - [Troubleshooting](#troubleshooting) | ||
| - [Common Issues](#common-issues) | ||
| - [1. Authentication Error (401)](#1-authentication-error-401) | ||
| - [2. Registry Not Found](#2-registry-not-found) | ||
| - [3. Import Path Issues](#3-import-path-issues) | ||
| - [4. Missing Dependencies](#4-missing-dependencies) | ||
| - [Getting Help](#getting-help) | ||
| - [Contributing](#contributing) | ||
| - [Development Setup](#development-setup) | ||
| - [License](#license) | ||
| ## Features | ||
| - **Modern Design System**: A cohesive and contemporary design language with a new mascot called Smoothy | ||
| - **Smooth Animations**: Built-in animations powered by Motion for delightful user experiences | ||
| - **Responsive Design**: Fully responsive components designed with Tailwind CSS | ||
| - **Dark Mode Support**: Components support both light and dark themes out of the box | ||
| - **Color Customization**: Dynamic color switcher for easy theme customization | ||
| - **Documentation**: Comprehensive documentation with props, examples, and usage guidelines | ||
| - **Accessibility**: Enhanced accessibility features across all components | ||
| - **TypeScript Support**: Full TypeScript support with type definitions | ||
| - **Easy Integration**: Simple API for integrating components into your projects | ||
| - **CLI Tools**: Dedicated SmoothUI CLI and shadcn registry support for easy component installation | ||
| ## Quick Start | ||
| Get started with SmoothUI in just one command: | ||
| ```bash | ||
| # Using SmoothUI CLI (recommended) | ||
| npx smoothui-cli add siri-orb | ||
| # Or using shadcn CLI | ||
| npx shadcn@latest add @smoothui/siri-orb | ||
| ``` | ||
| ## Usage | ||
| Then use the component: | ||
| ### Add components | ||
| ```tsx | ||
| import { SiriOrb } from "@/components/smoothui/ui/SiriOrb"; | ||
| export default function App() { | ||
| return <SiriOrb size="200px" />; | ||
| } | ||
| ``` | ||
| ## Installation | ||
| ### Using SmoothUI CLI | ||
| The SmoothUI CLI provides an interactive way to browse and install components with automatic dependency resolution. | ||
| ```bash | ||
@@ -20,53 +104,311 @@ # Add a single component | ||
| # Add multiple components | ||
| npx smoothui-cli add siri-orb grid-loader animated-tabs | ||
| npx smoothui-cli add siri-orb rich-popover animated-input | ||
| # Interactive mode - browse and select | ||
| # Interactive mode - browse and select components | ||
| npx smoothui-cli add | ||
| # List all available components | ||
| npx smoothui-cli list | ||
| ``` | ||
| ### List components | ||
| **Features:** | ||
| - Interactive component picker with search and categories | ||
| - Auto-detects package manager (npm, pnpm, yarn, bun) | ||
| - Auto-detects component paths and tsconfig aliases | ||
| - Shows dependency tree before installation | ||
| - Handles file conflicts with overwrite prompts | ||
| - Auto-installs npm dependencies | ||
| ### Using shadcn CLI | ||
| SmoothUI is an official shadcn registry, so you can install components directly using the `@smoothui` namespace: | ||
| ```bash | ||
| # Show all available components | ||
| npx smoothui-cli list | ||
| # Install a single component | ||
| npx shadcn@latest add @smoothui/siri-orb | ||
| # JSON output | ||
| npx smoothui-cli list --json | ||
| # Install multiple components | ||
| npx shadcn@latest add @smoothui/rich-popover @smoothui/animated-input | ||
| ``` | ||
| ### Options | ||
| ### Manual Installation | ||
| If you prefer to install components manually, you can copy the component files directly: | ||
| 1. **Install dependencies**: | ||
| ```bash | ||
| # Custom install path | ||
| npx smoothui-cli add siri-orb --path src/components/ui | ||
| pnpm add motion tailwindcss lucide-react clsx tailwind-merge | ||
| ``` | ||
| # Force overwrite existing files | ||
| npx smoothui-cli add siri-orb --force | ||
| 2. **Copy component files** from the [components directory](src/components/smoothui/ui/) | ||
| 3. **Set up utilities**: | ||
| ```bash | ||
| # Create lib/utils/cn.ts | ||
| mkdir -p lib/utils | ||
| ``` | ||
| ## Features | ||
| ```tsx | ||
| // lib/utils/cn.ts | ||
| import { clsx, type ClassValue } from "clsx"; | ||
| import { twMerge } from "tailwind-merge"; | ||
| - Interactive component picker with search and categories | ||
| - Auto-detects package manager (npm, pnpm, yarn, bun) | ||
| - Auto-detects component paths and tsconfig aliases | ||
| - Shows dependency tree before installation | ||
| - Handles file conflicts with overwrite prompts | ||
| - Auto-installs npm dependencies | ||
| export function cn(...inputs: ClassValue[]) { | ||
| return twMerge(clsx(inputs)); | ||
| } | ||
| ``` | ||
| ## Alternative: shadcn CLI | ||
| ## Usage | ||
| You can also use the shadcn CLI with the `@smoothui` registry: | ||
| ### Basic Usage | ||
| ```tsx | ||
| import { SiriOrb } from "@/components/smoothui/ui/SiriOrb"; | ||
| export default function App() { | ||
| return ( | ||
| <div className="flex min-h-screen items-center justify-center"> | ||
| <SiriOrb | ||
| size="200px" | ||
| colors={{ | ||
| bg: "oklch(95% 0.02 264.695)", | ||
| c1: "oklch(75% 0.15 350)", | ||
| c2: "oklch(80% 0.12 200)", | ||
| c3: "oklch(78% 0.14 280)", | ||
| }} | ||
| animationDuration={20} | ||
| /> | ||
| </div> | ||
| ); | ||
| } | ||
| ``` | ||
| ### Advanced Usage | ||
| ```tsx | ||
| import { RichPopover } from "@/components/smoothui/ui/RichPopover"; | ||
| import { ScrollableCardStack } from "@/components/smoothui/ui/ScrollableCardStack"; | ||
| export default function Dashboard() { | ||
| const cards = [ | ||
| { | ||
| id: "1", | ||
| name: "John Doe", | ||
| handle: "@johndoe", | ||
| avatar: "/avatars/john.jpg", | ||
| video: "/videos/john.mp4", | ||
| href: "https://twitter.com/johndoe", | ||
| }, | ||
| // ... more cards | ||
| ]; | ||
| return ( | ||
| <div className="space-y-8"> | ||
| <RichPopover /> | ||
| <ScrollableCardStack items={cards} /> | ||
| </div> | ||
| ); | ||
| } | ||
| ``` | ||
| ## Available Components | ||
| SmoothUI includes a wide variety of components: | ||
| ### UI Components | ||
| - **SiriOrb** - Animated orb with smooth color transitions | ||
| - **RichPopover** - Advanced popover with rich content | ||
| - **ScrollableCardStack** - Interactive card stack with smooth scrolling | ||
| - **AnimatedInput** - Input field with smooth animations | ||
| - **DynamicIsland** - iOS-style dynamic island component | ||
| - **FluidMorph** - Fluid morphing animations | ||
| - **MatrixCard** - Matrix-style card with particle effects | ||
| ### Interactive Components | ||
| - **CursorFollow** - Custom cursor following component | ||
| - **ScrambleHover** - Text scramble effect on hover | ||
| - **WaveText** - Animated wave text effect | ||
| - **TypewriterText** - Typewriter text animation | ||
| ### Layout Components | ||
| - **ExpandableCards** - Expandable card layout | ||
| - **ScrollableCardStack** - Stack of scrollable cards | ||
| - **AppDownloadStack** - App download showcase | ||
| ### Utility Components | ||
| - **ButtonCopy** - Copy button with feedback | ||
| - **ClipCornersButton** - Button with clipped corners | ||
| - **DotMorphButton** - Button with morphing dot animation | ||
| [View all components →](https://smoothui.dev) | ||
| ## MCP Support | ||
| SmoothUI is fully compatible with the **shadcn MCP server**, enabling AI assistants to discover and install components automatically. | ||
| ### 🤖 AI Assistant Integration | ||
| With MCP support, AI assistants like **Claude**, **Cursor**, and **GitHub Copilot** can: | ||
| - **Discover Components**: Browse all available SmoothUI components | ||
| - **Install Components**: Automatically install components with dependencies | ||
| - **Provide Usage Examples**: Get code examples and integration help | ||
| - **Smart Suggestions**: Receive intelligent component recommendations | ||
| ### Quick MCP Setup | ||
| 1. **Configure your registry** in `components.json`: | ||
| ```json | ||
| { | ||
| "registries": { | ||
| "@smoothui": "https://smoothui.dev/r/{name}.json" | ||
| } | ||
| } | ||
| ``` | ||
| 2. **Install MCP server**: | ||
| ```bash | ||
| npx shadcn@latest mcp init --client claude | ||
| # or for Cursor: npx shadcn@latest mcp init --client cursor | ||
| # or for VS Code: npx shadcn@latest mcp init --client vscode | ||
| ``` | ||
| 3. **Try these prompts**: | ||
| - "Show me the components in the smoothui registry" | ||
| - "Install the SiriOrb component from smoothui" | ||
| - "Create a landing page using smoothui components" | ||
| [Learn more about MCP support →](https://smoothui.dev/doc/mcp) | ||
| ## Registry System | ||
| SmoothUI uses a custom registry system compatible with shadcn CLI v3. Each component includes: | ||
| ### Automatic Dependencies | ||
| - **Package Dependencies**: Required npm packages are automatically included | ||
| - **Utility Files**: Shared utilities like `cn` are automatically bundled | ||
| - **Import Paths**: All import paths are automatically resolved | ||
| ### Component Structure | ||
| When you install a component, you get: | ||
| ``` | ||
| components/smoothui/ui/ | ||
| ├── ComponentName.tsx # Main component file | ||
| lib/utils/ | ||
| └── cn.ts # Utility functions (if needed) | ||
| ``` | ||
| ### Registry Features | ||
| - **Self-contained**: Each component includes all necessary dependencies | ||
| - **Type-safe**: Full TypeScript support with proper types | ||
| - **Optimized**: Components are optimized for performance | ||
| - **Accessible**: Built-in accessibility features | ||
| ## Troubleshooting | ||
| ### Common Issues | ||
| #### 1. Authentication Error (401) | ||
| **Error**: `You are not authorized to access the item` | ||
| **Solution**: This usually happens with Vercel preview deployments. Use the production URL: | ||
| ```json | ||
| { | ||
| "registries": { | ||
| "@smoothui": "https://smoothui.dev/r/{name}.json" | ||
| } | ||
| } | ||
| ``` | ||
| #### 2. Registry Not Found | ||
| **Error**: `The item at https://smoothui.dev/r/registry.json was not found` | ||
| **Solution**: The search command might not work as expected. Install components directly: | ||
| ```bash | ||
| npx shadcn@latest add @smoothui/siri-orb | ||
| ``` | ||
| ## Links | ||
| #### 3. Import Path Issues | ||
| - [Documentation](https://smoothui.dev) | ||
| - [Components](https://smoothui.dev/docs/components) | ||
| - [GitHub](https://github.com/educlopez/smoothui) | ||
| **Error**: `Cannot find module '@/lib/utils/cn'` | ||
| **Solution**: Make sure your `tsconfig.json` includes the path alias: | ||
| ```json | ||
| { | ||
| "compilerOptions": { | ||
| "paths": { | ||
| "@/*": ["./src/*"] | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| #### 4. Missing Dependencies | ||
| **Error**: `Cannot find module 'clsx'` | ||
| **Solution**: Install missing dependencies: | ||
| ```bash | ||
| pnpm add clsx tailwind-merge motion | ||
| ``` | ||
| ### Getting Help | ||
| - **Documentation**: Visit [smoothui.dev](https://smoothui.dev) for detailed documentation | ||
| - **Issues**: Report bugs on [GitHub Issues](https://github.com/educlopez/smoothui/issues) | ||
| - **Discussions**: Join discussions on [GitHub Discussions](https://github.com/educlopez/smoothui/discussions) | ||
| ## Contributing | ||
| We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details. | ||
| ### Development Setup | ||
| 1. **Clone the repository**: | ||
| ```bash | ||
| git clone https://github.com/educlopez/smoothui.git | ||
| cd smoothui | ||
| ``` | ||
| 2. **Install dependencies**: | ||
| ```bash | ||
| pnpm install | ||
| ``` | ||
| 3. **Start development server**: | ||
| ```bash | ||
| pnpm dev | ||
| ``` | ||
| 4. **Build registry**: | ||
| ```bash | ||
| pnpm run build:registry | ||
| ``` | ||
| ## License | ||
| MIT | ||
| This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. |
31690
68.24%54
20%