tweenlabs 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +283 -168
  2. package/bin/cli.js +167 -42
  3. package/package.json +3 -1
package/README.md CHANGED
@@ -1,168 +1,283 @@
1
- <!-- SEO Meta Block — paste this in your Next.js layout.tsx or head -->
2
- <!--
3
- title: TweenLabs — Premium Open Source GSAP Component Library for Next.js
4
- description: A free, open-source GSAP animation component library and UI template sandbox built with Next.js 16, React 19, and Lenis. Learn and build modern web animations with reusable, copy-paste ready components.
5
- keywords: TweenLabs, GSAP component library, GSAP Next.js, GSAP animations React, open source animation library, web animation components, Lenis smooth scroll, GSAP ScrollTrigger components, Next.js animation library
6
- -->
7
-
8
- # <img src="https://raw.githubusercontent.com/TweenLabs/TweenLabs/master/public/logo.svg" alt="TweenLabs Logo" width="40" height="40" align="center" /> TweenLabs
9
-
10
-
11
- > The open-source **GSAP animation component library** for Next.js developers — learn, copy, and contribute modern web animation patterns built with **GSAP 3.15**, **Next.js 16**, and **Lenis**.
12
-
13
- **[Live Demo](https://tweenlabs.xyz)** • **[Contributing Guide](#contributing)** • **[Roadmap](#roadmap)**
14
-
15
- ![Next.js](https://img.shields.io/badge/Next.js-16-black?style=flat-square)
16
- ![GSAP](https://img.shields.io/badge/GSAP-3.15-88CE02?style=flat-square)
17
- ![React](https://img.shields.io/badge/React-19-61DAFB?style=flat-square)
18
- ![License](https://img.shields.io/badge/license-MIT-blue?style=flat-square)
19
-
20
- <!-- [Contributors](https://img.shields.io/github/contributors/TweenLabs/TweenLabs?style=flat-square) -->
21
-
22
- ---
23
-
24
- ## What is TweenLabs?
25
-
26
- **TweenLabs** is a community-driven, open-source collection of reusable GSAP animation components for Next.js and React. Every component is production-ready, well-documented, and built to help developers understand *how* advanced web animations work — not just copy code blindly.
27
-
28
- No paid plugins. No locked content. Just clean, modern animation patterns anyone can learn from and build on.
29
-
30
- > 🔍 **Looking for a GSAP component library for Next.js?** You found it.
31
-
32
- ---
33
-
34
- ## Animation Components
35
-
36
- | Component | Description | GSAP Features Used |
37
- |-----------|-------------|-------------------|
38
- | **Gravity Drop** | Physics-based falling animations with realistic bounce | `gsap.to`, `ease`, stagger |
39
- | **Scroll-Triggered Assemblies** | Content reveals synced with scroll position | ScrollTrigger |
40
- | **Border Reveal Effects** | Inward/outward border animations | Timeline, `clipPath` |
41
- | **Horizontal Card Showcase** | Smooth carousel and card transitions | ScrollTrigger, `x` transforms |
42
- | **Page Transitions** | Seamless route change animations | Timeline, Next.js router |
43
- | **Smooth Scrolling** | Native-feel smooth scroll | Lenis + GSAP ticker |
44
-
45
- > More components added with every contribution. [See full list →](https://tweenlabs.xyz)
46
-
47
- ---
48
-
49
- ## 🛠 Tech Stack
50
-
51
- - **[Next.js 16](https://nextjs.org/)** – React framework with SSR and App Router
52
- - **[GSAP 3.15](https://gsap.com/)** – Industry-standard JavaScript animation library
53
- - **[React 19](https://react.dev/)** – Modern component-based UI
54
- - **[Lenis 1.3](https://github.com/darkroom-digital/lenis)** – Buttery smooth scroll
55
- - **[Tailwind CSS 4](https://tailwindcss.com/)** Utility-first styling
56
- - **[TypeScript](https://www.typescriptlang.org/)** Full type safety
57
-
58
- ---
59
-
60
- ## 🚀 Quick Start
61
-
62
- ```bash
63
- # Install pnpm if you don't have it
64
- npm install -g pnpm
65
-
66
- # Clone and run
67
- git clone https://github.com/TweenLabs/TweenLabs.git
68
- cd TweenLabs
69
- pnpm install
70
- pnpm dev
71
- ```
72
-
73
- Open [http://localhost:3000](http://localhost:3000) — pick any animation card and start exploring.
74
-
75
- ---
76
-
77
- ## 🤝 Contributing
78
-
79
- TweenLabs grows with the community. We welcome new components, bug fixes, docs, performance improvements, and accessibility enhancements.
80
-
81
- ### Steps
82
-
83
- ```bash
84
- # 1. Fork the repo, then clone your fork
85
- git clone https://github.com/YOUR_USERNAME/TweenLabs.git
86
-
87
- # 2. Create a branch
88
- git checkout -b feat/your-animation-name
89
-
90
- # 3. Install & run
91
- pnpm install && pnpm dev
92
-
93
- # 4. Make changes, then commit
94
- git commit -m "feat: add [animation name]"
95
-
96
- # 5. Push and open a PR
97
- git push origin feat/your-animation-name
98
- ```
99
-
100
- ### Adding a New Component
101
-
102
- - Follow the folder pattern: `src/app/XX-component-name/`
103
- - Include a `page.tsx` with your animation
104
- - Add inline comments explaining the GSAP logic
105
- - Reference `gsapskills.md` for best practices
106
-
107
- ### Good First Contributions
108
-
109
- - ✅ Add explanatory comments to existing animations
110
- - ✅ Test on mobile/tablet and report issues
111
- - Create animation variants with different easing curves
112
- - ✅ Improve accessibility (`prefers-reduced-motion`, ARIA)
113
- - Write or improve docs
114
-
115
- ### Code Guidelines
116
-
117
- - Use CSS transforms (`x`, `y`, `scale`) — never layout properties
118
- - Always respect `prefers-reduced-motion`
119
- - TypeScript only no `any` types
120
- - Write descriptive commit messages
121
-
122
- ---
123
-
124
- ## 🗺 Roadmap
125
-
126
- - [ ] Export as npm package (`@tweenlabs/components`)
127
- - [ ] Storybook integration for isolated component previews
128
- - [ ] Unit tests for animation logic
129
- - [ ] FLIP, Draggable, MorphSVG animation patterns
130
- - [ ] Community showcase gallery
131
- - [ ] Animation starter templates for common use cases
132
-
133
- ---
134
-
135
- ## 📚 Learn More
136
-
137
- New to GSAP? These resources pair perfectly with this repo:
138
-
139
- - [GSAP Official Docs](https://gsap.com/docs/) — start here
140
- - [GSAP + React Guide](https://gsap.com/resources/React/) — useGSAP hook
141
- - [ScrollTrigger Docs](https://gsap.com/docs/v3/Plugins/ScrollTrigger/) — scroll animations
142
- - [Next.js Docs](https://nextjs.org/docs) — framework reference
143
- - [Lenis Docs](https://lenis.studiofreight.com/) — smooth scroll setup
144
-
145
- ---
146
-
147
- ## 📜 License
148
-
149
- [MIT](LICENSE) free to use in personal and commercial projects.
150
-
151
- ---
152
-
153
- ## 🌟 Contributors
154
-
155
- Thanks to everyone building this together. ❤️
156
-
157
- <!-- Add contributor grid here once you have 5+ contributors -->
158
-
159
- ---
160
-
161
- **Built in public. Animated with love. Open to all. 🚀**
162
-
163
- <!--
164
- SEARCH TAGS (do not remove improves GitHub discoverability):
165
- tweenlabs, gsap animation library, gsap component library, gsap nextjs, gsap react components,
166
- open source animation, web animation components, lenis smooth scroll, scrolltrigger examples,
167
- next.js animation, gsap copy paste components, gsap learning, gsap playground
168
- -->
1
+ <!-- SEO Meta Block — paste this in your Next.js layout.tsx or head -->
2
+ <!--
3
+ title: TweenLabs — Premium Open Source GSAP Component Library for Next.js
4
+ description: A free, open-source GSAP animation component library and UI template sandbox built with Next.js 16, React 19, and Lenis. Learn and build modern web animations with reusable, copy-paste ready components.
5
+ keywords: TweenLabs, GSAP component library, GSAP Next.js, GSAP animations React, open source animation library, web animation components, Lenis smooth scroll, GSAP ScrollTrigger components, Next.js animation library
6
+ -->
7
+
8
+ # <img src="https://raw.githubusercontent.com/TweenLabs/TweenLabs/master/public/logo.svg" alt="TweenLabs Logo" width="40" height="40" align="center" /> TweenLabs
9
+
10
+ > The open-source **GSAP animation component library** for Next.js developers — learn, copy, and contribute modern web animation patterns built with **GSAP 3.15**, **Next.js 16**, and **Lenis**.
11
+
12
+ **[Live Demo](https://tweenlabs.xyz)** • **[Contributing Guide](#contributing)** • **[Roadmap](#roadmap)**
13
+
14
+ ![Next.js](https://img.shields.io/badge/Next.js-16-black?style=flat-square)
15
+ ![GSAP](https://img.shields.io/badge/GSAP-3.15-88CE02?style=flat-square)
16
+ ![React](https://img.shields.io/badge/React-19-61DAFB?style=flat-square)
17
+ ![License](https://img.shields.io/badge/license-MIT-blue?style=flat-square)
18
+ ![npm](https://img.shields.io/npm/v/tweenlabs?style=flat-square&color=CB3837)
19
+
20
+ <!-- [Contributors](https://img.shields.io/github/contributors/TweenLabs/TweenLabs?style=flat-square) -->
21
+
22
+ ---
23
+
24
+ ## What is TweenLabs?
25
+
26
+ **TweenLabs** is a community-driven, open-source collection of reusable GSAP animation components for Next.js and React. Every component is production-ready, well-documented, and built to help developers understand *how* advanced web animations work — not just copy code blindly.
27
+
28
+ No paid plugins. No locked content. Just clean, modern animation patterns anyone can learn from and build on.
29
+
30
+ > 🔍 **Looking for a GSAP component library for Next.js?** You found it.
31
+
32
+ ---
33
+
34
+ ## CLI — Install Components Instantly
35
+
36
+ TweenLabs ships with a **zero-dependency CLI** that lets you pull components directly into your codebase — no copy-paste required.
37
+
38
+ ### Initialize configuration
39
+
40
+ Run the `init` command to configure your preferred installation path. This creates a `tweenlabs.config.json` file in the root of your project, meaning you won't be prompted for the path when adding components in the future.
41
+
42
+ ```bash
43
+ npx tweenlabs@latest init
44
+ ```
45
+
46
+ By default, it will detect your project setup and suggest `./src/components/tweenlabs` or `./components/tweenlabs`.
47
+
48
+ ### Add a component
49
+
50
+ ```bash
51
+ npx tweenlabs@latest add <component-slug>
52
+ ```
53
+
54
+ This will:
55
+ 1. Fetch the component files from the TweenLabs registry
56
+ 2. Detect your project layout (supports `src/` and non-`src/` setups)
57
+ 3. Automatically resolve the install path to `src/components/tweenlabs/`
58
+ 4. Create the directory if it doesn't exist
59
+ 5. Detect and install any missing npm dependencies
60
+
61
+ ### Browse & install interactively
62
+
63
+ Run `add` without a slug to get an interactive picker:
64
+
65
+ ```bash
66
+ npx tweenlabs@latest add
67
+ ```
68
+
69
+ ```
70
+ ▲ tweenlabs v0.1.6
71
+
72
+ Select a component to install:
73
+
74
+ [1] . All Components
75
+ [2] gravity-drop Physics-based falling animations with realistic bounce
76
+ [3] scroll-assembly Content reveals synced with scroll position
77
+ [4] border-reveal Inward/outward border animations
78
+ ...
79
+
80
+ 👉 Enter the number of the component to add (1-8):
81
+ ```
82
+
83
+ ### List all available components
84
+
85
+ ```bash
86
+ npx tweenlabs@latest list
87
+ ```
88
+
89
+ ### Install all components at once
90
+
91
+ ```bash
92
+ npx tweenlabs@latest add .
93
+ ```
94
+
95
+ ---
96
+
97
+ ## 📁 Output Structure
98
+
99
+ After installation, components land here by default:
100
+
101
+ ```
102
+ your-project/
103
+ └── src/
104
+ └── components/
105
+ └── tweenlabs/
106
+ ├── GravityDrop.tsx
107
+ ├── BorderReveal.tsx
108
+ └── ...
109
+ ```
110
+
111
+ > **tweenlabs.config.json:** If a `tweenlabs.config.json` exists in your project's root, the CLI reads the `path` option and installs components there. This takes highest precedence.
112
+ >
113
+ > **shadcn UI users:** If no `tweenlabs.config.json` exists but your project has a `components.json`, TweenLabs reads the `aliases.components` field and installs into the matching directory under a `tweenlabs/` subfolder automatically.
114
+
115
+ ---
116
+
117
+ ## 🚩 CLI Flags
118
+
119
+ | Flag | Short | Description |
120
+ |------|-------|-------------|
121
+ | `--yes` | `-y` | Skip all prompts; accept all defaults and auto-install dependencies |
122
+ | `--path <dir>` | `-p` | Override the install directory |
123
+ | `--overwrite` | `-o` | Overwrite existing files without prompting |
124
+ | `--help` | `-h` | Show help |
125
+ | `--version` | `-v` | Show CLI version |
126
+
127
+ ### Examples
128
+
129
+ ```bash
130
+ # Install with a custom path
131
+ npx tweenlabs@latest add gravity-drop --path src/ui/animations
132
+
133
+ # Install all, skip all prompts, overwrite existing files
134
+ npx tweenlabs@latest add . --yes --overwrite
135
+
136
+ # Install with pnpm dlx
137
+ pnpm dlx tweenlabs add border-reveal
138
+ ```
139
+
140
+ ---
141
+
142
+ ## Animation Components
143
+
144
+ | Component | Description | GSAP Features Used |
145
+ |-----------|-------------|-------------------|
146
+ | **Gravity Drop** | Physics-based falling animations with realistic bounce | `gsap.to`, `ease`, stagger |
147
+ | **Scroll-Triggered Assemblies** | Content reveals synced with scroll position | ScrollTrigger |
148
+ | **Border Reveal Effects** | Inward/outward border animations | Timeline, `clipPath` |
149
+ | **Horizontal Card Showcase** | Smooth carousel and card transitions | ScrollTrigger, `x` transforms |
150
+ | **Page Transitions** | Seamless route change animations | Timeline, Next.js router |
151
+ | **Smooth Scrolling** | Native-feel smooth scroll | Lenis + GSAP ticker |
152
+
153
+ > More components added with every contribution. [See full list →](https://tweenlabs.xyz)
154
+
155
+ ---
156
+
157
+ ## 🛠 Tech Stack
158
+
159
+ - **[Next.js 16](https://nextjs.org/)** – React framework with SSR and App Router
160
+ - **[GSAP 3.15](https://gsap.com/)** – Industry-standard JavaScript animation library
161
+ - **[React 19](https://react.dev/)** Modern component-based UI
162
+ - **[Lenis 1.3](https://github.com/darkroom-digital/lenis)** – Buttery smooth scroll
163
+ - **[Tailwind CSS 4](https://tailwindcss.com/)** – Utility-first styling
164
+ - **[TypeScript](https://www.typescriptlang.org/)** Full type safety
165
+
166
+ ---
167
+
168
+ ## 🚀 Quick Start (Playground)
169
+
170
+ Want to run the full TweenLabs playground locally?
171
+
172
+ ```bash
173
+ # Install pnpm if you don't have it
174
+ npm install -g pnpm
175
+
176
+ # Clone and run
177
+ git clone https://github.com/TweenLabs/TweenLabs.git
178
+ cd TweenLabs
179
+ pnpm install
180
+ pnpm dev
181
+ ```
182
+
183
+ Open [http://localhost:3000](http://localhost:3000) — pick any animation card and start exploring.
184
+
185
+ ---
186
+
187
+ ## 🤝 Contributing
188
+
189
+ TweenLabs grows with the community. We welcome new components, bug fixes, docs, performance improvements, and accessibility enhancements.
190
+
191
+ ### Steps
192
+
193
+ ```bash
194
+ # 1. Fork the repo, then clone your fork
195
+ git clone https://github.com/YOUR_USERNAME/TweenLabs.git
196
+
197
+ # 2. Create a branch
198
+ git checkout -b feat/your-animation-name
199
+
200
+ # 3. Install & run
201
+ pnpm install && pnpm dev
202
+
203
+ # 4. Make changes, then commit
204
+ git commit -m "feat: add [animation name]"
205
+
206
+ # 5. Push and open a PR
207
+ git push origin feat/your-animation-name
208
+ ```
209
+
210
+ ### Adding a New Component
211
+
212
+ - Follow the folder pattern: `src/app/XX-component-name/`
213
+ - Include a `page.tsx` with your animation
214
+ - Add inline comments explaining the GSAP logic
215
+ - Reference `gsapskills.md` for best practices
216
+
217
+ ### Good First Contributions
218
+
219
+ - ✅ Add explanatory comments to existing animations
220
+ - ✅ Test on mobile/tablet and report issues
221
+ - ✅ Create animation variants with different easing curves
222
+ - ✅ Improve accessibility (`prefers-reduced-motion`, ARIA)
223
+ - ✅ Write or improve docs
224
+
225
+ ### Code Guidelines
226
+
227
+ - Use CSS transforms (`x`, `y`, `scale`) — never layout properties
228
+ - Always respect `prefers-reduced-motion`
229
+ - TypeScript only — no `any` types
230
+ - Write descriptive commit messages
231
+
232
+ ---
233
+
234
+ ## 🗺 Roadmap
235
+
236
+ - [x] CLI — install components via `npx tweenlabs add`
237
+ - [x] Auto-detect package manager (npm, pnpm, yarn, bun)
238
+ - [x] Auto-install missing dependencies
239
+ - [x] Interactive component picker
240
+ - [x] `components.json` / path resolution
241
+ - [ ] Export as npm package (`@tweenlabs/components`)
242
+ - [ ] Storybook integration for isolated component previews
243
+ - [ ] Unit tests for animation logic
244
+ - [ ] FLIP, Draggable, MorphSVG animation patterns
245
+ - [ ] Community showcase gallery
246
+ - [ ] Animation starter templates for common use cases
247
+
248
+ ---
249
+
250
+ ## 📚 Learn More
251
+
252
+ New to GSAP? These resources pair perfectly with this repo:
253
+
254
+ - [GSAP Official Docs](https://gsap.com/docs/) — start here
255
+ - [GSAP + React Guide](https://gsap.com/resources/React/) — useGSAP hook
256
+ - [ScrollTrigger Docs](https://gsap.com/docs/v3/Plugins/ScrollTrigger/) — scroll animations
257
+ - [Next.js Docs](https://nextjs.org/docs) — framework reference
258
+ - [Lenis Docs](https://lenis.studiofreight.com/) — smooth scroll setup
259
+
260
+ ---
261
+
262
+ ## 📜 License
263
+
264
+ [MIT](LICENSE) — free to use in personal and commercial projects.
265
+
266
+ ---
267
+
268
+ ## 🌟 Contributors
269
+
270
+ Thanks to everyone building this together. ❤️
271
+
272
+ <!-- Add contributor grid here once you have 5+ contributors -->
273
+
274
+ ---
275
+
276
+ **Built in public. Animated with love. Open to all. 🚀**
277
+
278
+ <!--
279
+ SEARCH TAGS (do not remove — improves GitHub discoverability):
280
+ tweenlabs, gsap animation library, gsap component library, gsap nextjs, gsap react components,
281
+ open source animation, web animation components, lenis smooth scroll, scrolltrigger examples,
282
+ next.js animation, gsap copy paste components, gsap learning, gsap playground
283
+ -->
package/bin/cli.js CHANGED
@@ -22,13 +22,14 @@ const helpText = `
22
22
  ${colors.bold}${colors.cyan}TweenLabs CLI${colors.reset} - Install premium GSAP components directly into your codebase.
23
23
 
24
24
  ${colors.bold}Usage:${colors.reset}
25
+ npx tweenlabs init Initialize configuration file
25
26
  npx tweenlabs list List all available components
26
27
  npx tweenlabs add <component-slug> Install a specific component
27
28
 
28
29
  ${colors.bold}Options:${colors.reset}
29
30
  -y, --yes Skip all prompts (auto-accept defaults & install dependencies)
30
31
  -p, --path <path> Specify a custom directory to install the component
31
- -o, --overwrite Overwrite existing component files without prompting
32
+ -o, --overwrite Overwrite existing component files/configuration without prompting
32
33
  -h, --help Show this help message
33
34
  -v, --version Show version
34
35
  `;
@@ -79,6 +80,31 @@ function askQuestion(query) {
79
80
  );
80
81
  }
81
82
 
83
+ // Levenshtein distance helper for spelling suggestions
84
+ function getLevenshteinDistance(a, b) {
85
+ const tmp = [];
86
+ let i;
87
+ let j;
88
+ const alen = a.length;
89
+ const blen = b.length;
90
+ if (alen === 0) return blen;
91
+ if (blen === 0) return alen;
92
+ for (i = 0; i <= alen; i++) {
93
+ tmp[i] = [i];
94
+ }
95
+ for (j = 0; j <= blen; j++) {
96
+ tmp[0][j] = j;
97
+ }
98
+ for (i = 1; i <= alen; i++) {
99
+ for (j = 1; j <= blen; j++) {
100
+ tmp[i][j] = a.charAt(i - 1) === b.charAt(j - 1)
101
+ ? tmp[i - 1][j - 1]
102
+ : Math.min(tmp[i - 1][j - 1] + 1, Math.min(tmp[i][j - 1] + 1, tmp[i - 1][j] + 1));
103
+ }
104
+ }
105
+ return tmp[alen][blen];
106
+ }
107
+
82
108
  // Detect client's package manager
83
109
  function detectPackageManager() {
84
110
  if (fs.existsSync(path.join(process.cwd(), "pnpm-lock.yaml"))) return "pnpm";
@@ -95,7 +121,7 @@ async function main() {
95
121
  process.exit(0);
96
122
  }
97
123
 
98
- let version = "0.1.4";
124
+ let version = "0.1.6";
99
125
  try {
100
126
  const pkg = require("../package.json");
101
127
  version = pkg.version;
@@ -145,8 +171,62 @@ async function main() {
145
171
  process.exit(0);
146
172
  }
147
173
 
174
+ if (cleanArgs[0] === "init") {
175
+ const configPath = path.join(process.cwd(), "tweenlabs.config.json");
176
+ if (fs.existsSync(configPath) && !isOverwrite && !isYes) {
177
+ console.log(
178
+ `${colors.yellow}! tweenlabs.config.json already exists.${colors.reset}`
179
+ );
180
+ const overwriteConfirm = await askQuestion(
181
+ `Overwrite configuration file? (y/n) ${colors.gray}[y]${colors.reset}: `
182
+ );
183
+ if (
184
+ overwriteConfirm &&
185
+ overwriteConfirm.toLowerCase() !== "y" &&
186
+ overwriteConfirm.toLowerCase() !== "yes"
187
+ ) {
188
+ console.log(`${colors.yellow}! Configuration initialization cancelled.${colors.reset}`);
189
+ process.exit(0);
190
+ }
191
+ }
192
+
193
+ let defaultDir = "";
194
+ if (fs.existsSync(path.join(process.cwd(), "src"))) {
195
+ defaultDir = "./src/components/tweenlabs";
196
+ } else {
197
+ defaultDir = "./components/tweenlabs";
198
+ }
199
+
200
+ let targetPath = defaultDir;
201
+ if (!isYes) {
202
+ const inputPath = await askQuestion(
203
+ `Configure component installation path (${defaultDir}): `
204
+ );
205
+ if (inputPath) {
206
+ targetPath = inputPath;
207
+ }
208
+ }
209
+
210
+ try {
211
+ const configData = {
212
+ path: targetPath,
213
+ };
214
+ fs.writeFileSync(configPath, JSON.stringify(configData, null, 2), "utf-8");
215
+ console.log(
216
+ `\n${colors.green}✔ Created tweenlabs.config.json with path: ${colors.bold}${targetPath}${colors.reset}\n`
217
+ );
218
+ process.exit(0);
219
+ } catch (err) {
220
+ console.error(
221
+ `${colors.red}Error: Failed to create tweenlabs.config.json.${colors.reset}`
222
+ );
223
+ console.error(`${colors.gray}Details: ${err.message}${colors.reset}`);
224
+ process.exit(1);
225
+ }
226
+ }
227
+
148
228
  if (cleanArgs[0] === "list") {
149
- console.log(`${colors.cyan}🔍 Fetching registry...${colors.reset}`);
229
+ console.log(`${colors.cyan}Fetching registry...${colors.reset}`);
150
230
  const domain =
151
231
  process.env.TWEENLABS_REGISTRY_URL || "https://tweenlabs.xyz";
152
232
  const url = `${domain}/api/registry/list`;
@@ -170,7 +250,7 @@ async function main() {
170
250
  process.exit(0);
171
251
  } catch (err) {
172
252
  console.error(
173
- `${colors.red} Failed to fetch components list.${colors.reset}`,
253
+ `${colors.red}Error: Failed to fetch components list.${colors.reset}`,
174
254
  );
175
255
  console.error(`${colors.gray}Details: ${err.message}${colors.reset}`);
176
256
  process.exit(1);
@@ -179,7 +259,7 @@ async function main() {
179
259
 
180
260
  if (cleanArgs[0] !== "add") {
181
261
  console.log(
182
- `${colors.red}Error: Unknown command "${cleanArgs[0]}". Did you mean "add" or "list"?${colors.reset}`,
262
+ `${colors.red}Error: Unknown command "${cleanArgs[0]}". Did you mean "add", "list" or "init"?${colors.reset}`,
183
263
  );
184
264
  console.log(helpText);
185
265
  process.exit(1);
@@ -187,7 +267,7 @@ async function main() {
187
267
 
188
268
  let componentSlug = cleanArgs[1];
189
269
  if (!componentSlug) {
190
- console.log(`${colors.cyan}🔍 Fetching registry...${colors.reset}`);
270
+ console.log(`${colors.cyan}Fetching registry...${colors.reset}`);
191
271
  const domain =
192
272
  process.env.TWEENLABS_REGISTRY_URL || "https://tweenlabs.xyz";
193
273
  const listUrl = `${domain}/api/registry/list`;
@@ -196,7 +276,7 @@ async function main() {
196
276
  listData = await fetchJson(listUrl);
197
277
  } catch (err) {
198
278
  console.error(
199
- `${colors.red} Failed to fetch components list.${colors.reset}`,
279
+ `${colors.red}Error: Failed to fetch components list.${colors.reset}`,
200
280
  );
201
281
  console.error(`${colors.gray}Details: ${err.message}${colors.reset}`);
202
282
  process.exit(1);
@@ -232,11 +312,11 @@ async function main() {
232
312
  console.log("");
233
313
 
234
314
  const choiceStr = await askQuestion(
235
- `👉 Enter the number of the component to add (1-${components.length + 1}): `,
315
+ `? Enter the number of the component to add (1-${components.length + 1}): `,
236
316
  );
237
317
  const choice = parseInt(choiceStr, 10);
238
318
  if (Number.isNaN(choice) || choice < 1 || choice > components.length + 1) {
239
- console.log(`${colors.red} Invalid choice. Exiting.${colors.reset}`);
319
+ console.log(`${colors.red}Error: Invalid choice. Exiting.${colors.reset}`);
240
320
  process.exit(1);
241
321
  }
242
322
 
@@ -253,34 +333,49 @@ async function main() {
253
333
  if (customPath) {
254
334
  targetDir = path.resolve(process.cwd(), customPath);
255
335
  } else {
256
- // 1. Try to read components.json (shadcn configuration)
257
- const componentsJsonPath = path.join(process.cwd(), "components.json");
258
- if (fs.existsSync(componentsJsonPath)) {
336
+ // 1. Try to read tweenlabs.config.json (our config)
337
+ const tweenlabsConfigPath = path.join(process.cwd(), "tweenlabs.config.json");
338
+ if (fs.existsSync(tweenlabsConfigPath)) {
259
339
  try {
260
- const config = JSON.parse(fs.readFileSync(componentsJsonPath, "utf-8"));
261
- const compAlias = config.aliases?.components;
262
- if (compAlias) {
263
- const cleanAlias = compAlias.replace(/^[@~]\//, "");
264
- if (
265
- fs.existsSync(path.join(process.cwd(), "src")) &&
266
- !cleanAlias.startsWith("src/")
267
- ) {
268
- targetDir = path.join(
269
- process.cwd(),
270
- "src",
271
- cleanAlias,
272
- "tweenlabs",
273
- );
274
- } else {
275
- targetDir = path.join(process.cwd(), cleanAlias, "tweenlabs");
276
- }
340
+ const config = JSON.parse(fs.readFileSync(tweenlabsConfigPath, "utf-8"));
341
+ if (config.path) {
342
+ targetDir = path.resolve(process.cwd(), config.path);
277
343
  }
278
344
  } catch (_err) {
279
345
  // Ignore JSON parse errors
280
346
  }
281
347
  }
282
348
 
283
- // 2. Fallback if targetDir is still not resolved
349
+ // 2. Try to read components.json (shadcn configuration) if still not resolved
350
+ if (!targetDir) {
351
+ const componentsJsonPath = path.join(process.cwd(), "components.json");
352
+ if (fs.existsSync(componentsJsonPath)) {
353
+ try {
354
+ const config = JSON.parse(fs.readFileSync(componentsJsonPath, "utf-8"));
355
+ const compAlias = config.aliases?.components;
356
+ if (compAlias) {
357
+ const cleanAlias = compAlias.replace(/^[@~]\//, "");
358
+ if (
359
+ fs.existsSync(path.join(process.cwd(), "src")) &&
360
+ !cleanAlias.startsWith("src/")
361
+ ) {
362
+ targetDir = path.join(
363
+ process.cwd(),
364
+ "src",
365
+ cleanAlias,
366
+ "tweenlabs",
367
+ );
368
+ } else {
369
+ targetDir = path.join(process.cwd(), cleanAlias, "tweenlabs");
370
+ }
371
+ }
372
+ } catch (_err) {
373
+ // Ignore JSON parse errors
374
+ }
375
+ }
376
+ }
377
+
378
+ // 3. Fallback if targetDir is still not resolved
284
379
  if (!targetDir) {
285
380
  if (fs.existsSync(path.join(process.cwd(), "src"))) {
286
381
  targetDir = path.join(process.cwd(), "src", "components", "tweenlabs");
@@ -291,14 +386,14 @@ async function main() {
291
386
  }
292
387
 
293
388
  console.log(
294
- `📁 Target directory: ${colors.bold}${path.relative(process.cwd(), targetDir)}${colors.reset}`,
389
+ `Target directory: ${colors.bold}${path.relative(process.cwd(), targetDir)}${colors.reset}`,
295
390
  );
296
391
 
297
392
  const domain = process.env.TWEENLABS_REGISTRY_URL || "https://tweenlabs.xyz";
298
393
  let slugsToInstall = [];
299
394
  if (componentSlug === "." || componentSlug === "all") {
300
395
  console.log(
301
- `${colors.cyan}🔍 Fetching all components list...${colors.reset}`,
396
+ `${colors.cyan}Fetching all components list...${colors.reset}`,
302
397
  );
303
398
  const listUrl = `${domain}/api/registry/list`;
304
399
  try {
@@ -306,7 +401,7 @@ async function main() {
306
401
  slugsToInstall = listData.components.map((c) => c.cleanSlug || c.slug);
307
402
  } catch (err) {
308
403
  console.error(
309
- `${colors.red} Failed to fetch components list.${colors.reset}`,
404
+ `${colors.red}Error: Failed to fetch components list.${colors.reset}`,
310
405
  );
311
406
  console.error(`${colors.gray}Details: ${err.message}${colors.reset}`);
312
407
  process.exit(1);
@@ -322,7 +417,7 @@ async function main() {
322
417
 
323
418
  for (const slug of slugsToInstall) {
324
419
  console.log(
325
- `${colors.cyan}🔍 Fetching ${colors.bold}${slug}${colors.reset}${colors.cyan} registry data...${colors.reset}`,
420
+ `${colors.cyan}Fetching ${colors.bold}${slug}${colors.reset}${colors.cyan} registry data...${colors.reset}`,
326
421
  );
327
422
  const url = `${domain}/api/registry/${slug}`;
328
423
  try {
@@ -340,19 +435,49 @@ async function main() {
340
435
  }
341
436
  } catch (_err) {
342
437
  console.error(
343
- `${colors.red} Failed to fetch component ${slug}. Skipping.${colors.reset}`,
438
+ `${colors.red}Error: Failed to fetch component "${slug}". Skipping.${colors.reset}`,
344
439
  );
440
+ // Fetch registry list to suggest closest matches
441
+ let suggestions = [];
442
+ try {
443
+ const listData = await fetchJson(`${domain}/api/registry/list`);
444
+ const validSlugs = listData.components.map((c) => c.cleanSlug || c.slug);
445
+
446
+ // Find slugs with low Levenshtein distance
447
+ const matches = validSlugs.map((validSlug) => {
448
+ return {
449
+ slug: validSlug,
450
+ distance: getLevenshteinDistance(slug, validSlug)
451
+ };
452
+ });
453
+
454
+ // Sort by distance ascending
455
+ matches.sort((a, b) => a.distance - b.distance);
456
+
457
+ // Threshold: distance <= 3 or distance <= half of query length
458
+ suggestions = matches
459
+ .filter((m) => m.distance <= 3 || m.distance <= Math.round(slug.length / 2))
460
+ .map((m) => m.slug);
461
+ } catch (_e) {
462
+ // If list fetch fails, we just don't show suggestions
463
+ }
464
+
465
+ if (suggestions.length > 0) {
466
+ console.error(
467
+ `${colors.yellow}Did you mean: ${suggestions.map(s => `${colors.bold}${s}${colors.reset}`).join(", ")}?${colors.reset}\n`
468
+ );
469
+ }
345
470
  }
346
471
  }
347
472
 
348
473
  if (filesToWrite.length === 0) {
349
- console.log(`${colors.red} No files to install. Exiting.${colors.reset}`);
474
+ console.log(`${colors.red}Error: No files to install. Exiting.${colors.reset}`);
350
475
  process.exit(1);
351
476
  }
352
477
 
353
478
  if (conflicts.length > 0 && !isOverwrite && !isYes) {
354
479
  console.log(
355
- `\n${colors.yellow}⚠️ The following files already exist:${colors.reset}`,
480
+ `\n${colors.yellow}! The following files already exist:${colors.reset}`,
356
481
  );
357
482
  for (const conflict of conflicts) {
358
483
  console.log(` → ${conflict}`);
@@ -365,7 +490,7 @@ async function main() {
365
490
  overwriteConfirm.toLowerCase() !== "y" &&
366
491
  overwriteConfirm.toLowerCase() !== "yes"
367
492
  ) {
368
- console.log(`${colors.yellow} Installation cancelled.${colors.reset}`);
493
+ console.log(`${colors.yellow}! Installation cancelled.${colors.reset}`);
369
494
  process.exit(0);
370
495
  }
371
496
  }
@@ -376,7 +501,7 @@ async function main() {
376
501
  }
377
502
 
378
503
  // Write component files
379
- console.log(`\n${colors.bold}💾 Writing component files...${colors.reset}`);
504
+ console.log(`\n${colors.bold}Writing component files...${colors.reset}`);
380
505
  for (const file of filesToWrite) {
381
506
  fs.writeFileSync(file.path, file.content, "utf-8");
382
507
  console.log(
@@ -402,7 +527,7 @@ async function main() {
402
527
  if (missingDeps.length > 0) {
403
528
  const pm = detectPackageManager();
404
529
  console.log(
405
- `\n${colors.bold}📦 Installing missing dependencies using ${pm}...${colors.reset}`,
530
+ `\n${colors.bold}Installing missing dependencies using ${pm}...${colors.reset}`,
406
531
  );
407
532
  for (const dep of missingDeps) {
408
533
  console.log(` → ${dep}`);
@@ -422,7 +547,7 @@ async function main() {
422
547
  );
423
548
  } catch (_err) {
424
549
  console.error(
425
- `\n${colors.red} Failed to install dependencies. Please run "${installCmd}" manually.${colors.reset}`,
550
+ `\n${colors.red}Error: Failed to install dependencies. Please run "${installCmd}" manually.${colors.reset}`,
426
551
  );
427
552
  }
428
553
  } else {
@@ -433,7 +558,7 @@ async function main() {
433
558
  }
434
559
 
435
560
  console.log(
436
- `\n${colors.bold}${colors.green}🎉 Done! All requested components installed successfully.${colors.reset}`,
561
+ `\n${colors.bold}${colors.green} Done! All requested components installed successfully.${colors.reset}`,
437
562
  );
438
563
  console.log(`You can now import and use them in your project.\n`);
439
564
  }
package/package.json CHANGED
@@ -1,6 +1,8 @@
1
1
  {
2
2
  "name": "tweenlabs",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
+ "description": "Zero-dependency CLI to install premium GSAP animation components directly into your Next.js or React project.",
5
+ "keywords": ["gsap", "animation", "nextjs", "react", "components", "cli", "tweenlabs", "scrolltrigger", "lenis"],
4
6
  "license": "MIT",
5
7
  "repository": {
6
8
  "type": "git",