pejay-ui 1.3.3 → 1.3.5
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 +83 -133
- package/bin/cli.js +247 -5
- package/package.json +1 -1
- package/registry.json +4 -4
package/README.md
CHANGED
|
@@ -1,165 +1,115 @@
|
|
|
1
|
-
# pejay-ui
|
|
1
|
+
# pejay-ui
|
|
2
2
|
|
|
3
3
|
A lightweight CLI tool to initialize, add, and remove React UI components in your projects.
|
|
4
4
|
|
|
5
|
-
## Commands
|
|
5
|
+
## Core Commands
|
|
6
6
|
|
|
7
7
|
### 1. Initialize Configuration
|
|
8
|
-
Initialize the configuration file `pejay-ui.json` in the root of your project.
|
|
9
8
|
```bash
|
|
10
9
|
npx pejay-ui init
|
|
11
10
|
```
|
|
12
11
|
|
|
13
12
|
### 2. Add Component
|
|
14
|
-
Download and install a component, automatically setting up its utilities (like `cn`) and resolving component-to-component dependencies.
|
|
15
13
|
```bash
|
|
16
|
-
npx pejay-ui add <component-name>
|
|
14
|
+
npx pejay-ui add <component-name-or-category> [options]
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Options:**
|
|
18
|
+
- `--all`: Install all components in the specified category.
|
|
19
|
+
- `--select`: Interactively select which components to install from the specified category.
|
|
20
|
+
|
|
21
|
+
**Examples:**
|
|
22
|
+
- Install a single component directly:
|
|
23
|
+
```bash
|
|
24
|
+
npx pejay-ui add form/input
|
|
25
|
+
```
|
|
26
|
+
- Install all components in a category (e.g., `form`):
|
|
27
|
+
```bash
|
|
28
|
+
npx pejay-ui add form --all
|
|
29
|
+
```
|
|
30
|
+
- Select specific components to install from a category (e.g., `form`):
|
|
31
|
+
```bash
|
|
32
|
+
npx pejay-ui add form --select
|
|
33
|
+
```
|
|
34
|
+
*(Note: Running `npx pejay-ui add <category>` without options will default to the interactive selection prompt).*
|
|
35
|
+
|
|
36
|
+
**Automatic Exports (Auto-Indexing):**
|
|
37
|
+
Installing components automatically generates or updates `index.ts` (or `index.js`) files at:
|
|
38
|
+
1. The category level (e.g., `src/pejay-ui/components/form/index.ts`)
|
|
39
|
+
2. The global components level (`src/pejay-ui/components/index.ts`)
|
|
40
|
+
|
|
41
|
+
This allows you to easily import multiple components:
|
|
42
|
+
```typescript
|
|
43
|
+
import { Input, Checkbox, AmountInput } from "@/pejay-ui/components";
|
|
17
44
|
```
|
|
18
|
-
*Example:* `npx pejay-ui add button` or `npx pejay-ui add form/date-picker`
|
|
19
45
|
|
|
20
46
|
### 3. Remove Component
|
|
21
|
-
Safely delete a component's files, and clean up any unused utilities or package dependencies that were installed with it.
|
|
22
47
|
```bash
|
|
23
48
|
npx pejay-ui remove <component-name>
|
|
24
49
|
```
|
|
25
|
-
*Example:* `npx pejay-ui remove button`
|
|
26
50
|
|
|
27
|
-
|
|
51
|
+
### 4. Check Components Status
|
|
52
|
+
```bash
|
|
53
|
+
npx pejay-ui status
|
|
54
|
+
```
|
|
28
55
|
|
|
29
|
-
|
|
56
|
+
Lists all available components categorized, showing their local installation status:
|
|
57
|
+
- Installed components are marked with a green `[✔]`.
|
|
58
|
+
- Uninstalled components are marked with `[ ]`.
|
|
30
59
|
|
|
31
|
-
|
|
60
|
+
---
|
|
32
61
|
|
|
33
|
-
|
|
62
|
+
## Available Components & Scaffolds
|
|
34
63
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
64
|
+
### Buttons
|
|
65
|
+
```bash
|
|
66
|
+
npx pejay-ui add button
|
|
67
|
+
```
|
|
39
68
|
|
|
40
69
|
### Form Inputs
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
* **`form/email-input`**: Dedicated input for email addresses with prefix icon.
|
|
59
|
-
```bash
|
|
60
|
-
npx pejay-ui add form/email-input
|
|
61
|
-
```
|
|
62
|
-
* **`form/file-input`**: Premium dropzone-style file upload component supporting drag-and-drop and progress/preview states.
|
|
63
|
-
```bash
|
|
64
|
-
npx pejay-ui add form/file-input
|
|
65
|
-
```
|
|
66
|
-
* **`form/number-input`**: Input field for numerical values with increment/decrement steppers.
|
|
67
|
-
```bash
|
|
68
|
-
npx pejay-ui add form/number-input
|
|
69
|
-
```
|
|
70
|
-
* **`form/password-input`**: Secure text input with eye icon toggle to show/hide the password.
|
|
71
|
-
```bash
|
|
72
|
-
npx pejay-ui add form/password-input
|
|
73
|
-
```
|
|
74
|
-
* **`form/phone-input`**: Formatted input field for telephone numbers.
|
|
75
|
-
```bash
|
|
76
|
-
npx pejay-ui add form/phone-input
|
|
77
|
-
```
|
|
78
|
-
* **`form/radio`**: Styled radio selection dot.
|
|
79
|
-
```bash
|
|
80
|
-
npx pejay-ui add form/radio
|
|
81
|
-
```
|
|
82
|
-
* **`form/radio-group`**: Group of mutually exclusive radio options.
|
|
83
|
-
```bash
|
|
84
|
-
npx pejay-ui add form/radio-group
|
|
85
|
-
```
|
|
86
|
-
* **`form/range-slider`**: Slider controls for choosing values from a numeric range.
|
|
87
|
-
```bash
|
|
88
|
-
npx pejay-ui add form/range-slider
|
|
89
|
-
```
|
|
90
|
-
* **`form/switch`**: Styled toggle switch representing boolean options.
|
|
91
|
-
```bash
|
|
92
|
-
npx pejay-ui add form/switch
|
|
93
|
-
```
|
|
94
|
-
* **`form/textarea`**: Multiline text area input with character counter/limit indicators.
|
|
95
|
-
```bash
|
|
96
|
-
npx pejay-ui add form/textarea
|
|
97
|
-
```
|
|
98
|
-
* **`form/url-input`**: Styled input field specifically formatted for web addresses/links.
|
|
99
|
-
```bash
|
|
100
|
-
npx pejay-ui add form/url-input
|
|
101
|
-
```
|
|
70
|
+
```bash
|
|
71
|
+
npx pejay-ui add form/input
|
|
72
|
+
npx pejay-ui add form/amount-input
|
|
73
|
+
npx pejay-ui add form/checkbox
|
|
74
|
+
npx pejay-ui add form/checkbox-group
|
|
75
|
+
npx pejay-ui add form/email-input
|
|
76
|
+
npx pejay-ui add form/file-input
|
|
77
|
+
npx pejay-ui add form/number-input
|
|
78
|
+
npx pejay-ui add form/password-input
|
|
79
|
+
npx pejay-ui add form/phone-input
|
|
80
|
+
npx pejay-ui add form/radio
|
|
81
|
+
npx pejay-ui add form/radio-group
|
|
82
|
+
npx pejay-ui add form/range-slider
|
|
83
|
+
npx pejay-ui add form/switch
|
|
84
|
+
npx pejay-ui add form/textarea
|
|
85
|
+
npx pejay-ui add form/url-input
|
|
86
|
+
```
|
|
102
87
|
|
|
103
88
|
### Date & Time Pickers
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
```bash
|
|
111
|
-
npx pejay-ui add form/date-range-picker
|
|
112
|
-
```
|
|
113
|
-
* **`form/time-picker`**: Dropdown component for picking specific hours, minutes, and AM/PM.
|
|
114
|
-
```bash
|
|
115
|
-
npx pejay-ui add form/time-picker
|
|
116
|
-
```
|
|
117
|
-
* **`form/time-range-picker`**: Popover time selector for configuring custom duration intervals.
|
|
118
|
-
```bash
|
|
119
|
-
npx pejay-ui add form/time-range-picker
|
|
120
|
-
```
|
|
89
|
+
```bash
|
|
90
|
+
npx pejay-ui add form/date-picker
|
|
91
|
+
npx pejay-ui add form/date-range-picker
|
|
92
|
+
npx pejay-ui add form/time-picker
|
|
93
|
+
npx pejay-ui add form/time-range-picker
|
|
94
|
+
```
|
|
121
95
|
|
|
122
96
|
### Dropdowns & Selects
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
```
|
|
128
|
-
* **`dropdown/multiselect-input`**: Searchable multiselect dropdown which renders selected options as dismissible tag pills.
|
|
129
|
-
```bash
|
|
130
|
-
npx pejay-ui add dropdown/multiselect-input
|
|
131
|
-
```
|
|
97
|
+
```bash
|
|
98
|
+
npx pejay-ui add dropdown/select-input
|
|
99
|
+
npx pejay-ui add dropdown/multiselect-input
|
|
100
|
+
```
|
|
132
101
|
|
|
133
102
|
### Layouts
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
npx pejay-ui add layouts/lv1
|
|
138
|
-
```
|
|
103
|
+
```bash
|
|
104
|
+
npx pejay-ui add layouts/lv1
|
|
105
|
+
```
|
|
139
106
|
|
|
140
107
|
### Scaffolds & Templates
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
```
|
|
150
|
-
* **`tanstack-router-client`**: TanStack Router setup with layouts, route guards, and file-based route stubs, copied into `src/tanstack-router/`.
|
|
151
|
-
```bash
|
|
152
|
-
npx pejay-ui add tanstack-router-client
|
|
153
|
-
```
|
|
154
|
-
* **`axios-client`**: Axios instance with interceptors, request helpers, and a sample API module, copied into `src/axios/`.
|
|
155
|
-
```bash
|
|
156
|
-
npx pejay-ui add axios-client
|
|
157
|
-
```
|
|
158
|
-
* **`redux-store-client`**: Redux Toolkit store setup with `redux-persist`, reducers, slices, and selectors, copied into `src/redux-store/`.
|
|
159
|
-
```bash
|
|
160
|
-
npx pejay-ui add redux-store-client
|
|
161
|
-
```
|
|
162
|
-
* **`rtk-query-client`**: RTK Query base API with `fetchBaseQuery`, tag management, middleware, and a sample endpoint, copied into `src/rtk-query/`.
|
|
163
|
-
```bash
|
|
164
|
-
npx pejay-ui add rtk-query-client
|
|
165
|
-
```
|
|
108
|
+
```bash
|
|
109
|
+
npx pejay-ui add tanstack-query-client
|
|
110
|
+
npx pejay-ui add react-router-client
|
|
111
|
+
npx pejay-ui add tanstack-router-client
|
|
112
|
+
npx pejay-ui add axios-client
|
|
113
|
+
npx pejay-ui add redux-store-client
|
|
114
|
+
npx pejay-ui add rtk-query-client
|
|
115
|
+
```
|
package/bin/cli.js
CHANGED
|
@@ -74,7 +74,9 @@ program
|
|
|
74
74
|
program
|
|
75
75
|
.command("add <component>")
|
|
76
76
|
.description("Add a component to your project")
|
|
77
|
-
.
|
|
77
|
+
.option("--all", "Add all components in the category")
|
|
78
|
+
.option("--select", "Select specific components from the category to add")
|
|
79
|
+
.action(async (component, options) => {
|
|
78
80
|
try {
|
|
79
81
|
const cwd = process.cwd();
|
|
80
82
|
const configPath = path.join(cwd, "pejay-ui.json");
|
|
@@ -95,6 +97,46 @@ program
|
|
|
95
97
|
const registry = await fs.readJSON(registryPath);
|
|
96
98
|
const isTsProject = await fs.pathExists(path.join(cwd, "tsconfig.json"));
|
|
97
99
|
|
|
100
|
+
// Determine which components to install
|
|
101
|
+
let selectedComponents = [];
|
|
102
|
+
|
|
103
|
+
// Check if it's a category
|
|
104
|
+
const categoryComponents = Object.keys(registry).filter(
|
|
105
|
+
(key) => registry[key].category === component
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
if (categoryComponents.length > 0) {
|
|
109
|
+
if (options.all) {
|
|
110
|
+
selectedComponents = categoryComponents;
|
|
111
|
+
} else {
|
|
112
|
+
// Dynamic prompt using inquirer checkbox for --select or when no flag is specified for category
|
|
113
|
+
const answers = await prompt([
|
|
114
|
+
{
|
|
115
|
+
type: "checkbox",
|
|
116
|
+
name: "components",
|
|
117
|
+
message: `Select components from category "${component}" to add:`,
|
|
118
|
+
choices: categoryComponents.map((key) => ({
|
|
119
|
+
name: `${registry[key].name} (${key})`,
|
|
120
|
+
value: key,
|
|
121
|
+
})),
|
|
122
|
+
},
|
|
123
|
+
]);
|
|
124
|
+
selectedComponents = answers.components;
|
|
125
|
+
if (selectedComponents.length === 0) {
|
|
126
|
+
console.log("No components selected. Exiting.");
|
|
127
|
+
process.exit(0);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
} else {
|
|
131
|
+
// Not a category, treat as a single component key
|
|
132
|
+
if (!registry[component]) {
|
|
133
|
+
console.error(`Error: Component or Category '${component}' not found in registry.`);
|
|
134
|
+
console.log(`Available categories/components: ${Array.from(new Set(Object.values(registry).map(c => c.category).filter(Boolean))).join(", ")} or ${Object.keys(registry).join(", ")}`);
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
137
|
+
selectedComponents = [component];
|
|
138
|
+
}
|
|
139
|
+
|
|
98
140
|
// Track all components to install (including dependencies) in topological/order of dependencies
|
|
99
141
|
const installQueue = [];
|
|
100
142
|
const visited = new Set();
|
|
@@ -106,7 +148,6 @@ program
|
|
|
106
148
|
const compData = registry[compName];
|
|
107
149
|
if (!compData) {
|
|
108
150
|
console.error(`Error: Component '${compName}' not found in registry.`);
|
|
109
|
-
console.log(`Available components: ${Object.keys(registry).join(", ")}`);
|
|
110
151
|
process.exit(1);
|
|
111
152
|
}
|
|
112
153
|
|
|
@@ -120,13 +161,15 @@ program
|
|
|
120
161
|
installQueue.push(compName);
|
|
121
162
|
};
|
|
122
163
|
|
|
123
|
-
|
|
164
|
+
for (const comp of selectedComponents) {
|
|
165
|
+
resolveDependencies(comp);
|
|
166
|
+
}
|
|
124
167
|
|
|
125
168
|
console.log("\n🚀 Starting installation...\n");
|
|
126
169
|
|
|
127
170
|
for (const compToInstall of installQueue) {
|
|
128
|
-
// Skip if already marked as installed in config (unless it is the
|
|
129
|
-
if (config.installed?.[compToInstall] && compToInstall
|
|
171
|
+
// Skip if already marked as installed in config (unless it is one of the explicitly requested components)
|
|
172
|
+
if (config.installed?.[compToInstall] && !selectedComponents.includes(compToInstall)) {
|
|
130
173
|
console.log(`Component '${compToInstall}' is already installed. Skipping dependency installation.`);
|
|
131
174
|
continue;
|
|
132
175
|
}
|
|
@@ -293,6 +336,56 @@ program
|
|
|
293
336
|
}
|
|
294
337
|
}
|
|
295
338
|
|
|
339
|
+
// 3.5 Automatically generate/update category-level and global index.ts/index.js files
|
|
340
|
+
if (componentData.category && !["scaffold", "scaffolds", "script", "scripts"].includes(componentData.category.toLowerCase())) {
|
|
341
|
+
const indexExt = isTsProject ? "ts" : "js";
|
|
342
|
+
const filesInDir = await fs.readdir(targetDir);
|
|
343
|
+
const exportableFiles = [];
|
|
344
|
+
|
|
345
|
+
for (const file of filesInDir) {
|
|
346
|
+
const filePath = path.join(targetDir, file);
|
|
347
|
+
const stat = await fs.stat(filePath);
|
|
348
|
+
if (stat.isFile()) {
|
|
349
|
+
const ext = path.extname(file);
|
|
350
|
+
const name = path.basename(file, ext);
|
|
351
|
+
if ((ext === ".tsx" || ext === ".ts" || ext === ".jsx" || ext === ".js") && name !== "index") {
|
|
352
|
+
exportableFiles.push(name);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (exportableFiles.length > 0) {
|
|
358
|
+
exportableFiles.sort();
|
|
359
|
+
const indexFilePath = path.join(targetDir, `index.${indexExt}`);
|
|
360
|
+
const exportLines = exportableFiles.map(name => `export * from "./${name}";`);
|
|
361
|
+
await fs.writeFile(indexFilePath, exportLines.join("\n") + "\n", "utf-8");
|
|
362
|
+
console.log(`✅ Updated index.${indexExt} in ${path.relative(cwd, targetDir)}`);
|
|
363
|
+
|
|
364
|
+
// Also update the global components index file
|
|
365
|
+
const componentsDir = path.dirname(targetDir); // src/pejay-ui/components
|
|
366
|
+
if (await fs.pathExists(componentsDir)) {
|
|
367
|
+
const categories = await fs.readdir(componentsDir);
|
|
368
|
+
const validCategories = [];
|
|
369
|
+
for (const cat of categories) {
|
|
370
|
+
const catDir = path.join(componentsDir, cat);
|
|
371
|
+
const catStat = await fs.stat(catDir);
|
|
372
|
+
if (catStat.isDirectory()) {
|
|
373
|
+
if (await fs.pathExists(path.join(catDir, `index.ts`)) || await fs.pathExists(path.join(catDir, `index.js`))) {
|
|
374
|
+
validCategories.push(cat);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
if (validCategories.length > 0) {
|
|
379
|
+
validCategories.sort();
|
|
380
|
+
const globalIndexFile = path.join(componentsDir, `index.${indexExt}`);
|
|
381
|
+
const globalExportLines = validCategories.map(cat => `export * from "./${cat}";`);
|
|
382
|
+
await fs.writeFile(globalIndexFile, globalExportLines.join("\n") + "\n", "utf-8");
|
|
383
|
+
console.log(`✅ Updated global index.${indexExt} in ${path.relative(cwd, componentsDir)}`);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
296
389
|
// 4. Update State tracking in config
|
|
297
390
|
config.installed = config.installed || {};
|
|
298
391
|
config.installed[compToInstall] = {
|
|
@@ -358,6 +451,73 @@ program
|
|
|
358
451
|
}
|
|
359
452
|
}
|
|
360
453
|
|
|
454
|
+
// 1.5 Update index files
|
|
455
|
+
if (componentData.category && !["scaffold", "scaffolds", "script", "scripts"].includes(componentData.category.toLowerCase())) {
|
|
456
|
+
const isTsProject = await fs.pathExists(path.join(cwd, "tsconfig.json"));
|
|
457
|
+
const targetDir = path.join(cwd, config.baseDir, "components", componentData.category);
|
|
458
|
+
const indexExt = isTsProject ? "ts" : "js";
|
|
459
|
+
|
|
460
|
+
if (await fs.pathExists(targetDir)) {
|
|
461
|
+
const filesInDir = await fs.readdir(targetDir);
|
|
462
|
+
const exportableFiles = [];
|
|
463
|
+
|
|
464
|
+
for (const file of filesInDir) {
|
|
465
|
+
const filePath = path.join(targetDir, file);
|
|
466
|
+
const stat = await fs.stat(filePath);
|
|
467
|
+
if (stat.isFile()) {
|
|
468
|
+
const ext = path.extname(file);
|
|
469
|
+
const name = path.basename(file, ext);
|
|
470
|
+
if ((ext === ".tsx" || ext === ".ts" || ext === ".jsx" || ext === ".js") && name !== "index") {
|
|
471
|
+
exportableFiles.push(name);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
const indexFilePath = path.join(targetDir, `index.${indexExt}`);
|
|
477
|
+
if (exportableFiles.length > 0) {
|
|
478
|
+
exportableFiles.sort();
|
|
479
|
+
const exportLines = exportableFiles.map(name => `export * from "./${name}";`);
|
|
480
|
+
await fs.writeFile(indexFilePath, exportLines.join("\n") + "\n", "utf-8");
|
|
481
|
+
console.log(`✅ Updated index.${indexExt} in ${path.relative(cwd, targetDir)}`);
|
|
482
|
+
} else {
|
|
483
|
+
// No files left, delete the category index file
|
|
484
|
+
if (await fs.pathExists(indexFilePath)) {
|
|
485
|
+
await fs.remove(indexFilePath);
|
|
486
|
+
console.log(`🗑️ Removed empty index.${indexExt} in ${path.relative(cwd, targetDir)}`);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// Also update the global components index file
|
|
491
|
+
const componentsDir = path.dirname(targetDir);
|
|
492
|
+
if (await fs.pathExists(componentsDir)) {
|
|
493
|
+
const categories = await fs.readdir(componentsDir);
|
|
494
|
+
const validCategories = [];
|
|
495
|
+
for (const cat of categories) {
|
|
496
|
+
const catDir = path.join(componentsDir, cat);
|
|
497
|
+
const catStat = await fs.stat(catDir);
|
|
498
|
+
if (catStat.isDirectory()) {
|
|
499
|
+
if (await fs.pathExists(path.join(catDir, `index.ts`)) || await fs.pathExists(path.join(catDir, `index.js`))) {
|
|
500
|
+
validCategories.push(cat);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
const globalIndexFile = path.join(componentsDir, `index.${indexExt}`);
|
|
505
|
+
if (validCategories.length > 0) {
|
|
506
|
+
validCategories.sort();
|
|
507
|
+
const globalExportLines = validCategories.map(cat => `export * from "./${cat}";`);
|
|
508
|
+
await fs.writeFile(globalIndexFile, globalExportLines.join("\n") + "\n", "utf-8");
|
|
509
|
+
console.log(`✅ Updated global index.${indexExt} in ${path.relative(cwd, componentsDir)}`);
|
|
510
|
+
} else {
|
|
511
|
+
// No categories left with index files, remove the global index
|
|
512
|
+
if (await fs.pathExists(globalIndexFile)) {
|
|
513
|
+
await fs.remove(globalIndexFile);
|
|
514
|
+
console.log(`🗑️ Removed empty global index.${indexExt} in ${path.relative(cwd, componentsDir)}`);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
361
521
|
// 2. Build Utility Usage Map
|
|
362
522
|
const utilityUsage = {};
|
|
363
523
|
for (const [compName, compInfo] of Object.entries(config.installed)) {
|
|
@@ -452,4 +612,86 @@ program
|
|
|
452
612
|
}
|
|
453
613
|
});
|
|
454
614
|
|
|
615
|
+
/* =============================
|
|
616
|
+
STATUS COMMAND
|
|
617
|
+
============================= */
|
|
618
|
+
program
|
|
619
|
+
.command("status")
|
|
620
|
+
.description("List all available components and check their installation status")
|
|
621
|
+
.action(async () => {
|
|
622
|
+
try {
|
|
623
|
+
const cwd = process.cwd();
|
|
624
|
+
const configPath = path.join(cwd, "pejay-ui.json");
|
|
625
|
+
|
|
626
|
+
let config = { installed: {} };
|
|
627
|
+
let hasConfig = true;
|
|
628
|
+
|
|
629
|
+
if (!await fs.pathExists(configPath)) {
|
|
630
|
+
hasConfig = false;
|
|
631
|
+
} else {
|
|
632
|
+
try {
|
|
633
|
+
config = await fs.readJSON(configPath);
|
|
634
|
+
} catch (e) {
|
|
635
|
+
hasConfig = false;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
const registryPath = path.join(packageRoot, "registry.json");
|
|
640
|
+
if (!await fs.pathExists(registryPath)) {
|
|
641
|
+
console.error("Error: Registry configuration not found.");
|
|
642
|
+
process.exit(1);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
const registry = await fs.readJSON(registryPath);
|
|
646
|
+
|
|
647
|
+
// Terminal styling colors
|
|
648
|
+
const GREEN = "\x1b[32m";
|
|
649
|
+
const CYAN = "\x1b[36m";
|
|
650
|
+
const DIM = "\x1b[2m";
|
|
651
|
+
const RESET = "\x1b[0m";
|
|
652
|
+
const YELLOW = "\x1b[33m";
|
|
653
|
+
|
|
654
|
+
console.log(`\n🔍 ${CYAN}pejay-ui Components Status:${RESET}\n`);
|
|
655
|
+
|
|
656
|
+
if (!hasConfig) {
|
|
657
|
+
console.log(`${YELLOW}Note: pejay-ui.json not found. Initialize first via 'npx pejay-ui init'.${RESET}`);
|
|
658
|
+
console.log(`${YELLOW}Showing all components as uninstalled.${RESET}\n`);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// Group registry items by category
|
|
662
|
+
const categories = {};
|
|
663
|
+
for (const [key, compData] of Object.entries(registry)) {
|
|
664
|
+
const category = compData.category || "other";
|
|
665
|
+
if (!categories[category]) {
|
|
666
|
+
categories[category] = [];
|
|
667
|
+
}
|
|
668
|
+
categories[category].push({
|
|
669
|
+
key,
|
|
670
|
+
name: compData.name,
|
|
671
|
+
installed: !!config.installed?.[key]
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
// Print categories and components sorted alphabetically
|
|
676
|
+
const sortedCategoryNames = Object.keys(categories).sort();
|
|
677
|
+
for (const cat of sortedCategoryNames) {
|
|
678
|
+
console.log(`${CYAN}Category: ${cat}${RESET}`);
|
|
679
|
+
const comps = categories[cat];
|
|
680
|
+
comps.sort((a, b) => a.name.localeCompare(b.name));
|
|
681
|
+
|
|
682
|
+
for (const comp of comps) {
|
|
683
|
+
if (comp.installed) {
|
|
684
|
+
console.log(` ${GREEN}[✔] ${comp.name}${RESET} ${DIM}(${comp.key})${RESET}`);
|
|
685
|
+
} else {
|
|
686
|
+
console.log(` [ ] ${comp.name} ${DIM}(${comp.key})${RESET}`);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
console.log(); // blank line between categories
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
} catch (err) {
|
|
693
|
+
console.error("\n❌ Status display failed\n", err);
|
|
694
|
+
}
|
|
695
|
+
});
|
|
696
|
+
|
|
455
697
|
program.parse();
|
package/package.json
CHANGED
package/registry.json
CHANGED
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"lucide-react",
|
|
47
47
|
"@floating-ui/react"
|
|
48
48
|
],
|
|
49
|
-
"dependencies": ["
|
|
49
|
+
"dependencies": ["dropdown/select-input"]
|
|
50
50
|
},
|
|
51
51
|
"form/date-range-picker": {
|
|
52
52
|
"name": "DateRangePicker",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"lucide-react",
|
|
60
60
|
"@floating-ui/react"
|
|
61
61
|
],
|
|
62
|
-
"dependencies": ["
|
|
62
|
+
"dependencies": ["dropdown/select-input"]
|
|
63
63
|
},
|
|
64
64
|
"form/email-input": {
|
|
65
65
|
"name": "EmailInput",
|
|
@@ -143,7 +143,7 @@
|
|
|
143
143
|
"lucide-react",
|
|
144
144
|
"@floating-ui/react"
|
|
145
145
|
],
|
|
146
|
-
"dependencies": ["
|
|
146
|
+
"dependencies": ["dropdown/select-input"]
|
|
147
147
|
},
|
|
148
148
|
"form/time-range-picker": {
|
|
149
149
|
"name": "TimeRangePicker",
|
|
@@ -156,7 +156,7 @@
|
|
|
156
156
|
"lucide-react",
|
|
157
157
|
"@floating-ui/react"
|
|
158
158
|
],
|
|
159
|
-
"dependencies": ["
|
|
159
|
+
"dependencies": ["dropdown/select-input"]
|
|
160
160
|
},
|
|
161
161
|
"form/url-input": {
|
|
162
162
|
"name": "UrlInput",
|