@viliha/vui-ui 1.3.0 → 1.4.1

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 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: fresh vs existing, with/without demo
76
+ npx @viliha/vui-ui init # interactive decision tree
77
77
  ```
78
78
 
79
- Files land in the consumer's repo (they own them). **Fresh project:** writes
80
- config too, runs out of the box. **Existing project:** `init --existing` never
81
- overwrites config it adds the shell/pages and prints the merge steps
82
- (`transpilePackages`, the `theme.css` import, the `@/*` alias, `import
83
- "./globals.css"`). Flags: `--fresh` / `--existing` / `--demo` / `--no-demo` /
84
- `--yes` / `--force` / `--dry-run`. If you only need components, skip `init` and
85
- just do the setup below.
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** — it asks whether this is a **fresh** or **existing**
52
- project and whether to include the **demo pages** (dashboard, CRM, calendar,
53
- chat, support, settings, auth). Files are copied into *your* repo, so you own and
54
- edit them. It's **non-destructive**: existing files are skipped.
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 project type
59
- --demo | --no-demo include demo pages (default: with demo)
60
- --yes, -y accept defaults (fresh, with demo)
61
- --force overwrite existing files
62
- --dry-run preview without writing
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 it runs, install the peer deps it prints, then `npm run dev` → `/dashboard`.
73
+ After a **pre-built** run, install the peer deps it prints, then `npm run dev` →
74
+ `/dashboard`.
66
75
 
67
- ### Fresh project (recommended)
76
+ ### Fresh + pre-built (recommended for new apps)
68
77
 
69
- Start from a `create-next-app` base. `init --fresh` also writes `next.config.mjs`,
70
- `app/globals.css`, and the root layout, so the demo runs out of the box.
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** it adds the shell + pages under `app/(app)/` and
76
- `app/_components/`, then prints the four things to merge yourself:
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 exactly what
92
- it will add. If you only want the components (not the shell/pages), **skip `init`
93
- entirely** the [Setup](#setup) above is all you need, then import from
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 — scaffolds the app shell + demo pages into a consumer's
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 by default: asks whether this is a fresh or existing project and
8
- // whether to include the demo pages. Non-interactive with flags (for CI/agents).
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,41 @@ 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
+ // theme's globals.css imports these:
46
+ "tw-animate-css",
47
+ "zod",
48
+ ].join(" ");
49
+
50
+ const WIRING_STEPS = ` 1. Install peer deps (those your components use):
51
+ npm i ${DEPS}
52
+ 2. app/globals.css:
53
+ @import "tailwindcss";
54
+ @import "@viliha/vui-ui/theme.css";
55
+ 3. next.config: transpilePackages: ["@viliha/vui-ui"]
56
+ 4. tsconfig.json (only if you copy the reference-app pieces):
57
+ "compilerOptions": { "paths": { "@/*": ["./*"] } }
58
+ Then import components from @viliha/vui-ui/* and build your pages.`;
19
59
 
20
60
  function usage() {
21
61
  console.log(`@viliha/vui-ui
@@ -23,18 +63,16 @@ function usage() {
23
63
  Usage:
24
64
  npx @viliha/vui-ui init [options]
25
65
 
26
- init Scaffold the VUI app shell (+ optional demo pages) into this
27
- project. Interactive unless flags are given. Non-destructive:
28
- existing files are skipped (use --force to overwrite).
66
+ init Set up the VUI theme in this project (interactive).
29
67
 
30
68
  Options:
31
- --fresh Treat as a brand-new project (copies config too).
32
- --existing Treat as an existing project (skips config; prints merge steps).
33
- --demo Include the demo pages (dashboard, CRM, calendar, chat, …).
34
- --no-demo Shell + dashboard only, no other demo pages.
35
- --yes, -y Accept defaults without prompting (fresh, with demo).
36
- --force Overwrite files that already exist.
37
- --dry-run Show what would be written without touching the disk.
69
+ --nextjs | --turbo standalone Next.js app, or a Turborepo (Q0)
70
+ --dir <path> Turborepo target app dir (default apps/web)
71
+ --fresh | --existing project type (Q1)
72
+ --prebuilt | --theme-only pre-built shell + demo, or just the theme (Q2)
73
+ --yes, -y accept defaults (nextjs, fresh, prebuilt)
74
+ --force overwrite existing files
75
+ --dry-run preview without writing
38
76
  `);
39
77
  }
40
78
 
@@ -50,9 +88,6 @@ if (!existsSync(TEMPLATE)) {
50
88
  process.exit(1);
51
89
  }
52
90
 
53
- const dry = has("--dry-run");
54
- const force = has("--force");
55
-
56
91
  async function ask(question, def) {
57
92
  if (has("--yes") || has("-y") || !stdin.isTTY) return def;
58
93
  const rl = createInterface({ input: stdin, output: stdout });
@@ -61,81 +96,118 @@ async function ask(question, def) {
61
96
  return a || def;
62
97
  }
63
98
 
64
- // Config/root files that an existing project almost certainly already has —
65
- // copying them would collide, so in "existing" mode we skip + print merge steps.
66
- const CONFIG_FILES = new Set([
99
+ // Theme wiring config an existing project already has — never clobbered there.
100
+ const WIRING = new Set([
67
101
  "next.config.mjs",
68
102
  "postcss.config.mjs",
103
+ ".env.example",
69
104
  "app/globals.css",
70
- "app/layout.tsx",
71
- "app/page.tsx",
72
105
  ]);
73
106
 
74
- // A file is a "demo page" (optional) vs the shell (always). The dashboard stays
75
- // in the shell so `/` has a landing page.
107
+ // Optional demo pages (vs the always-present shell). Dashboard stays in the
108
+ // shell so `/` has a landing page.
76
109
  function isDemo(rel) {
77
110
  const p = rel.split(sep).join("/");
78
111
  if (p.startsWith("app/auth/")) return true;
79
112
  if (p.startsWith("app/onboarding/")) return true;
80
113
  if (p.startsWith("app/register-business/")) return true;
81
- if (p.startsWith("app/(app)/")) {
114
+ if (p.startsWith("app/(app)/"))
82
115
  return !(p === "app/(app)/layout.tsx" || p.startsWith("app/(app)/dashboard/"));
83
- }
84
116
  return false;
85
117
  }
86
118
 
119
+ function category(rel) {
120
+ const p = rel.split(sep).join("/");
121
+ if (WIRING.has(p)) return "wiring";
122
+ if (isDemo(rel)) return "demo";
123
+ return "shell";
124
+ }
125
+
126
+ function allFiles(dir, out = []) {
127
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
128
+ const abs = join(dir, entry.name);
129
+ if (entry.isDirectory()) allFiles(abs, out);
130
+ else out.push(abs);
131
+ }
132
+ return out;
133
+ }
134
+
87
135
  async function main() {
88
- // 1. Fresh vs existing.
89
- let mode = has("--existing") ? "existing" : has("--fresh") ? "fresh" : null;
90
- if (!mode) {
136
+ const cwd = process.cwd();
137
+
138
+ // Q0 — standalone Next.js vs Turborepo (decides WHERE files land).
139
+ let turbo = has("--turbo") ? true : has("--nextjs") ? false : null;
140
+ if (turbo === null) {
91
141
  const a = await ask(
92
- "Is this a fresh project or an existing one? [F(resh)/e(xisting)] ",
93
- "f",
142
+ "Is this a standalone Next.js app or a Turborepo? [N/t] ",
143
+ "n",
94
144
  );
95
- mode = a.startsWith("e") ? "existing" : "fresh";
145
+ turbo = a.startsWith("t");
146
+ }
147
+ let appDir = ".";
148
+ if (turbo) {
149
+ const di = args.indexOf("--dir");
150
+ const flagDir = di >= 0 ? args[di + 1] : null;
151
+ appDir =
152
+ flagDir ||
153
+ (await ask("Target app directory inside the repo? [apps/web] ", "apps/web"));
96
154
  }
155
+ const targetRoot = join(cwd, appDir);
97
156
 
98
- // 2. Include the demo pages?
99
- let demo = has("--demo") ? true : has("--no-demo") ? false : null;
100
- if (demo === null) {
101
- const a = await ask(
102
- "Install the demo pages (dashboard, CRM, calendar, chat, support…)? [Y/n] ",
103
- "y",
157
+ // Q1 fresh vs existing.
158
+ let fresh = has("--fresh") ? true : has("--existing") ? false : null;
159
+ if (fresh === null) {
160
+ const a = await ask("Are you installing VUI in a fresh project? [Y/n] ", "y");
161
+ fresh = !a.startsWith("n");
162
+ }
163
+
164
+ // Q2 — pre-built (shell + demo) vs theme-only.
165
+ let prebuilt = has("--prebuilt") ? true : has("--theme-only") ? false : null;
166
+ if (prebuilt === null) {
167
+ const q = fresh
168
+ ? "Install the pre-built theme with a Next.js project (shell + demo pages)? [Y/n] "
169
+ : "Install the pre-built theme (shell + demo pages) into this project? [Y/n] ";
170
+ const a = await ask(q, "y");
171
+ prebuilt = !a.startsWith("n");
172
+ }
173
+
174
+ // Existing + theme-only: nothing to copy — just print the wiring steps.
175
+ if (!fresh && !prebuilt) {
176
+ console.log(
177
+ `\nTheme-only setup for an existing project — no files copied.\n\n` +
178
+ `Wire the theme into your app:\n${WIRING_STEPS}\n`,
104
179
  );
105
- demo = !a.startsWith("n");
180
+ return;
106
181
  }
107
182
 
108
- const cwd = process.cwd();
183
+ const files = allFiles(TEMPLATE);
109
184
  let created = 0;
110
185
  let skipped = 0;
111
186
  const configSkipped = [];
112
187
 
113
- const files = [];
114
- (function walk(dir) {
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
- );
188
+ const label = `${turbo ? "turbo" : "nextjs"}, ${fresh ? "fresh" : "existing"}, ${prebuilt ? "prebuilt" : "theme-only"}`;
189
+ console.log(`\nScaffolding VUI (${label}) into ${targetRoot}${dry ? " [dry run]" : ""}\n`);
127
190
 
128
191
  for (const abs of files) {
129
192
  const rel = relative(TEMPLATE, abs);
130
- const norm = rel.split(sep).join("/");
193
+ const cat = category(rel);
131
194
 
132
- if (isDemo(rel) && !demo) continue; // demo not selected
133
- if (mode === "existing" && CONFIG_FILES.has(norm)) {
134
- configSkipped.push(norm);
195
+ // Which files this leaf installs.
196
+ const include =
197
+ fresh && prebuilt
198
+ ? true // everything: config + shell + demo
199
+ : fresh && !prebuilt
200
+ ? cat === "wiring" // just the theme wiring
201
+ : /* existing && prebuilt */ cat !== "wiring"; // shell + demo, not config
202
+ if (!include) {
203
+ if (!fresh && cat === "wiring") configSkipped.push(rel.split(sep).join("/"));
135
204
  continue;
136
205
  }
137
206
 
138
- const dest = join(cwd, rel);
207
+ // Fresh overwrites boilerplate (create-next-app defaults); existing is
208
+ // non-destructive so we never clobber the user's own files.
209
+ const force = forceFlag || fresh;
210
+ const dest = join(targetRoot, rel);
139
211
  if (existsSync(dest) && !force) {
140
212
  skipped++;
141
213
  console.log(` skip ${rel}`);
@@ -153,59 +225,47 @@ async function main() {
153
225
  `\nDone — ${created} file(s) ${dry ? "would be added" : "added"}, ${skipped} skipped.`,
154
226
  );
155
227
 
156
- const DEPS = [
157
- "@radix-ui/react-icons",
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") {
228
+ // Per-leaf next steps.
229
+ if (fresh && prebuilt) {
178
230
  console.log(`
179
- ⚠ Existing project — config files were NOT written to avoid clobbering yours.
180
- Merge these into your project manually:
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).
231
+ Next steps:
232
+ 1. npm i ${DEPS}
233
+ 2. npm run dev -> http://localhost:3000 (redirects to /dashboard)
193
234
 
194
- The shell + pages were added under app/(app)/ and app/_components/ — review
195
- them (re-run with --dry-run first if unsure) before committing.
235
+ Everything is in YOUR repo edit app/_components/nav-config.ts, set your logo
236
+ (NEXT_PUBLIC_LOGO_URL in .env), and delete demo pages you don't need.
196
237
  `);
197
- }
238
+ } else if (fresh && !prebuilt) {
239
+ console.log(`
240
+ Theme wired (globals.css + next.config). No shell or demo pages — build your own.
198
241
 
199
- console.log(`Next steps:
200
- 1. Install peer dependencies:
201
- npm i ${DEPS}
202
- 2. Start the app:
203
- npm run dev -> http://localhost:3000 (redirects to /dashboard)
242
+ Next steps:
243
+ 1. npm i ${DEPS}
244
+ 2. Import components from @viliha/vui-ui/* in your pages, then: npm run dev
245
+ `);
246
+ } else {
247
+ // existing + prebuilt
248
+ console.log(`
249
+ ⚠ Existing project — config files were NOT written. Merge these yourself:
250
+ ${WIRING_STEPS}
204
251
 
205
- Everything landed in YOUR repo edit app/_components/nav-config.ts, set your
206
- logo (NEXT_PUBLIC_LOGO_URL in .env), and delete demo pages you don't need.
207
- Docs: /docs/navigation (sidebar, tabs) and /docs/configuration (logo, env).
252
+ The shell + demo were added under app/(app)/ and app/_components/ review them
253
+ (re-run with --dry-run first if unsure) before committing.${
254
+ configSkipped.length
255
+ ? `\nSkipped config: ${configSkipped.join(", ")}`
256
+ : ""
257
+ }
208
258
  `);
259
+ }
260
+
261
+ if (turbo) {
262
+ console.log(`Turborepo notes (target: ${appDir}):
263
+ • Add "@viliha/vui-ui" to ${appDir}/package.json dependencies.
264
+ • Ensure your workspace globs include this app (pnpm-workspace.yaml / workspaces).
265
+ • Run install + dev from the repo root or with a filter, e.g.
266
+ pnpm --filter <app-name> dev
267
+ `);
268
+ }
209
269
  }
210
270
 
211
271
  main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@viliha/vui-ui",
3
- "version": "1.3.0",
3
+ "version": "1.4.1",
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",
@@ -0,0 +1,40 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "lib": [
5
+ "dom",
6
+ "dom.iterable",
7
+ "esnext"
8
+ ],
9
+ "allowJs": true,
10
+ "skipLibCheck": true,
11
+ "strict": true,
12
+ "noEmit": true,
13
+ "esModuleInterop": true,
14
+ "module": "esnext",
15
+ "moduleResolution": "bundler",
16
+ "resolveJsonModule": true,
17
+ "isolatedModules": true,
18
+ "jsx": "preserve",
19
+ "incremental": true,
20
+ "plugins": [
21
+ {
22
+ "name": "next"
23
+ }
24
+ ],
25
+ "paths": {
26
+ "@/*": [
27
+ "./*"
28
+ ]
29
+ }
30
+ },
31
+ "include": [
32
+ "next-env.d.ts",
33
+ "**/*.ts",
34
+ "**/*.tsx",
35
+ ".next/types/**/*.ts"
36
+ ],
37
+ "exclude": [
38
+ "node_modules"
39
+ ]
40
+ }