smoothui-cli
Advanced tools
+1
-1
| #!/usr/bin/env node | ||
| import{confirm as Le,isCancel as ne,select as Ne,spinner as De}from"@clack/prompts";var _="https://smoothui.dev",k={"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"}],d={active:"\u25C6",done:"\u25C7",selected:"\u25CF",unselected:"\u25CB",cursor:"\u276F",success:"\u2713",error:"\u2717",bar:"\u2502"};import{createInterface as le,emitKeypressEvents as pe}from"readline";import p from"picocolors";var me=e=>{let t=new Map;for(let o of e){let n=o.category||"Other";t.has(n)||t.set(n,[]),t.get(n)?.push(o)}return t},de=(e,t)=>{console.log(`${p.cyan(d.active)} ${e}`),console.log(`${p.dim(d.bar)}`),console.log(`${p.dim(d.bar)} Search: ${t}${p.dim("\u258C")}`),console.log(`${p.dim(d.bar)}`)},fe=(e,t,o)=>{let n=t?p.green(d.selected):p.dim(d.unselected),s=o?p.cyan(d.cursor):" ",r=o?p.cyan(e.label):e.label;console.log(`${p.dim(d.bar)} ${s} ${n} ${r}`)},ge=e=>{console.log(`${p.dim(d.bar)}`),console.log(`${p.dim(d.bar)} ${p.dim(`${e} selected \u2502 \u2191\u2193 navigate \u2502 space select \u2502 enter confirm \u2502 esc cancel`)}`)},ue=(e,t,o)=>{let n=0,s=t.scrollOffset,r=t.scrollOffset+o;for(let[c,a]of e){if(n>=r)break;n>=s&&console.log(`${p.dim(d.bar)} ${p.bold(p.dim(c))}`),n++;for(let i of a){if(n>=r)break;if(n>=s){let m=t.selected.has(i.value),l=n-1===t.cursorIndex;fe(i,m,l)}n++}}},he=(e,t,o,n)=>()=>{let s=B(t,o.searchQuery);process.stdout.write("\x1B[2J\x1B[H"),de(e,o.searchQuery);let r=me(s);ue(r,o,n),ge(o.selected.size)},B=(e,t)=>{if(!t)return e;let o=t.toLowerCase();return e.filter(n=>n.label.toLowerCase().includes(o)||n.category?.toLowerCase().includes(o))},ye=(e,t,o,n)=>{e.name==="up"?(t.cursorIndex=Math.max(0,t.cursorIndex-1),t.cursorIndex<t.scrollOffset&&(t.scrollOffset=t.cursorIndex)):e.name==="down"&&(t.cursorIndex=Math.min(o-1,t.cursorIndex+1),t.cursorIndex>=t.scrollOffset+n&&(t.scrollOffset=t.cursorIndex-n+1))},be=(e,t)=>{let o=t[e.cursorIndex];o&&(e.selected.has(o.value)?e.selected.delete(o.value):e.selected.add(o.value))},xe=(e,t,o)=>{t.name==="backspace"?(o.searchQuery=o.searchQuery.slice(0,-1),o.cursorIndex=0,o.scrollOffset=0):e&&e.length===1&&!t.ctrl&&!t.meta&&(o.searchQuery+=e,o.cursorIndex=0,o.scrollOffset=0)},Q=e=>{process.stdin.isTTY&&process.stdin.setRawMode(!1),e.close()},H=e=>{let{message:t,items:o,maxVisible:n=10}=e;return new Promise(s=>{let r={selected:new Set,searchQuery:"",cursorIndex:0,scrollOffset:0},c=le({input:process.stdin,output:process.stdout});process.stdin.isTTY&&process.stdin.setRawMode(!0),pe(process.stdin,c);let a=he(t,o,r,n),i=(m,l)=>{let b=B(o,r.searchQuery),R=b.length;if(l.name==="escape"||l.ctrl&&l.name==="c"){Q(c),s(null);return}if(l.name==="return"){Q(c),s([...r.selected]);return}l.name==="up"||l.name==="down"?ye(l,r,R,n):l.name==="space"?be(r,b):xe(m,l,r),a()};process.stdin.on("keypress",i),a()})};import v from"picocolors";var O=d,C=v.dim,ve=v.cyan,we=v.green,Se=v.red,He=v.yellow,L=v.bold,N=v.gray;function T(e,t){console.log(`${e} ${t}`)}function j(e){T(ve(O.active),e)}function y(e){T(C(O.done),e)}function J(e){T(we(O.success),e)}function P(e){T(Se(O.error),e)}function g(e=""){console.log(`${C(O.bar)} ${e}`)}function I(){console.log(),j(L("SmoothUI")),console.log()}import{existsSync as M,readFileSync as W}from"fs";import{join as E}from"path";var Ie=/^(npm|pnpm|yarn|bun)@/,$e=()=>{let e=process.cwd();for(let{file:o,cmd:n}of Y)if(M(E(e,o)))return n;let t=E(e,"package.json");if(M(t))try{let o=JSON.parse(W(t,"utf-8"));if(o.packageManager){let n=o.packageManager.match(Ie);if(n)return n[1]}}catch{}return"npm"},Oe=()=>{let e=process.cwd();for(let t of K)if(M(E(e,t)))return t;return null},Ce=()=>{let e=process.cwd(),t=["tsconfig.json","jsconfig.json"];for(let o of t){let n=E(e,o);if(M(n))try{let r=W(n,"utf-8").replace(/\/\*[\s\S]*?\*\/|\/\/.*/g,""),a=JSON.parse(r).compilerOptions?.paths;if(a){for(let[i,m]of Object.entries(a))if(i.endsWith("/*")&&Array.isArray(m))return i.slice(0,-2)}}catch{}}return"@"},q=()=>({componentPath:Oe()||"components/ui",alias:Ce(),packageManager:$e()});import{spawnSync as je}from"child_process";import{existsSync as z,mkdirSync as Pe,readFileSync as et,writeFileSync as Re}from"fs";import{dirname as ke,join as Te}from"path";var Me=(e,t)=>t==="@"?e:e.replace(/@\/components\//g,`${t}/components/`);var V=async(e,t,o,n)=>{let s=[],r=[],c=o;for(let a of e.files){let i=Te(process.cwd(),t.componentPath,a.path),m=ke(i);if(z(m)||Pe(m,{recursive:!0}),z(i)&&!c){let b=await n(a.path);if(b==="skip"){r.push(a.path);continue}b==="all"&&(c=!0)}let l=Me(a.content,t.alias);Re(i,l,"utf-8"),s.push(a.path)}return{written:s,skipped:r}},X=(e,t)=>{if(e.length===0)return!0;let o={npm:["npm","install",...e],pnpm:["pnpm","add",...e],yarn:["yarn","add",...e],bun:["bun","add",...e]},[n,...s]=o[t];return je(n,s,{stdio:"pipe",shell:!0}).status===0};var Z=process.env.SMOOTHUI_REGISTRY_URL||_,Ee=/\/r\/([^/]+)\.json$/;async function Ae(){let e=`${Z}/r/registry.json`,t=await fetch(e);if(!t.ok)throw new Error(`Failed to fetch registry: ${t.status}`);return t.json()}async function ee(e){let t=`${Z}/r/${e}.json`,o=await fetch(t);if(!o.ok)throw new Error(`Component "${e}" not found`);return o.json()}function te(e){let t=e.match(Ee);return t?t[1]:e}async function A(){return(await Ae()).items.map(t=>t.name)}async function D(e,t=new Set){let o=await ee(e);t.add(e);let n=[];if(o.registryDependencies)for(let s of o.registryDependencies){let r=te(s);if(t.has(r)||!s.includes("smoothui.dev"))continue;let c=await D(r,t);n.push(c)}return{component:o,children:n}}function F(e){let t=[e.component];for(let o of e.children)t.push(...F(o));return t}function oe(e){let t=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 e){for(let r of s.dependencies||[])n.has(r)||t.add(r);for(let r of s.devDependencies||[])n.has(r)||o.add(r)}return{dependencies:[...t],devDependencies:[...o]}}function U(e,t="",o=!0){g(`${t}${o?"\u2514\u2500":"\u251C\u2500"} ${e.component.name}`);let s=t+(o?" ":"\u2502 "),r=[...e.component.dependencies||[]].filter(c=>!["react","react-dom","next"].includes(c));r.length>0&&e.children.length===0&&g(`${s}\u2514\u2500 ${N(`npm: ${r.join(", ")}`)}`);for(let c=0;c<e.children.length;c++){let a=e.children[c],i=c===e.children.length-1&&r.length===0;U(a,s,i)}r.length>0&&e.children.length>0&&g(`${s}\u2514\u2500 ${N(`npm: ${r.join(", ")}`)}`)}async function re(e,t){I();let o=q();t.path&&(o.componentPath=t.path),y(`Detected: ${o.componentPath}/ (${o.packageManager})`),console.log();let n=e;if(n.length===0){let h=(await A()).map(f=>{let S="Other";for(let[ie,ae]of Object.entries(k))if(ae.includes(f)){S=ie;break}return{value:f,label:f,category:S}}),x=await H({message:"Select components to install:",items:h});if(!x||x.length===0){console.log(),y("No components selected.");return}n=x,y(`Selected: ${n.join(", ")}`),console.log()}j("Resolving dependencies..."),g();let s=[],r=new Set;for(let u of n)try{let h=await D(u,r),x=F(h);for(let f of x)r.has(f.name)||(r.add(f.name),s.push(f));U(h)}catch(h){P(`Failed to resolve ${u}: ${h.message}`);return}g();let{dependencies:c,devDependencies:a}=oe(s),i=[...c,...a],m=s.length,l=i.length,b=l>0?`Install ${m} component${m>1?"s":""} + ${l} npm package${l>1?"s":""}?`:`Install ${m} component${m>1?"s":""}?`,R=await Le({message:b});if(ne(R)||!R){y("Installation cancelled.");return}console.log();let G=t.force??!1;for(let u of s){let{written:h,skipped:x}=await V(u,o,G,async f=>{let S=await Ne({message:`File exists: ${f}`,options:[{value:"overwrite",label:"Overwrite"},{value:"skip",label:"Skip"},{value:"all",label:"Overwrite all"}]});return ne(S)?"skip":(S==="all"&&(G=!0),S)});for(let f of h)y(`Written: ${o.componentPath}/${f}`);for(let f of x)y(`Skipped: ${f}`)}if(i.length>0){let u=De();u.start(`Installing ${i.join(", ")}`),X(i,o.packageManager)?u.stop(`Installed: ${i.join(", ")}`):(u.stop("Failed to install dependencies"),g(`Run manually: ${o.packageManager} add ${i.join(" ")}`))}console.log(),J(`Done! ${m} component${m>1?"s":""} installed.`)}async function se(e){let t=await A();if(e.json){console.log(JSON.stringify(t,null,2));return}I(),j(`Available components (${t.length})`),g();let o=new Map;for(let n of t){let s="Other";for(let[c,a]of Object.entries(k))if(a.includes(n)){s=c;break}let r=o.get(s);r?r.push(n):o.set(s,[n])}for(let[n,s]of o){g(L(C(n)));for(let r of s.sort())g(` ${r}`);g()}y(`Use ${C("npx smoothui add <component>")} to install`)}var $=process.argv.slice(2),w=$[0];function ce(){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 Fe(){try{if(!w||w==="--help"||w==="-h"){ce();return}if(w==="add"){let e=[],t,o=!1;for(let n=1;n<$.length;n++){let s=$[n];s==="--path"&&$[n+1]?(t=$[n+1],n++):s==="--force"||s==="-f"?o=!0:s.startsWith("-")||e.push(s)}await re(e,{path:t,force:o});return}if(w==="list"||w==="ls"){let e=$.includes("--json");await se({json:e});return}P(`Unknown command: ${w}`),ce(),process.exit(1)}catch(e){P(e.message),process.exit(1)}}Fe(); | ||
| 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 a of c){if(n>=r)break;if(n>=s){let p=e.selected.has(a.value),l=n-1===e.cursorIndex;ue(a,p,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),a=(p,l)=>{let x=B(o,r.searchQuery),S=x.length;if(l.name==="escape"||l.ctrl&&l.name==="c"){Q(i,a),s(null);return}if(l.name==="return"){Q(i,a),s([...r.selected]);return}l.name==="up"||l.name==="down"?xe(l,r,S,n):l.name==="space"?ve(r,x):we(p,l,r),c()};process.stdin.on("keypress",a),c()})};import v from"picocolors";var R=m,P=v.dim,Se=v.cyan,$e=v.green,Ie=v.red,We=v.yellow,N=v.bold,D=v.gray;function T(t,e){console.log(`${t} ${e}`)}function j(t){T(Se(R.active),t)}function b(t){T(P(R.done),t)}function J(t){T($e(R.success),t)}function k(t){T(Ie(R.error),t)}function f(t=""){console.log(`${P(R.bar)} ${t}`)}function I(){console.log(),j(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"},Re=()=>{let t=process.cwd();for(let e of K)if(E(A(t,e)))return e;return null},Pe=()=>{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[a,p]of Object.entries(c))if(a.endsWith("/*")&&Array.isArray(p))return a.slice(0,-2)}}catch{}}return"@"},q=()=>({componentPath:Re()||"components/ui",alias:Pe(),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 a of t.files){let p=X(c,a.path),l=Ee(c,p);if(l.startsWith("..")||Te(l))throw new Error(`Invalid registry file path: ${a.path}`);let x=Me(p);if(V(x)||je(x,{recursive:!0}),V(p)&&!i){let C=await n(a.path);if(C==="skip"){r.push(a.path);continue}C==="all"&&(i=!0)}let S=Ae(a.content,e.alias);ke(p,S,"utf-8"),s.push(a.path)}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],a=i===t.children.length-1&&r.length===0;G(c,s,a)}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(g=>{let $="Other";for(let[le,pe]of Object.entries(M))if(pe.includes(g)){$=le;break}return{value:g,label:g,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()}j("Resolving dependencies..."),f();let s=[],r=new Set;for(let u of n)if(!r.has(u))try{let h=await F(u),y=U(h);for(let g of y)r.has(g.name)||(r.add(g.name),s.push(g));G(h)}catch(h){k(`Failed to resolve ${u}: ${h.message}`);return}f();let{dependencies:i,devDependencies:c}=re(s),a=[...i,...c],p=s.length,l=i.length+c.length,x=l>0?`Install ${p} component${p>1?"s":""} + ${l} npm package${l>1?"s":""}?`:`Install ${p} component${p>1?"s":""}?`,S=await De({message:x});if(se(S)||!S){b("Installation cancelled.");return}console.log();let C=e.force??!1;for(let u of s){let{written:h,skipped:y}=await Z(u,o,C,async g=>{let $=await Fe({message:`File exists: ${g}`,options:[{value:"overwrite",label:"Overwrite"},{value:"skip",label:"Skip"},{value:"all",label:"Overwrite all"}]});return se($)?"skip":($==="all"&&(C=!0),$)});for(let g of h)b(`Written: ${o.componentPath}/${g}`);for(let g of y)b(`Skipped: ${g}`)}if(i.length>0||c.length>0){let u=Ue();if(u.start(`Installing ${a.join(", ")}`),ee(i,c,o.packageManager))u.stop(`Installed: ${a.join(", ")}`);else if(u.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! ${p} component${p>1?"s":""} installed.`)}async function ce(t){let e=await L();if(t.json){console.log(JSON.stringify(e,null,2));return}I(),j(`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),w=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(!w||w==="--help"||w==="-h"){ae();return}if(w==="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(w==="list"||w==="ls"){let t=O.includes("--json");await ce({json:t});return}k(`Unknown command: ${w}`),ae(),process.exit(1)}catch(t){let e=t instanceof Error?t.message:String(t);k(e||"An unknown error occurred"),process.exit(1)}}Ge(); |
+1
-1
| { | ||
| "name": "smoothui-cli", | ||
| "version": "1.0.4", | ||
| "version": "1.0.5", | ||
| "author": "educalvolpz", | ||
@@ -5,0 +5,0 @@ "description": "CLI to install SmoothUI components - beautifully designed React components with smooth animations", |
+31
-373
@@ -1,99 +0,15 @@ | ||
| # SmoothUI | ||
| # smoothui-cli | ||
|  | ||
| CLI to install [SmoothUI](https://smoothui.dev) components - beautifully designed React components with smooth animations. | ||
| <div align="center"> | ||
| ## Installation | ||
|  | ||
|  | ||
|  | ||
| [](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 | ||
| ``` | ||
| Then use the component: | ||
| ## Usage | ||
| ```tsx | ||
| import { SiriOrb } from "@/components/smoothui/ui/SiriOrb"; | ||
| ### Add components | ||
| 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 | ||
@@ -104,311 +20,53 @@ # Add a single component | ||
| # Add multiple components | ||
| npx smoothui-cli add siri-orb rich-popover animated-input | ||
| npx smoothui-cli add siri-orb grid-loader animated-tabs | ||
| # Interactive mode - browse and select components | ||
| # Interactive mode - browse and select | ||
| npx smoothui-cli add | ||
| # List all available components | ||
| npx smoothui-cli list | ||
| ``` | ||
| **Features:** | ||
| ### List components | ||
| - 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 | ||
| # Install a single component | ||
| npx shadcn@latest add @smoothui/siri-orb | ||
| # Show all available components | ||
| npx smoothui-cli list | ||
| # Install multiple components | ||
| npx shadcn@latest add @smoothui/rich-popover @smoothui/animated-input | ||
| # JSON output | ||
| npx smoothui-cli list --json | ||
| ``` | ||
| ### Manual Installation | ||
| ### Options | ||
| If you prefer to install components manually, you can copy the component files directly: | ||
| 1. **Install dependencies**: | ||
| ```bash | ||
| pnpm add motion tailwindcss lucide-react clsx tailwind-merge | ||
| ``` | ||
| # Custom install path | ||
| npx smoothui-cli add siri-orb --path src/components/ui | ||
| 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 | ||
| # Force overwrite existing files | ||
| npx smoothui-cli add siri-orb --force | ||
| ``` | ||
| ```tsx | ||
| // lib/utils/cn.ts | ||
| import { clsx, type ClassValue } from "clsx"; | ||
| import { twMerge } from "tailwind-merge"; | ||
| ## Features | ||
| export function cn(...inputs: ClassValue[]) { | ||
| return twMerge(clsx(inputs)); | ||
| } | ||
| ``` | ||
| - 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 | ||
| ## Usage | ||
| ## Alternative: shadcn CLI | ||
| ### Basic Usage | ||
| You can also use the shadcn CLI with the `@smoothui` registry: | ||
| ```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 | ||
| ``` | ||
| #### 3. Import Path Issues | ||
| ## Links | ||
| **Error**: `Cannot find module '@/lib/utils/cn'` | ||
| - [Documentation](https://smoothui.dev) | ||
| - [Components](https://smoothui.dev/docs/components) | ||
| - [GitHub](https://github.com/educlopez/smoothui) | ||
| **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 | ||
| This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. | ||
| MIT |
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
45
7.14%18793
-33.82%3
50%