purvex-ui 0.1.0

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/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "purvex-ui",
3
+ "version": "0.1.0",
4
+ "description": "React component generator with Tailwind CSS v4 & Purple Elegant theme - Built by Al Zaki Ibra Ramadani",
5
+ "main": "src/index.js",
6
+ "bin": {
7
+ "purvex-ui": "src/index.js"
8
+ },
9
+ "files": [
10
+ "src/",
11
+ "templates/",
12
+ "registry/"
13
+ ],
14
+ "scripts": {
15
+ "test": "echo \"No tests yet\"",
16
+ "prepare": "node -e \"require('fs').chmodSync('src/index.js', '755')\""
17
+ },
18
+ "keywords": [
19
+ "react",
20
+ "ui",
21
+ "components",
22
+ "tailwindcss",
23
+ "vite",
24
+ "cli",
25
+ "generator",
26
+ "purple",
27
+ "theme"
28
+ ],
29
+ "author": "Al Zaki Ibra Ramadani",
30
+ "license": "MIT",
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "git+https://github.com/VortechLabs/purvex-ui.git"
34
+ },
35
+ "bugs": {
36
+ "url": "https://github.com/VortechLabs/purvex-ui/issues"
37
+ },
38
+ "homepage": "https://purvex-ui.vercel.app",
39
+ "dependencies": {
40
+ "chalk": "^4.1.2",
41
+ "commander": "^11.1.0",
42
+ "fs-extra": "^11.2.0"
43
+ },
44
+ "engines": {
45
+ "node": ">=14.0.0"
46
+ }
47
+ }
@@ -0,0 +1,80 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const chalk = require('chalk');
4
+
5
+ async function addComponent(componentName, options) {
6
+ try {
7
+ console.log(chalk.blue.bold(`\nšŸ”„ Adding ${componentName}...\n`));
8
+
9
+ // 1. Load registry
10
+ const cliDir = path.dirname(path.dirname(__dirname));
11
+ const rootDir = path.dirname(path.dirname(cliDir));
12
+ const registryPath = path.join(rootDir, 'registry', 'components.json');
13
+
14
+ if (!fs.existsSync(registryPath)) {
15
+ console.log(chalk.red('āŒ Registry not found'));
16
+ return;
17
+ }
18
+
19
+ const registryContent = fs.readFileSync(registryPath, 'utf-8');
20
+ const registry = JSON.parse(registryContent);
21
+
22
+ // 2. Find component
23
+ const component = registry.components.find(c => c.name === componentName);
24
+
25
+ if (!component) {
26
+ console.log(chalk.red(`āŒ Component "${componentName}" not found`));
27
+ console.log(chalk.yellow('\nAvailable components:'));
28
+ registry.components.forEach(c => {
29
+ console.log(chalk.cyan(` - ${c.name}`));
30
+ });
31
+ return;
32
+ }
33
+
34
+ // 3. Setup target directory INSIDE src
35
+ const userProjectRoot = process.cwd();
36
+ const targetDir = path.join(userProjectRoot, 'src', 'components', 'ui');
37
+
38
+ // Auto create directory jika belum ada
39
+ if (!fs.existsSync(targetDir)) {
40
+ fs.mkdirSync(targetDir, { recursive: true });
41
+ }
42
+
43
+ // 4. Copy template files
44
+ for (const file of component.files) {
45
+ const sourcePath = path.join(rootDir, 'templates', file);
46
+ const targetPath = path.join(targetDir, path.basename(file));
47
+
48
+ if (!fs.existsSync(sourcePath)) {
49
+ console.log(chalk.yellow(`āš ļø Template ${file} not found, skipping...`));
50
+ continue;
51
+ }
52
+
53
+ // Read template
54
+ let content = fs.readFileSync(sourcePath, 'utf-8');
55
+
56
+ // Update import path untuk src/
57
+ content = content.replace(/@\/lib\/utils/g, '@/lib/utils');
58
+ content = content.replace(/@\/components\/ui/g, '@/components/ui');
59
+
60
+ // Write to target
61
+ fs.writeFileSync(targetPath, content);
62
+
63
+ const relativePath = path.relative(userProjectRoot, targetPath);
64
+ console.log(chalk.green('āœ… Created: ') + relativePath);
65
+ }
66
+
67
+ // 5. Success message
68
+ console.log(chalk.green.bold(`\nšŸŽ‰ Component ${componentName} added successfully!`));
69
+ console.log(chalk.white('\nUsage:'));
70
+ console.log(chalk.cyan(` import { ${componentName.charAt(0).toUpperCase() + componentName.slice(1)} } from "@/components/ui/${componentName}";`));
71
+
72
+ console.log(chalk.white('\nStart your dev server:'));
73
+ console.log(chalk.cyan(' npm run dev'));
74
+
75
+ } catch (error) {
76
+ console.error(chalk.red('āŒ Error adding component:'), error.message);
77
+ }
78
+ }
79
+
80
+ module.exports = { addComponent };
@@ -0,0 +1,147 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const chalk = require('chalk');
4
+ const { execSync } = require('child_process');
5
+
6
+ async function initProject() {
7
+ try {
8
+ const userProjectRoot = process.cwd();
9
+
10
+ console.log(chalk.blue.bold('šŸš€ Initializing Purvex UI...\n'));
11
+
12
+ // 1. Check package.json
13
+ const packageJsonPath = path.join(userProjectRoot, 'package.json');
14
+ if (!fs.existsSync(packageJsonPath)) {
15
+ console.log(chalk.red('āŒ Tidak ada package.json ditemukan'));
16
+ console.log(chalk.yellow(' Buat project React dulu: npx create-vite@latest . --template react'));
17
+ return;
18
+ }
19
+
20
+ // 2. Auto install dependencies jika belum ada
21
+ console.log(chalk.blue('šŸ“¦ Checking dependencies...'));
22
+
23
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
24
+ const deps = packageJson.dependencies || {};
25
+ const devDeps = packageJson.devDependencies || {};
26
+
27
+ const missingDeps = [];
28
+
29
+ if (!deps['clsx']) missingDeps.push('clsx');
30
+ if (!deps['tailwind-merge']) missingDeps.push('tailwind-merge');
31
+
32
+ if (missingDeps.length > 0) {
33
+ console.log(chalk.yellow(`āš ļø Installing missing dependencies: ${missingDeps.join(', ')}`));
34
+ try {
35
+ execSync(`npm install ${missingDeps.join(' ')}`, { stdio: 'inherit' });
36
+ console.log(chalk.green('āœ… Dependencies installed'));
37
+ } catch (error) {
38
+ console.log(chalk.yellow('āš ļø Install dependencies manually:'));
39
+ console.log(chalk.cyan(` npm install ${missingDeps.join(' ')}`));
40
+ }
41
+ } else {
42
+ console.log(chalk.green('āœ… All dependencies already installed'));
43
+ }
44
+
45
+ // 3. Check Tailwind CSS
46
+ const tailwindConfigs = [
47
+ 'tailwind.config.js',
48
+ 'tailwind.config.ts',
49
+ 'tailwind.config.cjs',
50
+ 'tailwind.config.mjs'
51
+ ];
52
+
53
+ const hasTailwindConfig = tailwindConfigs.some(config =>
54
+ fs.existsSync(path.join(userProjectRoot, config))
55
+ );
56
+
57
+ if (!hasTailwindConfig) {
58
+ console.log(chalk.yellow('\nāš ļø Tailwind CSS not found!'));
59
+ console.log(chalk.cyan(' Installing Tailwind CSS...'));
60
+ try {
61
+ execSync('npm install -D tailwindcss postcss autoprefixer', { stdio: 'inherit' });
62
+ execSync('npx tailwindcss init -p', { stdio: 'inherit' });
63
+ console.log(chalk.green('āœ… Tailwind CSS installed'));
64
+ } catch (error) {
65
+ console.log(chalk.yellow('āš ļø Install Tailwind CSS manually:'));
66
+ console.log(chalk.cyan(' 1. npm install -D tailwindcss postcss autoprefixer'));
67
+ console.log(chalk.cyan(' 2. npx tailwindcss init -p'));
68
+ }
69
+ } else {
70
+ console.log(chalk.green('āœ… Tailwind CSS already installed'));
71
+ }
72
+
73
+ // 4. Update Tailwind config untuk include components
74
+ const tailwindConfigPath = tailwindConfigs.find(config =>
75
+ fs.existsSync(path.join(userProjectRoot, config))
76
+ );
77
+
78
+ if (tailwindConfigPath) {
79
+ try {
80
+ const configContent = fs.readFileSync(path.join(userProjectRoot, tailwindConfigPath), 'utf-8');
81
+ if (!configContent.includes('./components')) {
82
+ console.log(chalk.blue('āš™ļø Updating Tailwind config...'));
83
+ const updatedContent = configContent.replace(
84
+ /content:\s*\[([\s\S]*?)\]/,
85
+ `content: [
86
+ './index.html',
87
+ './src/**/*.{js,ts,jsx,tsx}',
88
+ './components/**/*.{js,ts,jsx,tsx}'
89
+ ]`
90
+ );
91
+ fs.writeFileSync(path.join(userProjectRoot, tailwindConfigPath), updatedContent);
92
+ console.log(chalk.green('āœ… Tailwind config updated'));
93
+ }
94
+ } catch (error) {
95
+ // Skip jika error
96
+ }
97
+ }
98
+
99
+ // 5. Create directories INSIDE src
100
+ const srcDir = path.join(userProjectRoot, 'src');
101
+ const libDir = path.join(srcDir, 'lib');
102
+ const componentsDir = path.join(srcDir, 'components', 'ui');
103
+
104
+ // Create src jika belum ada (untuk project yang tidak punya src)
105
+ if (!fs.existsSync(srcDir)) {
106
+ fs.mkdirSync(srcDir, { recursive: true });
107
+ }
108
+
109
+ // Create lib/utils.js inside src
110
+ if (!fs.existsSync(libDir)) {
111
+ fs.mkdirSync(libDir, { recursive: true });
112
+ }
113
+
114
+ const utilsPath = path.join(libDir, 'utils.js');
115
+ if (!fs.existsSync(utilsPath)) {
116
+ const utilsTemplate = `import { clsx } from 'clsx';
117
+ import { twMerge } from 'tailwind-merge';
118
+
119
+ export function cn(...inputs) {
120
+ return twMerge(clsx(inputs));
121
+ }`;
122
+
123
+ fs.writeFileSync(utilsPath, utilsTemplate);
124
+ console.log(chalk.green('āœ… Created: src/lib/utils.js'));
125
+ }
126
+
127
+ // Create components directory inside src
128
+ if (!fs.existsSync(componentsDir)) {
129
+ fs.mkdirSync(componentsDir, { recursive: true });
130
+ console.log(chalk.green('āœ… Created: src/components/ui directory'));
131
+ }
132
+
133
+ // 6. Success message
134
+ console.log(chalk.green.bold('\nšŸŽ‰ Purvex UI initialized successfully!'));
135
+ console.log(chalk.white('\nTo add components:'));
136
+ console.log(chalk.cyan(' npx purvex-ui add button'));
137
+ console.log(chalk.cyan(' npx purvex-ui add card'));
138
+
139
+ console.log(chalk.white('\nStart using:'));
140
+ console.log(chalk.gray(' import { Button } from "@/components/ui/button"'));
141
+
142
+ } catch (error) {
143
+ console.error(chalk.red('āŒ Error during init:'), error.message);
144
+ }
145
+ }
146
+
147
+ module.exports = { initProject };
@@ -0,0 +1,30 @@
1
+ const { execSync } = require('child_process');
2
+ const chalk = require('chalk');
3
+
4
+ async function installDeps() {
5
+ try {
6
+ console.log(chalk.blue.bold('šŸ“¦ Installing Purvex UI dependencies...\n'));
7
+
8
+ // Install clsx dan tailwind-merge
9
+ console.log(chalk.blue('Installing clsx and tailwind-merge...'));
10
+ execSync('npm install clsx tailwind-merge', { stdio: 'inherit' });
11
+
12
+ // Install Tailwind CSS
13
+ console.log(chalk.blue('\nInstalling Tailwind CSS...'));
14
+ execSync('npm install -D tailwindcss postcss autoprefixer', { stdio: 'inherit' });
15
+ execSync('npx tailwindcss init -p', { stdio: 'inherit' });
16
+
17
+ console.log(chalk.green.bold('\nāœ… All dependencies installed!'));
18
+ console.log(chalk.white('\nNext:'));
19
+ console.log(chalk.cyan(' npx purvex-ui init'));
20
+
21
+ } catch (error) {
22
+ console.error(chalk.red('āŒ Error installing dependencies:'), error.message);
23
+ console.log(chalk.yellow('\nInstall manually:'));
24
+ console.log(chalk.cyan(' npm install clsx tailwind-merge'));
25
+ console.log(chalk.cyan(' npm install -D tailwindcss postcss autoprefixer'));
26
+ console.log(chalk.cyan(' npx tailwindcss init -p'));
27
+ }
28
+ }
29
+
30
+ module.exports = { installDeps };
@@ -0,0 +1,482 @@
1
+ const chalk = require('chalk');
2
+ const { execSync } = require('child_process');
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ async function setupProject() {
7
+ try {
8
+ console.log(chalk.green.bold('šŸš€ Setting up Purvex UI project with Tailwind v4...\n'));
9
+
10
+ const userProjectRoot = process.cwd();
11
+
12
+ // 1. Check jika React project
13
+ const packageJsonPath = path.join(userProjectRoot, 'package.json');
14
+ if (!fs.existsSync(packageJsonPath)) {
15
+ console.log(chalk.red('āŒ Not a React project'));
16
+ console.log(chalk.yellow('Create React project first:'));
17
+ console.log(chalk.cyan(' npx create-vite@latest . --template react'));
18
+ return;
19
+ }
20
+
21
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
22
+
23
+ // 2. Install dependencies (Tailwind v4 TERBARU)
24
+ console.log(chalk.blue('šŸ“¦ Installing Purvex UI dependencies (Tailwind v4)...'));
25
+
26
+ try {
27
+ // Install clsx dan tailwind-merge
28
+ execSync('npm install clsx tailwind-merge', { stdio: 'inherit' });
29
+
30
+ // Install Tailwind v4 (VERSI TERBARU)
31
+ execSync('npm install -D tailwindcss@latest @tailwindcss/vite', { stdio: 'inherit' });
32
+
33
+ console.log(chalk.green('āœ… Dependencies installed'));
34
+ } catch (error) {
35
+ console.log(chalk.yellow('āš ļø Install manually:'));
36
+ console.log(chalk.cyan(' npm install clsx tailwind-merge'));
37
+ console.log(chalk.cyan(' npm install -D tailwindcss@latest @tailwindcss/vite'));
38
+ }
39
+
40
+ // 3. Setup untuk Tailwind v4 - TIDAK PERLU tailwind.config.js lagi
41
+ console.log(chalk.blue('āš™ļø Setting up Tailwind CSS v4...'));
42
+
43
+ const tailwindConfigPath = path.join(userProjectRoot, 'tailwind.config.js');
44
+ const postcssConfigPath = path.join(userProjectRoot, 'postcss.config.js');
45
+
46
+ // Hapus file config lama jika ada
47
+ if (fs.existsSync(tailwindConfigPath)) {
48
+ fs.unlinkSync(tailwindConfigPath);
49
+ console.log(chalk.yellow('šŸ—‘ļø Removed old tailwind.config.js (not needed for v4)'));
50
+ }
51
+
52
+ if (fs.existsSync(postcssConfigPath)) {
53
+ fs.unlinkSync(postcssConfigPath);
54
+ console.log(chalk.yellow('šŸ—‘ļø Removed old postcss.config.js (not needed for v4)'));
55
+ }
56
+
57
+ // 4. Update Vite config untuk Tailwind v4
58
+ console.log(chalk.blue('šŸ”§ Configuring Vite for Tailwind v4...'));
59
+
60
+ const viteConfigs = ['vite.config.js', 'vite.config.ts'];
61
+ let viteConfigFile = null;
62
+ let isTypeScript = false;
63
+
64
+ for (const config of viteConfigs) {
65
+ if (fs.existsSync(path.join(userProjectRoot, config))) {
66
+ viteConfigFile = config;
67
+ isTypeScript = config.endsWith('.ts');
68
+ break;
69
+ }
70
+ }
71
+
72
+ // Import untuk TypeScript atau JavaScript
73
+ const importStatement = isTypeScript ?
74
+ `import tailwindcss from '@tailwindcss/vite'` :
75
+ `import tailwindcss from '@tailwindcss/vite'`;
76
+
77
+ if (!viteConfigFile) {
78
+ // Buat vite.config.js baru untuk Tailwind v4
79
+ viteConfigFile = 'vite.config.js';
80
+ const viteConfigContent = `import { defineConfig } from 'vite'
81
+ import react from '@vitejs/plugin-react'
82
+ import tailwindcss from '@tailwindcss/vite'
83
+ import path from 'path'
84
+
85
+ export default defineConfig({
86
+ plugins: [
87
+ react(),
88
+ tailwindcss(),
89
+ ],
90
+ resolve: {
91
+ alias: {
92
+ '@': path.resolve(__dirname, './src'),
93
+ },
94
+ },
95
+ })`;
96
+
97
+ fs.writeFileSync(path.join(userProjectRoot, viteConfigFile), viteConfigContent);
98
+ console.log(chalk.green(`āœ… Created: ${viteConfigFile} with Tailwind v4`));
99
+ } else {
100
+ // Update existing vite config untuk Tailwind v4
101
+ const configPath = path.join(userProjectRoot, viteConfigFile);
102
+ let configContent = fs.readFileSync(configPath, 'utf-8');
103
+
104
+ // Tambah import @tailwindcss/vite
105
+ if (!configContent.includes("@tailwindcss/vite")) {
106
+ configContent = importStatement + '\n' + configContent;
107
+ }
108
+
109
+ // Tambahkan tailwindcss() ke plugins
110
+ if (configContent.includes('plugins:')) {
111
+ if (configContent.includes('tailwindcss(),')) {
112
+ console.log(chalk.green('āœ… Tailwind v4 already configured in Vite'));
113
+ } else {
114
+ // Tambahkan ke array plugins
115
+ configContent = configContent.replace(
116
+ /plugins:\s*\[([\s\S]*?)\]/,
117
+ `plugins: [
118
+ $1\n tailwindcss(),`
119
+ );
120
+ // Tutup bracket dengan benar
121
+ configContent = configContent.replace(/tailwindcss\(\),\s*$/, 'tailwindcss(),\n ]');
122
+ }
123
+ } else {
124
+ // Tambah section plugins baru
125
+ configContent = configContent.replace(
126
+ /export default defineConfig\({([\s\S]*?)}\)/,
127
+ `export default defineConfig({
128
+ plugins: [
129
+ react(),
130
+ tailwindcss(),
131
+ ],$1})`
132
+ );
133
+ }
134
+
135
+ // Add path alias jika belum ada
136
+ if (!configContent.includes("'@': path.resolve")) {
137
+ // Add path import jika belum ada
138
+ if (!configContent.includes("import path from 'path'")) {
139
+ configContent = configContent.replace(
140
+ /import.*from.*['"]@vitejs\/plugin-react['"]/,
141
+ `import path from 'path'\n$&`
142
+ );
143
+ }
144
+
145
+ // Add alias configuration
146
+ if (configContent.includes('resolve:')) {
147
+ // Update existing resolve
148
+ configContent = configContent.replace(
149
+ /resolve:\s*{([\s\S]*?)}/,
150
+ `resolve: {
151
+ alias: {
152
+ '@': path.resolve(__dirname, './src'),
153
+ },$1}`
154
+ );
155
+ } else {
156
+ // Add new resolve section
157
+ configContent = configContent.replace(
158
+ /export default defineConfig\({([\s\S]*?)}\)/,
159
+ `export default defineConfig({
160
+ $1
161
+ resolve: {
162
+ alias: {
163
+ '@': path.resolve(__dirname, './src'),
164
+ },
165
+ },
166
+ })`
167
+ );
168
+ }
169
+ }
170
+
171
+ fs.writeFileSync(configPath, configContent);
172
+ console.log(chalk.green(`āœ… Updated: ${viteConfigFile} for Tailwind v4`));
173
+ }
174
+
175
+ // 5. Setup CSS file dengan Tailwind v4 syntax
176
+ console.log(chalk.blue('šŸŽØ Setting up CSS with Tailwind v4...'));
177
+
178
+ const srcDir = path.join(userProjectRoot, 'src');
179
+ const indexPath = path.join(srcDir, 'index.css');
180
+
181
+ // CSS untuk Tailwind v4 - Menggunakan @import bukan @tailwind
182
+ const cssContent = `/* Purvex UI - Tailwind v4 */
183
+ @import 'tailwindcss';
184
+
185
+ @import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap');
186
+
187
+ * {
188
+ font-family: "Montserrat", sans-serif;
189
+ }
190
+
191
+ /* CSS Variables untuk Purple Elegant Theme */
192
+ @theme {
193
+ --color-background: hsl(0 0% 100%);
194
+ --color-foreground: hsl(222.2 84% 4.9%);
195
+
196
+ --color-card: hsl(0 0% 100%);
197
+ --color-card-foreground: hsl(222.2 84% 4.9%);
198
+
199
+ --color-popover: hsl(0 0% 100%);
200
+ --color-popover-foreground: hsl(222.2 84% 4.9%);
201
+
202
+ /* PRIMARY - UNGU ELEGANT #7C3AED */
203
+ --color-primary: hsl(262.1 83.3% 57.8%);
204
+ --color-primary-foreground: hsl(210 40% 98%);
205
+
206
+ --color-secondary: hsl(210 40% 96.1%);
207
+ --color-secondary-foreground: hsl(222.2 47.4% 11.2%);
208
+
209
+ --color-muted: hsl(210 40% 96.1%);
210
+ --color-muted-foreground: hsl(215.4 16.3% 46.9%);
211
+
212
+ --color-accent: hsl(210 40% 96.1%);
213
+ --color-accent-foreground: hsl(222.2 47.4% 11.2%);
214
+
215
+ --color-destructive: hsl(0 84.2% 60.2%);
216
+ --color-destructive-foreground: hsl(210 40% 98%);
217
+
218
+ --color-border: hsl(214.3 31.8% 91.4%);
219
+ --color-input: hsl(214.3 31.8% 91.4%);
220
+ --color-ring: hsl(262.1 83.3% 57.8%);
221
+
222
+ --radius: 0.5rem;
223
+
224
+ /* Dark Mode Variables */
225
+ --color-dark-background: hsl(222.2 84% 4.9%);
226
+ --color-dark-foreground: hsl(210 40% 98%);
227
+
228
+ --color-dark-card: hsl(222.2 84% 4.9%);
229
+ --color-dark-card-foreground: hsl(210 40% 98%);
230
+
231
+ --color-dark-popover: hsl(222.2 84% 4.9%);
232
+ --color-dark-popover-foreground: hsl(210 40% 98%);
233
+
234
+ --color-dark-primary: hsl(263.4 70% 50.4%);
235
+ --color-dark-primary-foreground: hsl(210 40% 98%);
236
+
237
+ --color-dark-secondary: hsl(217.2 32.6% 17.5%);
238
+ --color-dark-secondary-foreground: hsl(210 40% 98%);
239
+
240
+ --color-dark-muted: hsl(217.2 32.6% 17.5%);
241
+ --color-dark-muted-foreground: hsl(215 20.2% 65.1%);
242
+
243
+ --color-dark-accent: hsl(217.2 32.6% 17.5%);
244
+ --color-dark-accent-foreground: hsl(210 40% 98%);
245
+
246
+ --color-dark-destructive: hsl(0 62.8% 30.6%);
247
+ --color-dark-destructive-foreground: hsl(210 40% 98%);
248
+
249
+ --color-dark-border: hsl(217.2 32.6% 17.5%);
250
+ --color-dark-input: hsl(217.2 32.6% 17.5%);
251
+ --color-dark-ring: hsl(263.4 70% 50.4%);
252
+ }
253
+
254
+ /* Custom Utility Classes */
255
+ @layer utilities {
256
+ .bg-background {
257
+ background-color: var(--color-background);
258
+ }
259
+
260
+ .text-foreground {
261
+ color: var(--color-foreground);
262
+ }
263
+
264
+ .border {
265
+ border-color: var(--color-border);
266
+ }
267
+
268
+ .bg-primary {
269
+ background-color: var(--color-primary);
270
+ }
271
+
272
+ .text-primary-foreground {
273
+ color: var(--color-primary-foreground);
274
+ }
275
+
276
+ /* Dark Mode Classes */
277
+ .dark .bg-background {
278
+ background-color: var(--color-dark-background);
279
+ }
280
+
281
+ .dark .text-foreground {
282
+ color: var(--color-dark-foreground);
283
+ }
284
+
285
+ .dark .border {
286
+ border-color: var(--color-dark-border);
287
+ }
288
+
289
+ .dark .bg-primary {
290
+ background-color: var(--color-dark-primary);
291
+ }
292
+
293
+ .dark .text-primary-foreground {
294
+ color: var(--color-dark-primary-foreground);
295
+ }
296
+
297
+ /* Gradient Utilities */
298
+ .bg-gradient-purple {
299
+ background: linear-gradient(to right, #7c3aed, #6d28d9, #5b21b6);
300
+ }
301
+
302
+ .bg-gradient-purple-dark {
303
+ background: linear-gradient(to right, #1f2937, #6b21a8, #1f2937);
304
+ }
305
+
306
+ /* Border Radius Utilities */
307
+ .rounded-lg {
308
+ border-radius: var(--radius);
309
+ }
310
+
311
+ .rounded-md {
312
+ border-radius: calc(var(--radius) - 2px);
313
+ }
314
+
315
+ .rounded-sm {
316
+ border-radius: calc(var(--radius) - 4px);
317
+ }
318
+ }
319
+
320
+ /* Base Styles */
321
+ @layer base {
322
+ * {
323
+ border-color: var(--color-border);
324
+ }
325
+
326
+ body {
327
+ background-color: var(--color-background);
328
+ color: var(--color-foreground);
329
+ }
330
+
331
+ /* Dark Mode */
332
+ .dark * {
333
+ border-color: var(--color-dark-border);
334
+ }
335
+
336
+ .dark body {
337
+ background-color: var(--color-dark-background);
338
+ color: var(--color-dark-foreground);
339
+ }
340
+ }`;
341
+
342
+ if (fs.existsSync(indexPath)) {
343
+ fs.writeFileSync(indexPath, cssContent);
344
+ console.log(chalk.green('āœ… Updated: src/index.css for Tailwind v4'));
345
+ } else {
346
+ if (!fs.existsSync(srcDir)) {
347
+ fs.mkdirSync(srcDir, { recursive: true });
348
+ }
349
+ fs.writeFileSync(indexPath, cssContent);
350
+ console.log(chalk.green('āœ… Created: src/index.css for Tailwind v4'));
351
+ }
352
+
353
+ // 6. Create Purvex UI directories
354
+ const libDir = path.join(srcDir, 'lib');
355
+ const componentsDir = path.join(srcDir, 'components', 'ui');
356
+
357
+ if (!fs.existsSync(libDir)) {
358
+ fs.mkdirSync(libDir, { recursive: true });
359
+ }
360
+
361
+ if (!fs.existsSync(componentsDir)) {
362
+ fs.mkdirSync(componentsDir, { recursive: true });
363
+ }
364
+
365
+ // 7. Create utils.js
366
+ const utilsPath = path.join(libDir, 'utils.js');
367
+ const utilsTemplate = `import { clsx } from 'clsx';
368
+ import { twMerge } from 'tailwind-merge';
369
+
370
+ export function cn(...inputs) {
371
+ return twMerge(clsx(inputs));
372
+ }`;
373
+
374
+ fs.writeFileSync(utilsPath, utilsTemplate);
375
+ console.log(chalk.green('āœ… Created: src/lib/utils.js'));
376
+
377
+ // 8. Buat tailwind.config.js sederhana jika diperlukan
378
+ // (Optional untuk advanced configuration)
379
+ const simpleTailwindConfig = `/**
380
+ * Tailwind v4 Configuration
381
+ * Note: Most configuration is now done via CSS @theme directive
382
+ */
383
+
384
+ export default {
385
+ // Content configuration
386
+ content: [
387
+ './index.html',
388
+ './src/**/*.{js,ts,jsx,tsx}',
389
+ './components/**/*.{js,ts,jsx,tsx}',
390
+ ],
391
+
392
+ // Optional plugins
393
+ plugins: [],
394
+ }`;
395
+
396
+ fs.writeFileSync(path.join(userProjectRoot, 'tailwind.config.js'), simpleTailwindConfig);
397
+ console.log(chalk.yellow('šŸ“ Created optional tailwind.config.js (minimal)'));
398
+
399
+ // 9. Install @types/node untuk TypeScript support
400
+ if (fs.existsSync(path.join(userProjectRoot, 'tsconfig.json'))) {
401
+ try {
402
+ execSync('npm install -D @types/node', { stdio: 'inherit' });
403
+ console.log(chalk.green('āœ… Installed @types/node for TypeScript'));
404
+ } catch (error) {
405
+ // Skip jika error
406
+ }
407
+ }
408
+
409
+ // 10. Update package.json scripts jika perlu
410
+ try {
411
+ const pkgJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
412
+
413
+ if (!pkgJson.scripts || !pkgJson.scripts.dev) {
414
+ pkgJson.scripts = {
415
+ ...pkgJson.scripts,
416
+ dev: 'vite',
417
+ build: 'vite build',
418
+ preview: 'vite preview'
419
+ };
420
+
421
+ fs.writeFileSync(packageJsonPath, JSON.stringify(pkgJson, null, 2));
422
+ console.log(chalk.green('āœ… Updated package.json scripts'));
423
+ }
424
+ } catch (error) {
425
+ // Ignore error
426
+ }
427
+
428
+ // 11. Success message
429
+ console.log(chalk.green.bold('\nšŸŽ‰ Purvex UI setup complete with Tailwind v4!'));
430
+ console.log(chalk.white('\nāœ… Everything installed automatically:'));
431
+ console.log(chalk.gray(' • Tailwind CSS v4 (LATEST)'));
432
+ console.log(chalk.gray(' • @tailwindcss/vite plugin'));
433
+ console.log(chalk.gray(' • clsx & tailwind-merge'));
434
+ console.log(chalk.gray(' • Path alias (@/ → src/)'));
435
+ console.log(chalk.gray(' • Purple Elegant color theme (#7C3AED)'));
436
+ console.log(chalk.gray(' • CSS variables system in CSS file'));
437
+
438
+ console.log(chalk.white('\nšŸŽÆ Next steps:'));
439
+ console.log(chalk.cyan(' 1. npx purvex-ui add button'));
440
+ console.log(chalk.cyan(' 2. npx purvex-ui add card'));
441
+
442
+ console.log(chalk.white('\nšŸš€ Start development server:'));
443
+ console.log(chalk.cyan(' npm run dev'));
444
+
445
+ console.log(chalk.white('\nšŸ“ Project structure created:'));
446
+ console.log(chalk.gray(' src/components/ui/ ← Your components'));
447
+ console.log(chalk.gray(' src/lib/utils.js ← cn() utility'));
448
+ console.log(chalk.gray(' vite.config.js ← Tailwind v4 plugin configured'));
449
+ console.log(chalk.gray(' src/index.css ← Theme & Tailwind v4 imports'));
450
+
451
+ console.log(chalk.white('\nšŸŽØ Theme: Purple Elegant'));
452
+ console.log(chalk.gray(' Primary: #7C3AED (Elegant Purple)'));
453
+ console.log(chalk.gray(' Dark mode: Black + Purple'));
454
+ console.log(chalk.gray(' Includes gradient utilities'));
455
+
456
+ console.log(chalk.white('\n⚔ Tailwind v4 Features:'));
457
+ console.log(chalk.gray(' • CSS-first configuration'));
458
+ console.log(chalk.gray(' • No PostCSS needed'));
459
+ console.log(chalk.gray(' • Faster build times'));
460
+ console.log(chalk.gray(' • @theme directive in CSS'));
461
+
462
+ console.log(chalk.white('\nšŸ’” Tip: Add more components with:'));
463
+ console.log(chalk.cyan(' npx purvex-ui add [component-name]'));
464
+
465
+ console.log(chalk.yellow('\nāš ļø Important: Tailwind v4 uses different syntax!'));
466
+ console.log(chalk.gray(' - Use @import "tailwindcss" not @tailwind directives'));
467
+ console.log(chalk.gray(' - Configure theme in CSS with @theme directive'));
468
+ console.log(chalk.gray(' - No tailwind.config.js needed (minimal version created)'));
469
+
470
+ } catch (error) {
471
+ console.error(chalk.red('āŒ Setup error:'), error.message);
472
+ console.log(chalk.yellow('\nšŸ’” Manual setup steps for Tailwind v4:'));
473
+ console.log(chalk.cyan(' 1. npm install clsx tailwind-merge'));
474
+ console.log(chalk.cyan(' 2. npm install -D tailwindcss@latest @tailwindcss/vite'));
475
+ console.log(chalk.cyan(' 3. Add tailwindcss() to vite.config.js plugins'));
476
+ console.log(chalk.cyan(' 4. Add @import "tailwindcss" to CSS file'));
477
+ console.log(chalk.cyan(' 5. Add @theme directive for custom colors'));
478
+ console.log(chalk.cyan(' 6. Add path alias to vite.config.js'));
479
+ }
480
+ }
481
+
482
+ module.exports = { setupProject };
@@ -0,0 +1,130 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const chalk = require('chalk');
4
+ const { execSync } = require('child_process');
5
+
6
+ async function uninstallProject() {
7
+ try {
8
+ console.log(chalk.red.bold('šŸ—‘ļø Uninstalling Purvex UI...\n'));
9
+
10
+ const userProjectRoot = process.cwd();
11
+
12
+ // 1. Confirm
13
+ const readline = require('readline').createInterface({
14
+ input: process.stdin,
15
+ output: process.stdout
16
+ });
17
+
18
+ const answer = await new Promise((resolve) => {
19
+ readline.question(
20
+ chalk.yellow('ā“ Remove Purvex UI files and dependencies? (y/N): '),
21
+ resolve
22
+ );
23
+ });
24
+
25
+ readline.close();
26
+
27
+ if (answer.toLowerCase() !== 'y') {
28
+ console.log(chalk.gray('Uninstall cancelled'));
29
+ return;
30
+ }
31
+
32
+ console.log(chalk.blue('šŸ“ Removing Purvex UI files...'));
33
+
34
+ // 2. Hapus file/folder
35
+ const filesToRemove = [
36
+ 'src/components/ui',
37
+ 'src/lib'
38
+ ];
39
+
40
+ filesToRemove.forEach(file => {
41
+ const filePath = path.join(userProjectRoot, file);
42
+ if (fs.existsSync(filePath)) {
43
+ fs.rmSync(filePath, { recursive: true, force: true });
44
+ console.log(chalk.green(`āœ… Removed: ${file}`));
45
+ }
46
+ });
47
+
48
+ // 3. Uninstall dependencies
49
+ console.log(chalk.blue('\nšŸ“¦ Removing dependencies...'));
50
+
51
+ try {
52
+ // Uninstall regular deps
53
+ execSync('npm uninstall clsx tailwind-merge', { stdio: 'inherit' });
54
+
55
+ // Uninstall dev deps
56
+ const devDeps = ['tailwindcss', 'postcss', 'autoprefixer', '@tailwindcss/vite'];
57
+ devDeps.forEach(dep => {
58
+ try {
59
+ execSync(`npm uninstall -D ${dep}`, { stdio: 'pipe' });
60
+ } catch (e) {
61
+ // Skip jika dep tidak ada
62
+ }
63
+ });
64
+
65
+ console.log(chalk.green('āœ… Dependencies removed'));
66
+ } catch (error) {
67
+ console.log(chalk.yellow('āš ļø Remove dependencies manually:'));
68
+ console.log(chalk.cyan(' npm uninstall clsx tailwind-merge'));
69
+ console.log(chalk.cyan(' npm uninstall -D tailwindcss postcss autoprefixer @tailwindcss/vite'));
70
+ }
71
+
72
+ // 4. Optional: Remove config files
73
+ console.log(chalk.blue('\nāš™ļø Cleaning up config files...'));
74
+
75
+ const configFiles = [
76
+ 'tailwind.config.js',
77
+ 'tailwind.config.ts',
78
+ 'tailwind.config.cjs',
79
+ 'postcss.config.js',
80
+ 'postcss.config.cjs'
81
+ ];
82
+
83
+ configFiles.forEach(config => {
84
+ const configPath = path.join(userProjectRoot, config);
85
+ if (fs.existsSync(configPath)) {
86
+ const answer2 = require('readline-sync').question(
87
+ chalk.yellow(`Remove ${config}? (y/N): `)
88
+ );
89
+
90
+ if (answer2.toLowerCase() === 'y') {
91
+ fs.unlinkSync(configPath);
92
+ console.log(chalk.green(`āœ… Removed: ${config}`));
93
+ }
94
+ }
95
+ });
96
+
97
+ // 5. Revert vite.config.js
98
+ const viteConfigs = ['vite.config.js', 'vite.config.ts'];
99
+ viteConfigs.forEach(config => {
100
+ const configPath = path.join(userProjectRoot, config);
101
+ if (fs.existsSync(configPath)) {
102
+ console.log(chalk.yellow(`\nāš ļø Need to manually revert ${config}:`));
103
+ console.log(chalk.cyan(' Remove tailwindcss() plugin from vite.config.js'));
104
+ console.log(chalk.cyan(' Remove @tailwindcss/vite import if exists'));
105
+ }
106
+ });
107
+
108
+ // 6. Revert index.css
109
+ const cssFiles = ['src/index.css', 'src/App.css', 'src/main.css'];
110
+ cssFiles.forEach(css => {
111
+ const cssPath = path.join(userProjectRoot, css);
112
+ if (fs.existsSync(cssPath)) {
113
+ let cssContent = fs.readFileSync(cssPath, 'utf-8');
114
+ if (cssContent.includes('@import "tailwindcss"')) {
115
+ cssContent = cssContent.replace(/@import "tailwindcss";?\n?/, '');
116
+ fs.writeFileSync(cssPath, cssContent);
117
+ console.log(chalk.green(`āœ… Cleaned: ${css}`));
118
+ }
119
+ }
120
+ });
121
+
122
+ console.log(chalk.green.bold('\nšŸŽ‰ Purvex UI successfully uninstalled!'));
123
+ console.log(chalk.white('\nProject restored to pre-Purvex UI state.'));
124
+
125
+ } catch (error) {
126
+ console.error(chalk.red('āŒ Uninstall error:'), error.message);
127
+ }
128
+ }
129
+
130
+ module.exports = { uninstallProject };
package/src/index.js ADDED
@@ -0,0 +1,126 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { Command } = require('commander');
4
+ const chalk = require('chalk');
5
+ const boxen = require('boxen');
6
+
7
+ // Banner
8
+ const banner = boxen.default(
9
+ chalk.green.bold('✨ Purvex UI') +
10
+ chalk.white(' - React Component Generator\n') +
11
+ chalk.gray('Fast, customizable UI components for React + Tailwind'),
12
+ {
13
+ padding: 1,
14
+ margin: 1,
15
+ borderStyle: 'round',
16
+ borderColor: 'cyan'
17
+ }
18
+ );
19
+
20
+ console.log(banner);
21
+
22
+ // Setup CLI
23
+ const program = new Command();
24
+
25
+ program
26
+ .name('purvex-ui')
27
+ .description('CLI untuk Purvex UI - Generator komponen React dengan Tailwind CSS')
28
+ .version('0.1.0');
29
+
30
+ // Command: init
31
+ program
32
+ .command('init')
33
+ .description('Initialize Purvex UI di project kamu')
34
+ .action(async () => {
35
+ const { initProject } = require('./commands/init');
36
+ await initProject();
37
+ });
38
+
39
+ // Command: add
40
+ program
41
+ .command('add <component>')
42
+ .description('Tambahkan komponen ke project')
43
+ .option('-p, --path <path>', 'Custom path untuk komponen (default: components/ui)', 'components/ui')
44
+ .action(async (component, options) => {
45
+ const { addComponent } = require('./commands/add');
46
+ await addComponent(component, options);
47
+ });
48
+
49
+ // Command: list
50
+ program
51
+ .command('list')
52
+ .description('Lihat semua komponen yang tersedia')
53
+ .action(async () => {
54
+ const fs = require('fs');
55
+ const path = require('path');
56
+
57
+ try {
58
+ const registryPath = path.join(__dirname, '..', '..', '..', 'registry', 'components.json');
59
+
60
+ if (!fs.existsSync(registryPath)) {
61
+ console.log(chalk.red('āŒ Registry tidak ditemukan'));
62
+ return;
63
+ }
64
+
65
+ const registryContent = fs.readFileSync(registryPath, 'utf-8');
66
+ const registry = JSON.parse(registryContent);
67
+
68
+ console.log(chalk.blue.bold('\nšŸ“¦ Komponen yang tersedia:\n'));
69
+ registry.components.forEach(comp => {
70
+ console.log(chalk.cyan(` • ${comp.name}`));
71
+ });
72
+ console.log(chalk.gray(`\nTotal: ${registry.components.length} komponen`));
73
+
74
+ } catch (error) {
75
+ console.error(chalk.red('āŒ Error:'), error.message);
76
+ }
77
+ });
78
+
79
+
80
+ // Command: setup
81
+ program
82
+ .command('setup')
83
+ .description('Setup Purvex UI with Tailwind v4 and Purple Elegant theme')
84
+ .action(async () => {
85
+ const { setupProject } = require('./commands/setup');
86
+ await setupProject();
87
+ });
88
+
89
+ // Command: fix
90
+ program
91
+ .command('fix')
92
+ .description('Fix Purvex UI configuration (path alias, colors, etc)')
93
+ .action(async () => {
94
+ const { fixProject } = require('./commands/fix');
95
+ await fixProject();
96
+ });
97
+
98
+ // Command: update-colors
99
+ // program
100
+ // .command('update-colors')
101
+ // .description('Update color palette to Purple Elegant theme')
102
+ // .action(async () => {
103
+ // const { updateColors } = require('./commands/update-colors');
104
+ // await updateColors();
105
+ // });
106
+
107
+ // Command: install
108
+ program
109
+ .command('install')
110
+ .description('Install all required dependencies automatically')
111
+ .action(async () => {
112
+ const { installDeps } = require('./commands/install');
113
+ await installDeps();
114
+ });
115
+
116
+ // Command: uninstall
117
+ program
118
+ .command('uninstall')
119
+ .description('Remove Purvex UI from your project')
120
+ .action(async () => {
121
+ const { uninstallProject } = require('./commands/uninstall');
122
+ await uninstallProject();
123
+ });
124
+
125
+ // Parse arguments
126
+ program.parse();