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.
- package/README.md +1 -1
- package/bin/cli.js +208 -83
- 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
|
-
|
|
37
|
-
|
|
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
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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.
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
114
|
-
|
|
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 (
|
|
120
|
-
console.log(
|
|
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 =
|
|
174
|
+
const componentSlug = cleanArgs[1];
|
|
126
175
|
if (!componentSlug) {
|
|
127
|
-
console.log(
|
|
128
|
-
|
|
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(
|
|
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(
|
|
143
|
-
|
|
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(
|
|
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 =
|
|
151
|
-
if (
|
|
152
|
-
targetDir = path.
|
|
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
|
-
|
|
156
|
-
`📁
|
|
252
|
+
console.log(
|
|
253
|
+
`📁 Target directory: ${colors.bold}${path.relative(process.cwd(), targetDir)}${colors.reset}`,
|
|
157
254
|
);
|
|
158
|
-
|
|
159
|
-
|
|
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(
|
|
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(
|
|
182
|
-
|
|
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(
|
|
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
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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(
|
|
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(
|
|
220
|
-
|
|
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.
|
|
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
|
},
|