@viliha/vui-ui 1.3.0 → 1.4.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/AGENT.md +15 -8
- package/README.md +29 -18
- package/bin/vui.mjs +135 -108
- package/package.json +1 -1
package/AGENT.md
CHANGED
|
@@ -73,16 +73,23 @@ The package ships the components; the **app shell** (layout, sidebar, open tabs,
|
|
|
73
73
|
command palette, nav config, logo) and **demo pages** are scaffolded with:
|
|
74
74
|
|
|
75
75
|
```bash
|
|
76
|
-
npx @viliha/vui-ui init # interactive
|
|
76
|
+
npx @viliha/vui-ui init # interactive decision tree
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
-
Files land in the consumer's repo (they own them).
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
just
|
|
79
|
+
Files land in the consumer's repo (they own them). Two questions:
|
|
80
|
+
1. **Fresh project?** (`--fresh` / `--existing`)
|
|
81
|
+
2. **Pre-built theme?** (`--prebuilt` = shell + demo pages / `--theme-only` = just
|
|
82
|
+
the theme wiring you configure)
|
|
83
|
+
|
|
84
|
+
- **fresh + prebuilt** → full runnable app (config + shell + demo).
|
|
85
|
+
- **fresh + theme-only** → just `globals.css` + `next.config` wiring; build your own.
|
|
86
|
+
- **existing + prebuilt** → shell + demo added; config **never** overwritten —
|
|
87
|
+
prints the merge steps (`transpilePackages`, the `theme.css` import, the `@/*`
|
|
88
|
+
alias, `import "./globals.css"`).
|
|
89
|
+
- **existing + theme-only** → nothing copied; prints the wiring steps.
|
|
90
|
+
|
|
91
|
+
Other flags: `--yes` / `--force` / `--dry-run`. If you only need components, use
|
|
92
|
+
`--theme-only` (or skip `init`) and do the setup below.
|
|
86
93
|
|
|
87
94
|
VUI ships as TypeScript source.
|
|
88
95
|
|
package/README.md
CHANGED
|
@@ -48,32 +48,43 @@ pages** live in a one-shot scaffolder:
|
|
|
48
48
|
npx @viliha/vui-ui init
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
-
It's **interactive** —
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
It's **interactive** — a short decision tree:
|
|
52
|
+
|
|
53
|
+
1. **Fresh project?** (fresh vs existing)
|
|
54
|
+
2. **Pre-built theme?** — the full shell + demo pages, or just the theme wiring
|
|
55
|
+
that you configure yourself.
|
|
56
|
+
|
|
57
|
+
Files are copied into *your* repo, so you own and edit them.
|
|
58
|
+
|
|
59
|
+
| | **Pre-built** (shell + demo) | **Theme-only** (you configure) |
|
|
60
|
+
| --- | --- | --- |
|
|
61
|
+
| **Fresh** | full runnable app: config + shell + demo pages | just the theme wiring (`globals.css`, `next.config`) — build your own pages |
|
|
62
|
+
| **Existing** | shell + demo added; your config is **never** overwritten (prints merge steps) | nothing copied; prints the wiring steps |
|
|
55
63
|
|
|
56
64
|
```
|
|
57
65
|
Flags (for CI / agents, skip the prompts):
|
|
58
|
-
--fresh | --existing
|
|
59
|
-
--
|
|
60
|
-
--yes, -y
|
|
61
|
-
--force
|
|
62
|
-
--dry-run
|
|
66
|
+
--fresh | --existing project type (Q1)
|
|
67
|
+
--prebuilt | --theme-only pre-built shell + demo, or just the theme (Q2)
|
|
68
|
+
--yes, -y accept defaults (fresh, prebuilt)
|
|
69
|
+
--force overwrite existing files
|
|
70
|
+
--dry-run preview without writing
|
|
63
71
|
```
|
|
64
72
|
|
|
65
|
-
After
|
|
73
|
+
After a **pre-built** run, install the peer deps it prints, then `npm run dev` →
|
|
74
|
+
`/dashboard`.
|
|
66
75
|
|
|
67
|
-
### Fresh
|
|
76
|
+
### Fresh + pre-built (recommended for new apps)
|
|
68
77
|
|
|
69
|
-
Start from a `create-next-app` base.
|
|
70
|
-
`app/globals.css`,
|
|
78
|
+
Start from a `create-next-app` base. This writes `next.config.mjs`,
|
|
79
|
+
`app/globals.css`, the shell, and the demo pages (overwriting the create-next-app
|
|
80
|
+
boilerplate), so the demo runs out of the box.
|
|
71
81
|
|
|
72
82
|
### ⚠️ Existing project — read this first
|
|
73
83
|
|
|
74
84
|
Adding VUI to an app you already have needs care. **`init --existing` never
|
|
75
|
-
overwrites your config
|
|
76
|
-
`app/_components/`,
|
|
85
|
+
overwrites your config.** Pick pre-built to add the shell + pages under
|
|
86
|
+
`app/(app)/` and `app/_components/`, or theme-only to copy nothing — either way it
|
|
87
|
+
prints the four things to wire up:
|
|
77
88
|
|
|
78
89
|
1. **`next.config`** — add `transpilePackages: ["@viliha/vui-ui"]`.
|
|
79
90
|
(Optional, for the open-tabs *keep-alive*: `output: "export"`,
|
|
@@ -88,9 +99,9 @@ overwrites your config** — it adds the shell + pages under `app/(app)/` and
|
|
|
88
99
|
4. **Root `app/layout.tsx`** — `import "./globals.css"` (and mount fonts to match
|
|
89
100
|
the demo's look).
|
|
90
101
|
|
|
91
|
-
Run **`npx @viliha/vui-ui init --existing --dry-run`** first to see
|
|
92
|
-
it will add. If you only want the components
|
|
93
|
-
|
|
102
|
+
Run **`npx @viliha/vui-ui init --existing --prebuilt --dry-run`** first to see
|
|
103
|
+
exactly what it will add. If you only want the components, choose **theme-only**
|
|
104
|
+
(or skip `init` and follow the [Setup](#setup) above), then import from
|
|
94
105
|
`@viliha/vui-ui/*`.
|
|
95
106
|
|
|
96
107
|
> **Note on the theme in an existing app:** VUI owns its design tokens in
|
package/bin/vui.mjs
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// @viliha/vui-ui CLI —
|
|
3
|
-
// Next.js project (shadcn-style: the files land in YOUR repo, you own them).
|
|
2
|
+
// @viliha/vui-ui CLI — sets up the theme in a consumer's Next.js project.
|
|
4
3
|
//
|
|
5
4
|
// npx @viliha/vui-ui init [options]
|
|
6
5
|
//
|
|
7
|
-
// Interactive
|
|
8
|
-
//
|
|
6
|
+
// Interactive decision tree (skip with flags):
|
|
7
|
+
// 1. Fresh project? --fresh | --existing
|
|
8
|
+
// 2. Pre-built theme + demo? --prebuilt | --theme-only
|
|
9
|
+
//
|
|
10
|
+
// fresh + prebuilt -> full runnable app: config + shell + demo pages
|
|
11
|
+
// fresh + theme-only -> just the theme wiring (globals/next.config), you build
|
|
12
|
+
// existing + prebuilt -> shell + demo added (config untouched; merge steps printed)
|
|
13
|
+
// existing + theme-only -> nothing copied; prints the wiring steps
|
|
9
14
|
import { copyFileSync, existsSync, mkdirSync, readdirSync } from "node:fs";
|
|
10
15
|
import { dirname, join, relative, sep } from "node:path";
|
|
11
16
|
import { fileURLToPath } from "node:url";
|
|
@@ -16,6 +21,39 @@ const TEMPLATE = fileURLToPath(new URL("../template/", import.meta.url));
|
|
|
16
21
|
const args = process.argv.slice(2);
|
|
17
22
|
const cmd = args[0];
|
|
18
23
|
const has = (f) => args.includes(f);
|
|
24
|
+
const dry = has("--dry-run");
|
|
25
|
+
const forceFlag = has("--force");
|
|
26
|
+
|
|
27
|
+
const DEPS = [
|
|
28
|
+
"@radix-ui/react-icons",
|
|
29
|
+
"@radix-ui/react-slot",
|
|
30
|
+
"@tanstack/react-table",
|
|
31
|
+
"class-variance-authority",
|
|
32
|
+
"clsx",
|
|
33
|
+
"cmdk",
|
|
34
|
+
"date-fns",
|
|
35
|
+
"input-otp",
|
|
36
|
+
"lucide-react",
|
|
37
|
+
"next-themes",
|
|
38
|
+
"radix-ui",
|
|
39
|
+
"react-day-picker",
|
|
40
|
+
"react-hook-form",
|
|
41
|
+
"@hookform/resolvers",
|
|
42
|
+
"recharts",
|
|
43
|
+
"sonner",
|
|
44
|
+
"tailwind-merge",
|
|
45
|
+
"zod",
|
|
46
|
+
].join(" ");
|
|
47
|
+
|
|
48
|
+
const WIRING_STEPS = ` 1. Install peer deps (those your components use):
|
|
49
|
+
npm i ${DEPS}
|
|
50
|
+
2. app/globals.css:
|
|
51
|
+
@import "tailwindcss";
|
|
52
|
+
@import "@viliha/vui-ui/theme.css";
|
|
53
|
+
3. next.config: transpilePackages: ["@viliha/vui-ui"]
|
|
54
|
+
4. tsconfig.json (only if you copy the reference-app pieces):
|
|
55
|
+
"compilerOptions": { "paths": { "@/*": ["./*"] } }
|
|
56
|
+
Then import components from @viliha/vui-ui/* and build your pages.`;
|
|
19
57
|
|
|
20
58
|
function usage() {
|
|
21
59
|
console.log(`@viliha/vui-ui
|
|
@@ -23,18 +61,14 @@ function usage() {
|
|
|
23
61
|
Usage:
|
|
24
62
|
npx @viliha/vui-ui init [options]
|
|
25
63
|
|
|
26
|
-
init
|
|
27
|
-
project. Interactive unless flags are given. Non-destructive:
|
|
28
|
-
existing files are skipped (use --force to overwrite).
|
|
64
|
+
init Set up the VUI theme in this project (interactive).
|
|
29
65
|
|
|
30
66
|
Options:
|
|
31
|
-
--fresh
|
|
32
|
-
--
|
|
33
|
-
--
|
|
34
|
-
--
|
|
35
|
-
--
|
|
36
|
-
--force Overwrite files that already exist.
|
|
37
|
-
--dry-run Show what would be written without touching the disk.
|
|
67
|
+
--fresh | --existing project type (Q1)
|
|
68
|
+
--prebuilt | --theme-only pre-built shell + demo, or just the theme (Q2)
|
|
69
|
+
--yes, -y accept defaults (fresh, prebuilt)
|
|
70
|
+
--force overwrite existing files
|
|
71
|
+
--dry-run preview without writing
|
|
38
72
|
`);
|
|
39
73
|
}
|
|
40
74
|
|
|
@@ -50,9 +84,6 @@ if (!existsSync(TEMPLATE)) {
|
|
|
50
84
|
process.exit(1);
|
|
51
85
|
}
|
|
52
86
|
|
|
53
|
-
const dry = has("--dry-run");
|
|
54
|
-
const force = has("--force");
|
|
55
|
-
|
|
56
87
|
async function ask(question, def) {
|
|
57
88
|
if (has("--yes") || has("-y") || !stdin.isTTY) return def;
|
|
58
89
|
const rl = createInterface({ input: stdin, output: stdout });
|
|
@@ -61,80 +92,97 @@ async function ask(question, def) {
|
|
|
61
92
|
return a || def;
|
|
62
93
|
}
|
|
63
94
|
|
|
64
|
-
//
|
|
65
|
-
|
|
66
|
-
const CONFIG_FILES = new Set([
|
|
95
|
+
// Theme wiring config an existing project already has — never clobbered there.
|
|
96
|
+
const WIRING = new Set([
|
|
67
97
|
"next.config.mjs",
|
|
68
98
|
"postcss.config.mjs",
|
|
99
|
+
".env.example",
|
|
69
100
|
"app/globals.css",
|
|
70
|
-
"app/layout.tsx",
|
|
71
|
-
"app/page.tsx",
|
|
72
101
|
]);
|
|
73
102
|
|
|
74
|
-
//
|
|
75
|
-
//
|
|
103
|
+
// Optional demo pages (vs the always-present shell). Dashboard stays in the
|
|
104
|
+
// shell so `/` has a landing page.
|
|
76
105
|
function isDemo(rel) {
|
|
77
106
|
const p = rel.split(sep).join("/");
|
|
78
107
|
if (p.startsWith("app/auth/")) return true;
|
|
79
108
|
if (p.startsWith("app/onboarding/")) return true;
|
|
80
109
|
if (p.startsWith("app/register-business/")) return true;
|
|
81
|
-
if (p.startsWith("app/(app)/"))
|
|
110
|
+
if (p.startsWith("app/(app)/"))
|
|
82
111
|
return !(p === "app/(app)/layout.tsx" || p.startsWith("app/(app)/dashboard/"));
|
|
83
|
-
}
|
|
84
112
|
return false;
|
|
85
113
|
}
|
|
86
114
|
|
|
115
|
+
function category(rel) {
|
|
116
|
+
const p = rel.split(sep).join("/");
|
|
117
|
+
if (WIRING.has(p)) return "wiring";
|
|
118
|
+
if (isDemo(rel)) return "demo";
|
|
119
|
+
return "shell";
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function allFiles(dir, out = []) {
|
|
123
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
124
|
+
const abs = join(dir, entry.name);
|
|
125
|
+
if (entry.isDirectory()) allFiles(abs, out);
|
|
126
|
+
else out.push(abs);
|
|
127
|
+
}
|
|
128
|
+
return out;
|
|
129
|
+
}
|
|
130
|
+
|
|
87
131
|
async function main() {
|
|
88
|
-
//
|
|
89
|
-
let
|
|
90
|
-
if (
|
|
91
|
-
const a = await ask(
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
132
|
+
// Q1 — fresh vs existing.
|
|
133
|
+
let fresh = has("--fresh") ? true : has("--existing") ? false : null;
|
|
134
|
+
if (fresh === null) {
|
|
135
|
+
const a = await ask("Are you installing VUI in a fresh project? [Y/n] ", "y");
|
|
136
|
+
fresh = !a.startsWith("n");
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Q2 — pre-built (shell + demo) vs theme-only.
|
|
140
|
+
let prebuilt = has("--prebuilt") ? true : has("--theme-only") ? false : null;
|
|
141
|
+
if (prebuilt === null) {
|
|
142
|
+
const q = fresh
|
|
143
|
+
? "Install the pre-built theme with a Next.js project (shell + demo pages)? [Y/n] "
|
|
144
|
+
: "Install the pre-built theme (shell + demo pages) into this project? [Y/n] ";
|
|
145
|
+
const a = await ask(q, "y");
|
|
146
|
+
prebuilt = !a.startsWith("n");
|
|
96
147
|
}
|
|
97
148
|
|
|
98
|
-
//
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
"y",
|
|
149
|
+
// Existing + theme-only: nothing to copy — just print the wiring steps.
|
|
150
|
+
if (!fresh && !prebuilt) {
|
|
151
|
+
console.log(
|
|
152
|
+
`\nTheme-only setup for an existing project — no files copied.\n\n` +
|
|
153
|
+
`Wire the theme into your app:\n${WIRING_STEPS}\n`,
|
|
104
154
|
);
|
|
105
|
-
|
|
155
|
+
return;
|
|
106
156
|
}
|
|
107
157
|
|
|
108
158
|
const cwd = process.cwd();
|
|
159
|
+
const files = allFiles(TEMPLATE);
|
|
109
160
|
let created = 0;
|
|
110
161
|
let skipped = 0;
|
|
111
162
|
const configSkipped = [];
|
|
112
163
|
|
|
113
|
-
const
|
|
114
|
-
(
|
|
115
|
-
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
116
|
-
const abs = join(dir, entry.name);
|
|
117
|
-
if (entry.isDirectory()) walk(abs);
|
|
118
|
-
else files.push(abs);
|
|
119
|
-
}
|
|
120
|
-
})(TEMPLATE);
|
|
121
|
-
|
|
122
|
-
console.log(
|
|
123
|
-
`\nScaffolding VUI (${mode}${demo ? ", with demo" : ", no demo"}) into ${cwd}${
|
|
124
|
-
dry ? " [dry run]" : ""
|
|
125
|
-
}\n`,
|
|
126
|
-
);
|
|
164
|
+
const label = `${fresh ? "fresh" : "existing"}, ${prebuilt ? "prebuilt" : "theme-only"}`;
|
|
165
|
+
console.log(`\nScaffolding VUI (${label}) into ${cwd}${dry ? " [dry run]" : ""}\n`);
|
|
127
166
|
|
|
128
167
|
for (const abs of files) {
|
|
129
168
|
const rel = relative(TEMPLATE, abs);
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
169
|
+
const cat = category(rel);
|
|
170
|
+
|
|
171
|
+
// Which files this leaf installs.
|
|
172
|
+
const include =
|
|
173
|
+
fresh && prebuilt
|
|
174
|
+
? true // everything: config + shell + demo
|
|
175
|
+
: fresh && !prebuilt
|
|
176
|
+
? cat === "wiring" // just the theme wiring
|
|
177
|
+
: /* existing && prebuilt */ cat !== "wiring"; // shell + demo, not config
|
|
178
|
+
if (!include) {
|
|
179
|
+
if (!fresh && cat === "wiring") configSkipped.push(rel.split(sep).join("/"));
|
|
135
180
|
continue;
|
|
136
181
|
}
|
|
137
182
|
|
|
183
|
+
// Fresh overwrites boilerplate (create-next-app defaults); existing is
|
|
184
|
+
// non-destructive so we never clobber the user's own files.
|
|
185
|
+
const force = forceFlag || fresh;
|
|
138
186
|
const dest = join(cwd, rel);
|
|
139
187
|
if (existsSync(dest) && !force) {
|
|
140
188
|
skipped++;
|
|
@@ -153,59 +201,38 @@ async function main() {
|
|
|
153
201
|
`\nDone — ${created} file(s) ${dry ? "would be added" : "added"}, ${skipped} skipped.`,
|
|
154
202
|
);
|
|
155
203
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
"@radix-ui/react-slot",
|
|
159
|
-
"@tanstack/react-table",
|
|
160
|
-
"class-variance-authority",
|
|
161
|
-
"clsx",
|
|
162
|
-
"cmdk",
|
|
163
|
-
"date-fns",
|
|
164
|
-
"input-otp",
|
|
165
|
-
"lucide-react",
|
|
166
|
-
"next-themes",
|
|
167
|
-
"radix-ui",
|
|
168
|
-
"react-day-picker",
|
|
169
|
-
"react-hook-form",
|
|
170
|
-
"@hookform/resolvers",
|
|
171
|
-
"recharts",
|
|
172
|
-
"sonner",
|
|
173
|
-
"tailwind-merge",
|
|
174
|
-
"zod",
|
|
175
|
-
].join(" ");
|
|
176
|
-
|
|
177
|
-
if (mode === "existing") {
|
|
204
|
+
// Per-leaf next steps.
|
|
205
|
+
if (fresh && prebuilt) {
|
|
178
206
|
console.log(`
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
• next.config: add
|
|
183
|
-
transpilePackages: ["@viliha/vui-ui"]
|
|
184
|
-
(for the demo's tab keep-alive: output:"export", images:{unoptimized:true},
|
|
185
|
-
trailingSlash:true — optional, only if you want static-export keep-alive)
|
|
186
|
-
• app/globals.css: add
|
|
187
|
-
@import "tailwindcss";
|
|
188
|
-
@import "@viliha/vui-ui/theme.css";
|
|
189
|
-
• tsconfig.json: map the alias the scaffold uses
|
|
190
|
-
"compilerOptions": { "paths": { "@/*": ["./*"] } }
|
|
191
|
-
• Root app/layout.tsx: import "./globals.css" and mount fonts if you want the
|
|
192
|
-
demo's exact look (see the scaffolded app/(app)/layout.tsx for the shell).
|
|
207
|
+
Next steps:
|
|
208
|
+
1. npm i ${DEPS}
|
|
209
|
+
2. npm run dev -> http://localhost:3000 (redirects to /dashboard)
|
|
193
210
|
|
|
194
|
-
|
|
195
|
-
|
|
211
|
+
Everything is in YOUR repo — edit app/_components/nav-config.ts, set your logo
|
|
212
|
+
(NEXT_PUBLIC_LOGO_URL in .env), and delete demo pages you don't need.
|
|
196
213
|
`);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
1. Install peer dependencies:
|
|
201
|
-
npm i ${DEPS}
|
|
202
|
-
2. Start the app:
|
|
203
|
-
npm run dev -> http://localhost:3000 (redirects to /dashboard)
|
|
214
|
+
} else if (fresh && !prebuilt) {
|
|
215
|
+
console.log(`
|
|
216
|
+
Theme wired (globals.css + next.config). No shell or demo pages — build your own.
|
|
204
217
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
218
|
+
Next steps:
|
|
219
|
+
1. npm i ${DEPS}
|
|
220
|
+
2. Import components from @viliha/vui-ui/* in your pages, then: npm run dev
|
|
208
221
|
`);
|
|
222
|
+
} else {
|
|
223
|
+
// existing + prebuilt
|
|
224
|
+
console.log(`
|
|
225
|
+
⚠ Existing project — config files were NOT written. Merge these yourself:
|
|
226
|
+
${WIRING_STEPS}
|
|
227
|
+
|
|
228
|
+
The shell + demo were added under app/(app)/ and app/_components/ — review them
|
|
229
|
+
(re-run with --dry-run first if unsure) before committing.${
|
|
230
|
+
configSkipped.length
|
|
231
|
+
? `\nSkipped config: ${configSkipped.join(", ")}`
|
|
232
|
+
: ""
|
|
233
|
+
}
|
|
234
|
+
`);
|
|
235
|
+
}
|
|
209
236
|
}
|
|
210
237
|
|
|
211
238
|
main();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@viliha/vui-ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Vui UI — a clean, token-driven React admin/CRM component library built on Tailwind CSS v4, shadcn-style patterns, and Radix Icons. Ships as TypeScript source (Just-in-Time), compiled by the consuming app.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Suman Bonakurthi",
|