tweenlabs 0.1.0 → 0.1.2

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 +1 -1
  2. package/bin/cli.js +208 -83
  3. package/package.json +10 -1
package/README.md CHANGED
@@ -5,7 +5,7 @@ description: A free, open-source GSAP animation component library and UI templat
5
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
6
  -->
7
7
 
8
- # <img src="public/logo.svg" alt="TweenLabs Logo" width="40" height="40" align="center" /> TweenLabs
8
+ # <img src="https://raw.githubusercontent.com/TweenLabs/TweenLabs/master/public/logo.svg" alt="TweenLabs Logo" width="40" height="40" align="center" /> TweenLabs
9
9
 
10
10
 
11
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**.
package/bin/cli.js CHANGED
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const fs = require("fs");
4
- const path = require("path");
5
- const { execSync } = require("child_process");
6
- const https = require("https");
7
- const readline = require("readline");
3
+ const fs = require("node:fs");
4
+ const path = require("node:path");
5
+ const { execSync } = require("node:child_process");
6
+ const https = require("node:https");
7
+ const readline = require("node:readline");
8
8
 
9
9
  // Simple Terminal Colors
10
10
  const colors = {
@@ -24,40 +24,44 @@ ${colors.bold}${colors.cyan}TweenLabs CLI${colors.reset} - Install premium GSAP
24
24
  ${colors.bold}Usage:${colors.reset}
25
25
  npx tweenlabs list List all available components
26
26
  npx tweenlabs add <component-slug> Install a specific component
27
- npx tweenlabs add all Install all components
28
-
29
- ${colors.bold}Examples:${colors.reset}
30
- npx tweenlabs list
31
- npx tweenlabs add 11-magnetic-dock
32
- npx tweenlabs add magnetic-dock
33
- npx tweenlabs add all
34
27
 
35
28
  ${colors.bold}Options:${colors.reset}
36
- --help, -h Show this help message
37
- --version, -v Show version
29
+ -y, --yes Skip all prompts (auto-accept defaults & install dependencies)
30
+ -p, --path <path> Specify a custom directory to install the component
31
+ -o, --overwrite Overwrite existing component files without prompting
32
+ -h, --help Show this help message
33
+ -v, --version Show version
38
34
  `;
39
35
 
40
36
  // Helper to make GET requests without external dependencies
41
37
  function fetchJson(url) {
42
38
  return new Promise((resolve, reject) => {
43
- const client = url.startsWith("https") ? https : require("http");
44
- client.get(url, {
45
- headers: { "User-Agent": "tweenlabs-cli" }
46
- }, (res) => {
47
- if (res.statusCode !== 200) {
48
- reject(new Error(`Server returned status code ${res.statusCode}`));
49
- return;
50
- }
51
- let data = "";
52
- res.on("data", (chunk) => { data += chunk; });
53
- res.on("end", () => {
54
- try {
55
- resolve(JSON.parse(data));
56
- } catch (err) {
57
- reject(err);
58
- }
59
- });
60
- }).on("error", reject);
39
+ const client = url.startsWith("https") ? https : require("node:http");
40
+ client
41
+ .get(
42
+ url,
43
+ {
44
+ headers: { "User-Agent": "tweenlabs-cli" },
45
+ },
46
+ (res) => {
47
+ if (res.statusCode !== 200) {
48
+ reject(new Error(`Server returned status code ${res.statusCode}`));
49
+ return;
50
+ }
51
+ let data = "";
52
+ res.on("data", (chunk) => {
53
+ data += chunk;
54
+ });
55
+ res.on("end", () => {
56
+ try {
57
+ resolve(JSON.parse(data));
58
+ } catch (err) {
59
+ reject(err);
60
+ }
61
+ });
62
+ },
63
+ )
64
+ .on("error", reject);
61
65
  });
62
66
  }
63
67
 
@@ -71,7 +75,7 @@ function askQuestion(query) {
71
75
  rl.question(query, (ans) => {
72
76
  rl.close();
73
77
  resolve(ans.trim());
74
- })
78
+ }),
75
79
  );
76
80
  }
77
81
 
@@ -86,7 +90,7 @@ function detectPackageManager() {
86
90
  async function main() {
87
91
  const args = process.argv.slice(2);
88
92
 
89
- if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
93
+ if (args.includes("--help") || args.includes("-h")) {
90
94
  console.log(helpText);
91
95
  process.exit(0);
92
96
  }
@@ -96,40 +100,93 @@ async function main() {
96
100
  process.exit(0);
97
101
  }
98
102
 
99
- if (args[0] === "list") {
100
- console.log(`\n${colors.cyan}🔍 Fetching available components list...${colors.reset}`);
101
- const domain = process.env.TWEENLABS_REGISTRY_URL || "https://tweenlabs.xyz";
103
+ // Parse flags
104
+ const isYes = args.includes("-y") || args.includes("--yes");
105
+ const isOverwrite = args.includes("-o") || args.includes("--overwrite");
106
+
107
+ // Parse path option
108
+ let customPath = null;
109
+ const pathIdx = args.findIndex((arg) => arg === "-p" || arg === "--path");
110
+ if (pathIdx !== -1 && pathIdx + 1 < args.length) {
111
+ customPath = args[pathIdx + 1];
112
+ }
113
+
114
+ // Filter out options and their arguments from cleanArgs
115
+ const cleanArgs = [];
116
+ for (let i = 0; i < args.length; i++) {
117
+ if (
118
+ args[i] === "-y" ||
119
+ args[i] === "--yes" ||
120
+ args[i] === "-o" ||
121
+ args[i] === "--overwrite"
122
+ ) {
123
+ continue;
124
+ }
125
+ if (args[i] === "-p" || args[i] === "--path") {
126
+ i++; // skip next arg (the path value)
127
+ continue;
128
+ }
129
+ cleanArgs.push(args[i]);
130
+ }
131
+
132
+ if (cleanArgs.length === 0) {
133
+ console.log(helpText);
134
+ process.exit(0);
135
+ }
136
+
137
+ if (cleanArgs[0] === "list") {
138
+ console.log(
139
+ `\n${colors.cyan}🔍 Fetching available components list...${colors.reset}`,
140
+ );
141
+ const domain =
142
+ process.env.TWEENLABS_REGISTRY_URL || "https://tweenlabs.xyz";
102
143
  const url = `${domain}/api/registry/list`;
103
144
  try {
104
145
  const data = await fetchJson(url);
105
- console.log(`\n${colors.bold}${colors.green}Available TweenLabs Components:${colors.reset}\n`);
146
+ console.log(
147
+ `\n${colors.bold}${colors.green}Available TweenLabs Components:${colors.reset}\n`,
148
+ );
106
149
  for (const comp of data.components) {
107
- console.log(` ${colors.bold}${colors.cyan}* ${comp.name}${colors.reset}`);
150
+ console.log(
151
+ ` ${colors.bold}${colors.cyan}* ${comp.name}${colors.reset}`,
152
+ );
108
153
  console.log(` Slug: ${comp.slug} (or ${comp.cleanSlug})`);
109
154
  console.log(` Desc: ${comp.description}\n`);
110
155
  }
111
156
  process.exit(0);
112
157
  } catch (err) {
113
- console.error(`\n${colors.red}❌ Failed to fetch components list.${colors.reset}`);
114
- console.error(colors.gray + `Details: ${err.message}` + colors.reset);
158
+ console.error(
159
+ `\n${colors.red}❌ Failed to fetch components list.${colors.reset}`,
160
+ );
161
+ console.error(`${colors.gray}Details: ${err.message}${colors.reset}`);
115
162
  process.exit(1);
116
163
  }
117
164
  }
118
165
 
119
- if (args[0] !== "add") {
120
- console.log(`${colors.red}Error: Unknown command "${args[0]}". Did you mean "add" or "list"?${colors.reset}`);
166
+ if (cleanArgs[0] !== "add") {
167
+ console.log(
168
+ `${colors.red}Error: Unknown command "${cleanArgs[0]}". Did you mean "add" or "list"?${colors.reset}`,
169
+ );
121
170
  console.log(helpText);
122
171
  process.exit(1);
123
172
  }
124
173
 
125
- const componentSlug = args[1];
174
+ const componentSlug = cleanArgs[1];
126
175
  if (!componentSlug) {
127
- console.log(`${colors.red}Error: Please specify a component slug to add.${colors.reset}`);
128
- console.log(colors.gray + "Example: npx tweenlabs add 11-magnetic-dock" + colors.reset);
176
+ console.log(
177
+ `${colors.red}Error: Please specify a component slug to add.${colors.reset}`,
178
+ );
179
+ console.log(
180
+ colors.gray +
181
+ "Example: npx tweenlabs add 11-magnetic-dock" +
182
+ colors.reset,
183
+ );
129
184
  process.exit(1);
130
185
  }
131
186
 
132
- console.log(`\n${colors.cyan}🔍 Fetching ${colors.bold}${componentSlug}${colors.reset}${colors.cyan} registry data...${colors.reset}`);
187
+ console.log(
188
+ `\n${colors.cyan}🔍 Fetching ${colors.bold}${componentSlug}${colors.reset}${colors.cyan} registry data...${colors.reset}`,
189
+ );
133
190
 
134
191
  // Determine registry domain (allow local testing via env variable)
135
192
  const domain = process.env.TWEENLABS_REGISTRY_URL || "https://tweenlabs.xyz";
@@ -139,24 +196,85 @@ async function main() {
139
196
  try {
140
197
  componentData = await fetchJson(url);
141
198
  } catch (err) {
142
- console.error(`\n${colors.red}❌ Failed to fetch component. Make sure the slug is correct and the server is running.${colors.reset}`);
143
- console.error(colors.gray + `Details: ${err.message}` + colors.reset);
199
+ console.error(
200
+ `\n${colors.red}❌ Failed to fetch component. Make sure the slug is correct and the server is running.${colors.reset}`,
201
+ );
202
+ console.error(`${colors.gray}Details: ${err.message}${colors.reset}`);
144
203
  process.exit(1);
145
204
  }
146
205
 
147
- console.log(`${colors.green}✓ Component found: ${colors.bold}${componentData.className}${colors.reset}`);
206
+ console.log(
207
+ `${colors.green}✓ Component found: ${colors.bold}${componentData.className}${colors.reset}`,
208
+ );
148
209
 
149
210
  // Resolve target directory
150
- let targetDir = path.join(process.cwd(), "src", "components");
151
- if (!fs.existsSync(path.join(process.cwd(), "src"))) {
152
- targetDir = path.join(process.cwd(), "components");
211
+ let targetDir = "";
212
+ if (customPath) {
213
+ targetDir = path.resolve(process.cwd(), customPath);
214
+ } else {
215
+ // 1. Try to read components.json (shadcn configuration)
216
+ const componentsJsonPath = path.join(process.cwd(), "components.json");
217
+ if (fs.existsSync(componentsJsonPath)) {
218
+ try {
219
+ const config = JSON.parse(fs.readFileSync(componentsJsonPath, "utf-8"));
220
+ const compAlias = config.aliases?.components;
221
+ if (compAlias) {
222
+ const cleanAlias = compAlias.replace(/^[@~]\//, "");
223
+ if (
224
+ fs.existsSync(path.join(process.cwd(), "src")) &&
225
+ !cleanAlias.startsWith("src/")
226
+ ) {
227
+ targetDir = path.join(
228
+ process.cwd(),
229
+ "src",
230
+ cleanAlias,
231
+ "tweenlabs",
232
+ );
233
+ } else {
234
+ targetDir = path.join(process.cwd(), cleanAlias, "tweenlabs");
235
+ }
236
+ }
237
+ } catch (_err) {
238
+ // Ignore JSON parse errors
239
+ }
240
+ }
241
+
242
+ // 2. Fallback if targetDir is still not resolved
243
+ if (!targetDir) {
244
+ if (fs.existsSync(path.join(process.cwd(), "src"))) {
245
+ targetDir = path.join(process.cwd(), "src", "components", "tweenlabs");
246
+ } else {
247
+ targetDir = path.join(process.cwd(), "components", "tweenlabs");
248
+ }
249
+ }
153
250
  }
154
251
 
155
- const userPathAnswer = await askQuestion(
156
- `📁 Where should the component be installed? ${colors.gray}(default: ${targetDir})${colors.reset}: `
252
+ console.log(
253
+ `📁 Target directory: ${colors.bold}${path.relative(process.cwd(), targetDir)}${colors.reset}`,
157
254
  );
158
- if (userPathAnswer) {
159
- targetDir = path.resolve(process.cwd(), userPathAnswer);
255
+
256
+ // Check if any files already exist
257
+ let hasExistingFiles = false;
258
+ for (const file of componentData.files) {
259
+ const filePath = path.join(targetDir, file.name);
260
+ if (fs.existsSync(filePath)) {
261
+ hasExistingFiles = true;
262
+ break;
263
+ }
264
+ }
265
+
266
+ if (hasExistingFiles && !isOverwrite && !isYes) {
267
+ const overwriteConfirm = await askQuestion(
268
+ `⚠️ Component files already exist in ${colors.bold}${path.relative(process.cwd(), targetDir)}${colors.reset}. Overwrite? (y/n) ${colors.gray}[y]${colors.reset}: `,
269
+ );
270
+ if (
271
+ overwriteConfirm &&
272
+ overwriteConfirm.toLowerCase() !== "y" &&
273
+ overwriteConfirm.toLowerCase() !== "yes"
274
+ ) {
275
+ console.log(`${colors.yellow}Installation cancelled.${colors.reset}`);
276
+ process.exit(0);
277
+ }
160
278
  }
161
279
 
162
280
  // Create target directory if it doesn't exist
@@ -169,7 +287,9 @@ async function main() {
169
287
  for (const file of componentData.files) {
170
288
  const filePath = path.join(targetDir, file.name);
171
289
  fs.writeFileSync(filePath, file.content, "utf-8");
172
- console.log(`${colors.green} → Created: ${colors.bold}${path.relative(process.cwd(), filePath)}${colors.reset}`);
290
+ console.log(
291
+ `${colors.green} → Created: ${colors.bold}${path.relative(process.cwd(), filePath)}${colors.reset}`,
292
+ );
173
293
  }
174
294
 
175
295
  // Check and install dependencies
@@ -178,8 +298,10 @@ async function main() {
178
298
  // Read package.json to see if dependencies are already installed
179
299
  let pkgJson = {};
180
300
  try {
181
- pkgJson = JSON.parse(fs.readFileSync(path.join(process.cwd(), "package.json"), "utf-8"));
182
- } catch (err) {
301
+ pkgJson = JSON.parse(
302
+ fs.readFileSync(path.join(process.cwd(), "package.json"), "utf-8"),
303
+ );
304
+ } catch (_err) {
183
305
  // Ignore
184
306
  }
185
307
 
@@ -188,36 +310,39 @@ async function main() {
188
310
 
189
311
  if (missingDeps.length > 0) {
190
312
  const pm = detectPackageManager();
191
- console.log(`\n${colors.yellow}📦 Missing dependencies: ${colors.bold}${missingDeps.join(", ")}${colors.reset}`);
192
-
193
- const installConfirm = await askQuestion(
194
- `Do you want to install them using ${colors.bold}${pm}${colors.reset}? (y/n) ${colors.gray}[y]${colors.reset}: `
313
+ console.log(
314
+ `\n${colors.cyan}📦 Installing missing dependencies: ${colors.bold}${missingDeps.join(", ")}${colors.reset} using ${pm}...`,
195
315
  );
196
316
 
197
- if (!installConfirm || installConfirm.toLowerCase() === "y" || installConfirm.toLowerCase() === "yes") {
198
- console.log(`${colors.cyan}Installing dependencies using ${pm}...${colors.reset}`);
199
- let installCmd = "";
200
- if (pm === "pnpm") installCmd = `pnpm add ${missingDeps.join(" ")}`;
201
- else if (pm === "yarn") installCmd = `yarn add ${missingDeps.join(" ")}`;
202
- else if (pm === "bun") installCmd = `bun add ${missingDeps.join(" ")}`;
203
- else installCmd = `npm install ${missingDeps.join(" ")}`;
204
-
205
- try {
206
- execSync(installCmd, { stdio: "inherit" });
207
- console.log(`${colors.green}✓ Dependencies installed successfully!${colors.reset}`);
208
- } catch (err) {
209
- console.error(`${colors.red}❌ Failed to install dependencies. Please run "${installCmd}" manually.${colors.reset}`);
210
- }
211
- } else {
212
- console.log(`${colors.yellow}⚠️ Skipping installation. Please make sure to install ${missingDeps.join(", ")} manually.${colors.reset}`);
317
+ let installCmd = "";
318
+ if (pm === "pnpm") installCmd = `pnpm add ${missingDeps.join(" ")}`;
319
+ else if (pm === "yarn") installCmd = `yarn add ${missingDeps.join(" ")}`;
320
+ else if (pm === "bun") installCmd = `bun add ${missingDeps.join(" ")}`;
321
+ else installCmd = `npm install ${missingDeps.join(" ")}`;
322
+
323
+ try {
324
+ execSync(installCmd, { stdio: "inherit" });
325
+ console.log(
326
+ `${colors.green}✓ Dependencies installed successfully!${colors.reset}`,
327
+ );
328
+ } catch (_err) {
329
+ console.error(
330
+ `${colors.red}❌ Failed to install dependencies. Please run "${installCmd}" manually.${colors.reset}`,
331
+ );
213
332
  }
214
333
  } else {
215
- console.log(`\n${colors.green}✓ All dependencies (${dependencies.join(", ")}) already installed.${colors.reset}`);
334
+ console.log(
335
+ `\n${colors.green}✓ All dependencies (${dependencies.join(", ")}) already installed.${colors.reset}`,
336
+ );
216
337
  }
217
338
  }
218
339
 
219
- console.log(`\n${colors.green}${colors.bold}🎉 Installation complete!${colors.reset}`);
220
- console.log(`You can now import and use the ${colors.bold}${componentData.className}${colors.reset} component in your project.\n`);
340
+ console.log(
341
+ `\n${colors.green}${colors.bold}🎉 Installation complete!${colors.reset}`,
342
+ );
343
+ console.log(
344
+ `You can now import and use the ${colors.bold}${componentData.className}${colors.reset} component in your project.\n`,
345
+ );
221
346
  }
222
347
 
223
348
  main().catch((err) => {
package/package.json CHANGED
@@ -1,6 +1,15 @@
1
1
  {
2
2
  "name": "tweenlabs",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
+ "license": "MIT",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/TweenLabs/TweenLabs.git"
8
+ },
9
+ "homepage": "https://tweenlabs.xyz",
10
+ "bugs": {
11
+ "url": "https://github.com/TweenLabs/TweenLabs/issues"
12
+ },
4
13
  "bin": {
5
14
  "tweenlabs": "bin/cli.js"
6
15
  },