@zentauri-ui/zentauri-components 2.1.1 → 2.1.3
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 +17 -16
- package/cli/cli.integration.test.ts +115 -0
- package/cli/index.mjs +254 -5
- package/cli/registry.json +84 -0
- package/dist/{chunk-TYBQZO6Y.mjs → chunk-2P4WPYC3.mjs} +3 -3
- package/dist/{chunk-TYBQZO6Y.mjs.map → chunk-2P4WPYC3.mjs.map} +1 -1
- package/dist/{chunk-ZMFRJHO6.mjs → chunk-3BL5PO3K.mjs} +3 -3
- package/dist/{chunk-ZMFRJHO6.mjs.map → chunk-3BL5PO3K.mjs.map} +1 -1
- package/dist/{chunk-TT33BIIT.js → chunk-BJDT7P2I.js} +35 -35
- package/dist/{chunk-TT33BIIT.js.map → chunk-BJDT7P2I.js.map} +1 -1
- package/dist/{chunk-7N2UYQBJ.mjs → chunk-BOTDQV6U.mjs} +4 -4
- package/dist/{chunk-7N2UYQBJ.mjs.map → chunk-BOTDQV6U.mjs.map} +1 -1
- package/dist/{chunk-3N575QVC.mjs → chunk-BPSW3SRE.mjs} +3 -3
- package/dist/{chunk-3N575QVC.mjs.map → chunk-BPSW3SRE.mjs.map} +1 -1
- package/dist/{chunk-5TVBPPS6.js → chunk-DKBMNS6F.js} +11 -11
- package/dist/{chunk-5TVBPPS6.js.map → chunk-DKBMNS6F.js.map} +1 -1
- package/dist/{chunk-4LCH4OJ5.js → chunk-E7P4O3ET.js} +10 -10
- package/dist/{chunk-4LCH4OJ5.js.map → chunk-E7P4O3ET.js.map} +1 -1
- package/dist/{chunk-VHYUH5OH.js → chunk-GNQH247A.js} +6 -6
- package/dist/{chunk-VHYUH5OH.js.map → chunk-GNQH247A.js.map} +1 -1
- package/dist/{chunk-42ZSQNDF.mjs → chunk-NIVJFG5Z.mjs} +35 -35
- package/dist/{chunk-42ZSQNDF.mjs.map → chunk-NIVJFG5Z.mjs.map} +1 -1
- package/dist/{chunk-BIQZC26Q.js → chunk-OH5VOGNW.js} +4 -4
- package/dist/chunk-OH5VOGNW.js.map +1 -0
- package/dist/chunk-SOYCLBHK.js +19 -0
- package/dist/{chunk-R4D5V7NT.js.map → chunk-SOYCLBHK.js.map} +1 -1
- package/dist/{chunk-V2LI5QZD.js → chunk-UJPB5NHW.js} +18 -18
- package/dist/{chunk-V2LI5QZD.js.map → chunk-UJPB5NHW.js.map} +1 -1
- package/dist/{chunk-TVEK6PKH.mjs → chunk-XY3TKIIH.mjs} +4 -4
- package/dist/chunk-XY3TKIIH.mjs.map +1 -0
- package/dist/{chunk-GOH2THVW.mjs → chunk-ZNI3AB3W.mjs} +3 -3
- package/dist/{chunk-GOH2THVW.mjs.map → chunk-ZNI3AB3W.mjs.map} +1 -1
- package/dist/design-system/alert.d.ts +33 -33
- package/dist/design-system/facade.js +4 -4
- package/dist/design-system/facade.mjs +3 -3
- package/dist/design-system/tabs.d.ts +2 -2
- package/dist/ui/alert/animated.js +3 -3
- package/dist/ui/alert/animated.mjs +2 -2
- package/dist/ui/alert.js +11 -11
- package/dist/ui/alert.mjs +3 -3
- package/dist/ui/buttons/animated.js +6 -6
- package/dist/ui/buttons/animated.mjs +4 -4
- package/dist/ui/buttons.js +7 -7
- package/dist/ui/buttons.mjs +5 -5
- package/dist/ui/context-menu/context-menu.d.ts.map +1 -1
- package/dist/ui/context-menu/types.d.ts +3 -1
- package/dist/ui/context-menu/types.d.ts.map +1 -1
- package/dist/ui/context-menu.js +17 -0
- package/dist/ui/context-menu.js.map +1 -1
- package/dist/ui/context-menu.mjs +17 -0
- package/dist/ui/context-menu.mjs.map +1 -1
- package/dist/ui/dropdown/dropdown.d.ts +1 -1
- package/dist/ui/dropdown/dropdown.d.ts.map +1 -1
- package/dist/ui/dropdown.js +43 -2
- package/dist/ui/dropdown.js.map +1 -1
- package/dist/ui/dropdown.mjs +44 -3
- package/dist/ui/dropdown.mjs.map +1 -1
- package/dist/ui/dynamic-stepper.js +16 -16
- package/dist/ui/dynamic-stepper.mjs +5 -5
- package/dist/ui/pagination.js +12 -12
- package/dist/ui/pagination.mjs +4 -4
- package/dist/ui/tabs/animated.js +3 -3
- package/dist/ui/tabs/animated.mjs +2 -2
- package/dist/ui/tabs.js +10 -10
- package/dist/ui/tabs.mjs +2 -2
- package/package.json +10 -3
- package/src/design-system/alert.ts +33 -33
- package/src/design-system/tabs.ts +2 -2
- package/src/hooks/useTableFilter/useTableFilter.test.ts +12 -2
- package/src/ui/context-menu/context-menu.test.tsx +38 -0
- package/src/ui/context-menu/context-menu.tsx +17 -1
- package/src/ui/context-menu/types.ts +3 -0
- package/src/ui/dropdown/dropdown.tsx +59 -2
- package/dist/chunk-BIQZC26Q.js.map +0 -1
- package/dist/chunk-R4D5V7NT.js +0 -19
- package/dist/chunk-TVEK6PKH.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -29,17 +29,17 @@ Generated from the component package Vitest JSON report via `pnpm --filter @zent
|
|
|
29
29
|
|
|
30
30
|
| Metric | Result |
|
|
31
31
|
| ---------- | ---------------- |
|
|
32
|
-
| Test files |
|
|
33
|
-
| Tests |
|
|
34
|
-
|
|
35
|
-
| Area
|
|
36
|
-
|
|
|
37
|
-
| Components and UI utilities
|
|
38
|
-
| Standalone animations
|
|
39
|
-
| React hooks
|
|
40
|
-
| Design system facade
|
|
41
|
-
| CLI and import rewriting
|
|
42
|
-
|
|
|
32
|
+
| Test files | 93 passed (93) |
|
|
33
|
+
| Tests | 751 passed (751) |
|
|
34
|
+
|
|
35
|
+
| Area | Test files | Tests |
|
|
36
|
+
| ------------------------------ | ---------- | ----- |
|
|
37
|
+
| Components and UI utilities | 46 | 455 |
|
|
38
|
+
| Standalone animations | 1 | 45 |
|
|
39
|
+
| React hooks | 41 | 174 |
|
|
40
|
+
| Design system facade | 1 | 11 |
|
|
41
|
+
| CLI and import rewriting | 2 | 24 |
|
|
42
|
+
| Accessibility (axe + keyboard) | 2 | 42 |
|
|
43
43
|
|
|
44
44
|
### Per-suite snapshot
|
|
45
45
|
|
|
@@ -49,17 +49,19 @@ Generated from the component package Vitest JSON report via `pnpm --filter @zent
|
|
|
49
49
|
| `src/ui/buttons/button.test.tsx` | 44 |
|
|
50
50
|
| `src/ui/inputs/input.test.tsx` | 40 |
|
|
51
51
|
| `src/ui/peer-isolation.test.ts` | 29 |
|
|
52
|
+
| `src/accessibility/axe-core.test.tsx` | 24 |
|
|
52
53
|
| `src/ui/combobox/combobox.test.tsx` | 24 |
|
|
54
|
+
| `cli/cli.integration.test.ts` | 19 |
|
|
55
|
+
| `src/accessibility/keyboard-interaction.test.tsx` | 18 |
|
|
53
56
|
| `src/ui/pagination/pagination.test.tsx` | 15 |
|
|
54
57
|
| `src/ui/timeline/timeline.test.tsx` | 14 |
|
|
55
|
-
| `
|
|
58
|
+
| `src/ui/context-menu/context-menu.test.tsx` | 12 |
|
|
56
59
|
| `src/lib/facade.test.ts` | 11 |
|
|
57
60
|
| `src/ui/alert/alert.test.tsx` | 11 |
|
|
58
61
|
| `src/ui/rating/rating.test.tsx` | 11 |
|
|
59
62
|
| `src/ui/select/select.test.tsx` | 11 |
|
|
60
63
|
| `src/ui/table/table.test.tsx` | 11 |
|
|
61
64
|
| `src/hooks/usePagination/usePagination.test.ts` | 10 |
|
|
62
|
-
| `src/ui/context-menu/context-menu.test.tsx` | 10 |
|
|
63
65
|
| `src/ui/marquee/marquee.test.tsx` | 10 |
|
|
64
66
|
| `src/ui/modal/modal.test.tsx` | 10 |
|
|
65
67
|
| `src/ui/otp-input/otp-input.test.tsx` | 10 |
|
|
@@ -83,7 +85,6 @@ Generated from the component package Vitest JSON report via `pnpm --filter @zent
|
|
|
83
85
|
| `src/ui/drawer/drawer.test.tsx` | 7 |
|
|
84
86
|
| `src/ui/kbd/kbd.test.tsx` | 7 |
|
|
85
87
|
| `src/ui/typography/typography.test.tsx` | 7 |
|
|
86
|
-
| `src/accessibility/axe-core.test.tsx` | 6 |
|
|
87
88
|
| `src/charts/charts.test.tsx` | 6 |
|
|
88
89
|
| `src/hooks/useClipboard/useClipboard.test.ts` | 6 |
|
|
89
90
|
| `src/hooks/useCountdown/useCountdown.test.ts` | 6 |
|
|
@@ -846,8 +847,8 @@ From this package directory in the monorepo:
|
|
|
846
847
|
|
|
847
848
|
- `pnpm build` (or `npm run build`) — production bundle via `tsup` (Rollup treeshake + `scripts/prepend-use-client.mjs` via `onSuccess` so each UI entry under `dist/ui/`, animation entry under `dist/animations/`, chart entry under `dist/charts/`, and `dist/ui/<name>/animated.*` starts with `"use client"` where needed)
|
|
848
849
|
- `pnpm dev` — `tsup` watch mode (same `onSuccess` hook after each rebuild)
|
|
849
|
-
- `pnpm test` / `pnpm test:watch` — **Vitest** and **Testing Library** unit tests // currently covered
|
|
850
|
-
- `pnpm test:a11y` — focused
|
|
850
|
+
- `pnpm test` / `pnpm test:watch` — **Vitest** and **Testing Library** unit tests // currently covered 751 test cases in total
|
|
851
|
+
- `pnpm test:a11y` — focused accessibility coverage for package-level UI primitives and compound components: **axe-core** audits for every interactive component plus **keyboard-interaction** tests (focus order, arrow-key nav, Home/End, Escape/Enter) for the compound components
|
|
851
852
|
- `pnpm check:tokens` — enforce the `--zui-*` token contract across design-system, variant, and local custom-property usage without generating a large checked-in token catalog
|
|
852
853
|
- **`pnpm run generate:registry`** — runs `scripts/generate-registry.mjs`, which reads **`uiComponentNames`**, **`uiAnimatedComponentNames`**, **`animationEntryNames`**, **`chartEntryNames`**, and **`hooksEntryNames`** from `tsup.config.ts`, applies fixed **`nameAliases`**, scans each component/chart source to build **`peerHints`**, and writes **`cli/registry.json`** (`components` + `animations` + `hooks` + `peerHints`). Run this after adding or renaming UI, animation, chart, or hook entries so the CLI stays in sync (the script prints counts).
|
|
853
854
|
|
|
@@ -17,7 +17,122 @@ function runCli(cwd: string, args: string[]): string {
|
|
|
17
17
|
});
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
function runCliError(cwd: string, args: string[]): string {
|
|
21
|
+
try {
|
|
22
|
+
runCli(cwd, args);
|
|
23
|
+
throw new Error("Expected CLI command to fail");
|
|
24
|
+
} catch (error) {
|
|
25
|
+
if (error && typeof error === "object" && "stderr" in error) {
|
|
26
|
+
return Buffer.isBuffer(error.stderr)
|
|
27
|
+
? error.stderr.toString("utf8")
|
|
28
|
+
: String(error.stderr);
|
|
29
|
+
}
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
20
34
|
describe("zentauri-ui CLI", () => {
|
|
35
|
+
it("should init with framework-aware Tailwind source guidance", () => {
|
|
36
|
+
const dir = mkdtempSync(join(tmpdir(), "zentauri-cli-init-next-"));
|
|
37
|
+
try {
|
|
38
|
+
const packageJsonPath = join(dir, "package.json");
|
|
39
|
+
execFileSync(
|
|
40
|
+
process.execPath,
|
|
41
|
+
[
|
|
42
|
+
"-e",
|
|
43
|
+
`require("node:fs").writeFileSync(${JSON.stringify(
|
|
44
|
+
packageJsonPath,
|
|
45
|
+
)}, JSON.stringify({ dependencies: { next: "16.0.0" } }))`,
|
|
46
|
+
],
|
|
47
|
+
{ cwd: dir },
|
|
48
|
+
);
|
|
49
|
+
const out = runCli(dir, ["init"]);
|
|
50
|
+
expect(out).toContain("Detected framework: Next.js");
|
|
51
|
+
expect(out).toContain('@source "./src/components/ui";');
|
|
52
|
+
expect(out).toContain("pnpm add react react-dom");
|
|
53
|
+
expect(existsSync(join(dir, "components.json"))).toBe(true);
|
|
54
|
+
} finally {
|
|
55
|
+
rmSync(dir, { recursive: true, force: true });
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("should detect Remix over Vite when both dependencies are present", () => {
|
|
60
|
+
const dir = mkdtempSync(join(tmpdir(), "zentauri-cli-init-remix-"));
|
|
61
|
+
try {
|
|
62
|
+
const packageJsonPath = join(dir, "package.json");
|
|
63
|
+
execFileSync(
|
|
64
|
+
process.execPath,
|
|
65
|
+
[
|
|
66
|
+
"-e",
|
|
67
|
+
`require("node:fs").writeFileSync(${JSON.stringify(
|
|
68
|
+
packageJsonPath,
|
|
69
|
+
)}, JSON.stringify({ dependencies: { "@remix-run/react": "2.0.0" }, devDependencies: { vite: "5.0.0" } }))`,
|
|
70
|
+
],
|
|
71
|
+
{ cwd: dir },
|
|
72
|
+
);
|
|
73
|
+
const out = runCli(dir, ["init"]);
|
|
74
|
+
expect(out).toContain("Detected framework: Remix");
|
|
75
|
+
expect(out).toContain('@source "./app/components/ui";');
|
|
76
|
+
} finally {
|
|
77
|
+
rmSync(dir, { recursive: true, force: true });
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("should list addable UI, chart, animation, and hook entries", () => {
|
|
82
|
+
const dir = mkdtempSync(join(tmpdir(), "zentauri-cli-list-"));
|
|
83
|
+
try {
|
|
84
|
+
const out = runCli(dir, ["list"]);
|
|
85
|
+
expect(out).toContain("UI components");
|
|
86
|
+
expect(out).toContain("buttons");
|
|
87
|
+
expect(out).toContain("charts/line");
|
|
88
|
+
expect(out).toContain("animations/fade-in");
|
|
89
|
+
expect(out).toContain("Hooks");
|
|
90
|
+
expect(out).toContain("useWindowSize");
|
|
91
|
+
} finally {
|
|
92
|
+
rmSync(dir, { recursive: true, force: true });
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("should print component info with install and import commands", () => {
|
|
97
|
+
const dir = mkdtempSync(join(tmpdir(), "zentauri-cli-info-"));
|
|
98
|
+
try {
|
|
99
|
+
const out = runCli(dir, ["info", "button"]);
|
|
100
|
+
expect(out).toContain("Name: buttons");
|
|
101
|
+
expect(out).toContain("npx zentauri-ui add button");
|
|
102
|
+
expect(out).toContain("@zentauri-ui/zentauri-components/ui/buttons");
|
|
103
|
+
expect(out).toContain("--animated");
|
|
104
|
+
} finally {
|
|
105
|
+
rmSync(dir, { recursive: true, force: true });
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("should add an animated component explicitly and report missing peers", () => {
|
|
110
|
+
const dir = mkdtempSync(join(tmpdir(), "zentauri-cli-add-animated-"));
|
|
111
|
+
try {
|
|
112
|
+
runCli(dir, ["init"]);
|
|
113
|
+
const out = runCli(dir, ["add", "--animated", "button"]);
|
|
114
|
+
expect(
|
|
115
|
+
existsSync(join(dir, "src/components/ui/buttons/animated/index.ts")),
|
|
116
|
+
).toBe(true);
|
|
117
|
+
expect(out).toContain("Including animated entry for buttons");
|
|
118
|
+
expect(out).toContain("Missing peer dependencies in this project");
|
|
119
|
+
expect(out).toContain("framer-motion");
|
|
120
|
+
} finally {
|
|
121
|
+
rmSync(dir, { recursive: true, force: true });
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("should reject --animated for components without an animated entry", () => {
|
|
126
|
+
const dir = mkdtempSync(join(tmpdir(), "zentauri-cli-add-static-only-"));
|
|
127
|
+
try {
|
|
128
|
+
runCli(dir, ["init"]);
|
|
129
|
+
const stderr = runCliError(dir, ["add", "--animated", "pagination"]);
|
|
130
|
+
expect(stderr).toContain('Component "pagination" has no animated entry');
|
|
131
|
+
} finally {
|
|
132
|
+
rmSync(dir, { recursive: true, force: true });
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
21
136
|
it("should init, add accordion, and rewrite internal imports", () => {
|
|
22
137
|
const dir = mkdtempSync(join(tmpdir(), "zentauri-cli-int-"));
|
|
23
138
|
try {
|
package/cli/index.mjs
CHANGED
|
@@ -136,7 +136,7 @@ function loadRegistry() {
|
|
|
136
136
|
*/
|
|
137
137
|
function printHelp() {
|
|
138
138
|
const reg = loadRegistry();
|
|
139
|
-
const componentsList = (reg.components ?? []).join("\n");
|
|
139
|
+
const componentsList = (reg.uiComponents ?? reg.components ?? []).join("\n");
|
|
140
140
|
const animationsList = (reg.animations ?? []).join("\n");
|
|
141
141
|
const hooksList = (reg.hooks ?? []).join("\n");
|
|
142
142
|
|
|
@@ -145,7 +145,10 @@ function printHelp() {
|
|
|
145
145
|
Usage:
|
|
146
146
|
zentauri-components init [options] Create components.json with defaults
|
|
147
147
|
zentauri-components add <component> [...] Copy UI components (and their hooks)
|
|
148
|
+
zentauri-components add --animated <component> Copy and validate an animated UI entry
|
|
148
149
|
zentauri-components add hook <hook> [...] Copy hook source only (plus transitive hook deps)
|
|
150
|
+
zentauri-components list Show addable components, charts, animations, and hooks
|
|
151
|
+
zentauri-components info <name> Show install/import details for one entry
|
|
149
152
|
zentauri-components theme <hex> Generate compact global --zui-* theme tokens
|
|
150
153
|
|
|
151
154
|
List of components:
|
|
@@ -301,6 +304,132 @@ function defaultConfig() {
|
|
|
301
304
|
};
|
|
302
305
|
}
|
|
303
306
|
|
|
307
|
+
const CORE_PEERS = [
|
|
308
|
+
"react",
|
|
309
|
+
"react-dom",
|
|
310
|
+
"class-variance-authority",
|
|
311
|
+
"clsx",
|
|
312
|
+
"tailwind-merge",
|
|
313
|
+
];
|
|
314
|
+
|
|
315
|
+
// Ordered by specificity: meta-frameworks first, generic Vite last. Vite-era
|
|
316
|
+
// Remix (and other Vite-based setups) also depend on `vite`, so a first-match
|
|
317
|
+
// scan with Vite earlier would misclassify them.
|
|
318
|
+
const FRAMEWORKS = [
|
|
319
|
+
{
|
|
320
|
+
name: "Next.js",
|
|
321
|
+
deps: ["next"],
|
|
322
|
+
files: ["next.config.js", "next.config.mjs", "next.config.ts"],
|
|
323
|
+
source: '@source "./src/components/ui";',
|
|
324
|
+
note: "Place the @source line in your global CSS file, often app/globals.css.",
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
name: "Remix",
|
|
328
|
+
deps: ["@remix-run/react", "@remix-run/node"],
|
|
329
|
+
files: ["remix.config.js", "remix.config.mjs"],
|
|
330
|
+
source: '@source "./app/components/ui";',
|
|
331
|
+
note: 'If components.json keeps the default src/ paths, use @source "./src/components/ui" instead.',
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
name: "Astro",
|
|
335
|
+
deps: ["astro"],
|
|
336
|
+
files: ["astro.config.js", "astro.config.mjs", "astro.config.ts"],
|
|
337
|
+
source: '@source "./src/components/ui";',
|
|
338
|
+
note: "Use this in the global stylesheet loaded by your Astro React integration.",
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
name: "Vite",
|
|
342
|
+
deps: ["vite"],
|
|
343
|
+
files: ["vite.config.js", "vite.config.mjs", "vite.config.ts"],
|
|
344
|
+
source: '@source "./src/components/ui";',
|
|
345
|
+
note: "Place the @source line in src/index.css or the CSS file imported by your app entry.",
|
|
346
|
+
},
|
|
347
|
+
];
|
|
348
|
+
|
|
349
|
+
function readProjectPackageJson(cwd) {
|
|
350
|
+
const packagePath = join(cwd, "package.json");
|
|
351
|
+
if (!existsSync(packagePath)) {
|
|
352
|
+
return undefined;
|
|
353
|
+
}
|
|
354
|
+
try {
|
|
355
|
+
return JSON.parse(readFileSync(packagePath, "utf8"));
|
|
356
|
+
} catch {
|
|
357
|
+
return undefined;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function packageDependencyMap(pkg) {
|
|
362
|
+
return {
|
|
363
|
+
...(pkg?.dependencies ?? {}),
|
|
364
|
+
...(pkg?.devDependencies ?? {}),
|
|
365
|
+
...(pkg?.peerDependencies ?? {}),
|
|
366
|
+
...(pkg?.optionalDependencies ?? {}),
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function detectFramework(cwd) {
|
|
371
|
+
const deps = packageDependencyMap(readProjectPackageJson(cwd));
|
|
372
|
+
return (
|
|
373
|
+
FRAMEWORKS.find((framework) => framework.deps.some((dep) => deps[dep])) ??
|
|
374
|
+
FRAMEWORKS.find((framework) =>
|
|
375
|
+
framework.files.some((file) => existsSync(join(cwd, file))),
|
|
376
|
+
)
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function printInitGuidance(cwd) {
|
|
381
|
+
const framework = detectFramework(cwd);
|
|
382
|
+
const missingCorePeers = getMissingDependencies(cwd, CORE_PEERS);
|
|
383
|
+
console.log(
|
|
384
|
+
`Detected framework: ${framework?.name ?? "React app (framework not detected)"}`,
|
|
385
|
+
);
|
|
386
|
+
console.log("\nInstall core peer dependencies:");
|
|
387
|
+
console.log(` pnpm add ${CORE_PEERS.join(" ")}`);
|
|
388
|
+
console.log(` npm install ${CORE_PEERS.join(" ")}`);
|
|
389
|
+
if (missingCorePeers.length > 0) {
|
|
390
|
+
console.log(` Missing now: ${missingCorePeers.join(", ")}`);
|
|
391
|
+
}
|
|
392
|
+
console.log("\nTailwind v4 source scanning:");
|
|
393
|
+
console.log(` ${framework?.source ?? '@source "./src/components/ui";'}`);
|
|
394
|
+
console.log(
|
|
395
|
+
` ${framework?.note ?? "Place the @source line in the global CSS file processed by Tailwind."}`,
|
|
396
|
+
);
|
|
397
|
+
console.log("\nOptional peers:");
|
|
398
|
+
console.log(" framer-motion # animated UI and animation entries");
|
|
399
|
+
console.log(" react-icons # icon-heavy components such as rating");
|
|
400
|
+
console.log(" recharts # chart entries");
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function findPackageJson(startDir) {
|
|
404
|
+
let d = startDir;
|
|
405
|
+
for (;;) {
|
|
406
|
+
const p = join(d, "package.json");
|
|
407
|
+
if (existsSync(p)) {
|
|
408
|
+
return p;
|
|
409
|
+
}
|
|
410
|
+
const parent = dirname(d);
|
|
411
|
+
if (parent === d) {
|
|
412
|
+
return undefined;
|
|
413
|
+
}
|
|
414
|
+
d = parent;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
function getMissingDependencies(cwd, deps) {
|
|
419
|
+
const packagePath = findPackageJson(cwd);
|
|
420
|
+
if (!packagePath) {
|
|
421
|
+
return deps;
|
|
422
|
+
}
|
|
423
|
+
try {
|
|
424
|
+
const installed = packageDependencyMap(
|
|
425
|
+
JSON.parse(readFileSync(packagePath, "utf8")),
|
|
426
|
+
);
|
|
427
|
+
return deps.filter((dep) => !installed[dep]);
|
|
428
|
+
} catch {
|
|
429
|
+
return deps;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
304
433
|
/**
|
|
305
434
|
* Ensures `add` has everything it needs to compute destination paths and rewrite
|
|
306
435
|
* imports. Throws a single clear error if the config is incomplete.
|
|
@@ -396,6 +525,85 @@ function resolveHookName(input, registry) {
|
|
|
396
525
|
);
|
|
397
526
|
}
|
|
398
527
|
|
|
528
|
+
function resolveAnyRegistryName(input, registry) {
|
|
529
|
+
try {
|
|
530
|
+
return { kind: "component", name: resolveComponentName(input, registry) };
|
|
531
|
+
} catch {
|
|
532
|
+
try {
|
|
533
|
+
return { kind: "hook", name: resolveHookName(input, registry) };
|
|
534
|
+
} catch {
|
|
535
|
+
throw new Error(`Unknown entry "${input}". Run: zentauri-ui list`);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
function isAnimatedComponent(name, registry) {
|
|
541
|
+
return (registry.animatedComponents ?? []).includes(name);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
function printList(registry) {
|
|
545
|
+
const ui = Array.from(
|
|
546
|
+
new Set([
|
|
547
|
+
...(registry.uiComponents ?? registry.components ?? []),
|
|
548
|
+
...(registry.animatedComponents ?? []).filter(
|
|
549
|
+
(name) => !(registry.uiComponents ?? []).includes(name),
|
|
550
|
+
),
|
|
551
|
+
]),
|
|
552
|
+
);
|
|
553
|
+
const charts = registry.charts ?? [];
|
|
554
|
+
const animations = registry.animations ?? [];
|
|
555
|
+
const hooks = registry.hooks ?? [];
|
|
556
|
+
|
|
557
|
+
console.log("UI components:");
|
|
558
|
+
console.log(ui.join("\n"));
|
|
559
|
+
console.log("\nCharts:");
|
|
560
|
+
console.log(charts.join("\n"));
|
|
561
|
+
console.log("\nAnimations:");
|
|
562
|
+
console.log(animations.join("\n"));
|
|
563
|
+
console.log("\nHooks:");
|
|
564
|
+
console.log(hooks.join("\n"));
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
function importPathFor(name, kind, registry) {
|
|
568
|
+
const uiComponents = registry.uiComponents ?? [];
|
|
569
|
+
|
|
570
|
+
if (kind === "hook") {
|
|
571
|
+
return `@zentauri-ui/zentauri-components/hooks/${name}`;
|
|
572
|
+
}
|
|
573
|
+
if (name.startsWith("charts/")) {
|
|
574
|
+
return `@zentauri-ui/zentauri-components/${name}`;
|
|
575
|
+
}
|
|
576
|
+
if (name.startsWith("animations/")) {
|
|
577
|
+
return `@zentauri-ui/zentauri-components/${name}`;
|
|
578
|
+
}
|
|
579
|
+
if (isAnimatedComponent(name, registry) && !uiComponents.includes(name)) {
|
|
580
|
+
return `@zentauri-ui/zentauri-components/ui/${name}/animated`;
|
|
581
|
+
}
|
|
582
|
+
return `@zentauri-ui/zentauri-components/ui/${name}`;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
function printInfo(input, registry) {
|
|
586
|
+
const { kind, name } = resolveAnyRegistryName(input, registry);
|
|
587
|
+
const peers = kind === "component" ? (registry.peerHints?.[name] ?? []) : [];
|
|
588
|
+
|
|
589
|
+
console.log(`Name: ${name}`);
|
|
590
|
+
console.log(`Type: ${kind === "hook" ? "hook" : "addable entry"}`);
|
|
591
|
+
console.log(`Add command: npx zentauri-ui add ${input}`);
|
|
592
|
+
if (kind === "hook") {
|
|
593
|
+
console.log(`Hook-only command: npx zentauri-ui add hook ${name}`);
|
|
594
|
+
}
|
|
595
|
+
console.log(`Import: ${importPathFor(name, kind, registry)}`);
|
|
596
|
+
if (kind === "component" && isAnimatedComponent(name, registry)) {
|
|
597
|
+
console.log(
|
|
598
|
+
`Animated import: @zentauri-ui/zentauri-components/ui/${name}/animated`,
|
|
599
|
+
);
|
|
600
|
+
console.log(`Animated vendoring: npx zentauri-ui add --animated ${input}`);
|
|
601
|
+
}
|
|
602
|
+
if (peers.length > 0) {
|
|
603
|
+
console.log(`Peer hints: ${peers.join(", ")}`);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
399
607
|
const THEME_COLOR_NAMES = [
|
|
400
608
|
"blue",
|
|
401
609
|
"cyan",
|
|
@@ -901,7 +1109,7 @@ const PEER_HINT_REASONS = {
|
|
|
901
1109
|
* @param {object} registry — from `loadRegistry()`
|
|
902
1110
|
* @param {object} config — validated `components.json` (for resolvedPaths.ui)
|
|
903
1111
|
*/
|
|
904
|
-
function printAdoptionHints(resolvedNames, registry, config) {
|
|
1112
|
+
function printAdoptionHints(resolvedNames, registry, config, configDir) {
|
|
905
1113
|
const peerHints = registry.peerHints ?? {};
|
|
906
1114
|
/** @type {Map<string, string[]>} peer -> component names that need it */
|
|
907
1115
|
const needed = new Map();
|
|
@@ -923,6 +1131,14 @@ function printAdoptionHints(resolvedNames, registry, config) {
|
|
|
923
1131
|
);
|
|
924
1132
|
}
|
|
925
1133
|
console.log(` Install with: npm i ${[...needed.keys()].join(" ")}`);
|
|
1134
|
+
const missing = getMissingDependencies(configDir, [...needed.keys()]);
|
|
1135
|
+
if (missing.length > 0) {
|
|
1136
|
+
console.log("\nMissing peer dependencies in this project:");
|
|
1137
|
+
for (const peer of missing) {
|
|
1138
|
+
console.log(` - ${peer}`);
|
|
1139
|
+
}
|
|
1140
|
+
console.log(` Install with: pnpm add ${missing.join(" ")}`);
|
|
1141
|
+
}
|
|
926
1142
|
}
|
|
927
1143
|
|
|
928
1144
|
const uiPath = config?.resolvedPaths?.ui ?? "your components directory";
|
|
@@ -962,6 +1178,7 @@ async function cmdInit(cwd) {
|
|
|
962
1178
|
const body = `${JSON.stringify(defaultConfig(), null, 2)}\n`;
|
|
963
1179
|
await writeFile(target, body, "utf8");
|
|
964
1180
|
console.log(`Wrote ${target}`);
|
|
1181
|
+
printInitGuidance(cwd);
|
|
965
1182
|
}
|
|
966
1183
|
|
|
967
1184
|
/**
|
|
@@ -983,7 +1200,7 @@ async function cmdInit(cwd) {
|
|
|
983
1200
|
* // No components.json in cwd or parents — stderr:
|
|
984
1201
|
* // No components.json found. Run: zentauri-components init (or: zentauri-ui init)
|
|
985
1202
|
*/
|
|
986
|
-
async function cmdAdd(names, cwd) {
|
|
1203
|
+
async function cmdAdd(names, cwd, options = {}) {
|
|
987
1204
|
const configPath = await findComponentsJson(cwd);
|
|
988
1205
|
if (!configPath) {
|
|
989
1206
|
console.error(
|
|
@@ -997,6 +1214,7 @@ async function cmdAdd(names, cwd) {
|
|
|
997
1214
|
validateConfig(config);
|
|
998
1215
|
|
|
999
1216
|
const registry = loadRegistry();
|
|
1217
|
+
const animated = Boolean(options.animated);
|
|
1000
1218
|
const hookMode = names.length > 0 && names[0].toLowerCase() === "hook";
|
|
1001
1219
|
const payload = hookMode ? names.slice(1) : names;
|
|
1002
1220
|
|
|
@@ -1009,6 +1227,11 @@ async function cmdAdd(names, cwd) {
|
|
|
1009
1227
|
}
|
|
1010
1228
|
|
|
1011
1229
|
if (hookMode) {
|
|
1230
|
+
if (animated) {
|
|
1231
|
+
console.error("--animated can only be used with UI component entries.");
|
|
1232
|
+
process.exitCode = 1;
|
|
1233
|
+
return;
|
|
1234
|
+
}
|
|
1012
1235
|
await ensureUtilsFile(config, configDir, packageRoot);
|
|
1013
1236
|
const resolvedHooks = payload.map((n) => resolveHookName(n, registry));
|
|
1014
1237
|
const finalHooks = await collectHookTransitiveClosure(
|
|
@@ -1024,6 +1247,15 @@ async function cmdAdd(names, cwd) {
|
|
|
1024
1247
|
}
|
|
1025
1248
|
|
|
1026
1249
|
const resolvedNames = payload.map((n) => resolveComponentName(n, registry));
|
|
1250
|
+
if (animated) {
|
|
1251
|
+
for (const name of resolvedNames) {
|
|
1252
|
+
if (!isAnimatedComponent(name, registry)) {
|
|
1253
|
+
console.error(`Component "${name}" has no animated entry.`);
|
|
1254
|
+
process.exitCode = 1;
|
|
1255
|
+
return;
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1027
1259
|
|
|
1028
1260
|
await ensureUtilsFile(config, configDir, packageRoot);
|
|
1029
1261
|
await copyDesignSystemFolder(config, configDir, packageRoot);
|
|
@@ -1031,6 +1263,9 @@ async function cmdAdd(names, cwd) {
|
|
|
1031
1263
|
const allHooks = new Set();
|
|
1032
1264
|
for (const name of resolvedNames) {
|
|
1033
1265
|
console.log(`Adding ${name}…`);
|
|
1266
|
+
if (animated) {
|
|
1267
|
+
console.log(`Including animated entry for ${name}…`);
|
|
1268
|
+
}
|
|
1034
1269
|
const uh = await copyUiComponent(name, config, configDir, packageRoot);
|
|
1035
1270
|
for (const h of uh) {
|
|
1036
1271
|
allHooks.add(h);
|
|
@@ -1047,7 +1282,7 @@ async function cmdAdd(names, cwd) {
|
|
|
1047
1282
|
}
|
|
1048
1283
|
|
|
1049
1284
|
console.log("Done.");
|
|
1050
|
-
printAdoptionHints(resolvedNames, registry, config);
|
|
1285
|
+
printAdoptionHints(resolvedNames, registry, config, configDir);
|
|
1051
1286
|
}
|
|
1052
1287
|
|
|
1053
1288
|
async function cmdTheme(hex, options, cwd) {
|
|
@@ -1102,6 +1337,7 @@ async function main() {
|
|
|
1102
1337
|
out: { type: "string" },
|
|
1103
1338
|
selector: { type: "string" },
|
|
1104
1339
|
dark: { type: "string" },
|
|
1340
|
+
animated: { type: "boolean" },
|
|
1105
1341
|
},
|
|
1106
1342
|
});
|
|
1107
1343
|
|
|
@@ -1131,6 +1367,19 @@ async function main() {
|
|
|
1131
1367
|
await cmdInit(cwd);
|
|
1132
1368
|
return;
|
|
1133
1369
|
}
|
|
1370
|
+
if (cmd === "list") {
|
|
1371
|
+
printList(loadRegistry());
|
|
1372
|
+
return;
|
|
1373
|
+
}
|
|
1374
|
+
if (cmd === "info") {
|
|
1375
|
+
if (rest.length === 0) {
|
|
1376
|
+
console.error("Usage: zentauri-components info <component|hook>");
|
|
1377
|
+
process.exitCode = 1;
|
|
1378
|
+
return;
|
|
1379
|
+
}
|
|
1380
|
+
printInfo(rest[0], loadRegistry());
|
|
1381
|
+
return;
|
|
1382
|
+
}
|
|
1134
1383
|
if (cmd === "add") {
|
|
1135
1384
|
if (rest.length === 0) {
|
|
1136
1385
|
console.error(
|
|
@@ -1139,7 +1388,7 @@ async function main() {
|
|
|
1139
1388
|
process.exitCode = 1;
|
|
1140
1389
|
return;
|
|
1141
1390
|
}
|
|
1142
|
-
await cmdAdd(rest, cwd);
|
|
1391
|
+
await cmdAdd(rest, cwd, { animated: values.animated });
|
|
1143
1392
|
return;
|
|
1144
1393
|
}
|
|
1145
1394
|
if (cmd === "theme") {
|
package/cli/registry.json
CHANGED
|
@@ -1,6 +1,90 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"description": "Addable UI components (src/ui), animation entries (src/animations/*), chart entries (src/charts/*), and hooks (src/hooks). Generated by scripts/generate-registry.mjs.",
|
|
4
|
+
"uiComponents": [
|
|
5
|
+
"accordion",
|
|
6
|
+
"alert",
|
|
7
|
+
"animated-number",
|
|
8
|
+
"avatar",
|
|
9
|
+
"badge",
|
|
10
|
+
"breadcrumb",
|
|
11
|
+
"buttons",
|
|
12
|
+
"card",
|
|
13
|
+
"checkbox",
|
|
14
|
+
"combobox",
|
|
15
|
+
"command",
|
|
16
|
+
"context-menu",
|
|
17
|
+
"copy-button",
|
|
18
|
+
"divider",
|
|
19
|
+
"drawer",
|
|
20
|
+
"dropdown",
|
|
21
|
+
"dynamic-stepper",
|
|
22
|
+
"empty-state",
|
|
23
|
+
"file-upload",
|
|
24
|
+
"inputs",
|
|
25
|
+
"kbd",
|
|
26
|
+
"marquee",
|
|
27
|
+
"modal",
|
|
28
|
+
"otp-input",
|
|
29
|
+
"pagination",
|
|
30
|
+
"popover",
|
|
31
|
+
"progress",
|
|
32
|
+
"radio-group",
|
|
33
|
+
"rating",
|
|
34
|
+
"scroll-area",
|
|
35
|
+
"search",
|
|
36
|
+
"select",
|
|
37
|
+
"skeleton",
|
|
38
|
+
"slider",
|
|
39
|
+
"table",
|
|
40
|
+
"tabs",
|
|
41
|
+
"timeline",
|
|
42
|
+
"toast",
|
|
43
|
+
"toggle",
|
|
44
|
+
"tooltip",
|
|
45
|
+
"tree-view",
|
|
46
|
+
"typography"
|
|
47
|
+
],
|
|
48
|
+
"animatedComponents": [
|
|
49
|
+
"accordion",
|
|
50
|
+
"alert",
|
|
51
|
+
"avatar",
|
|
52
|
+
"badge",
|
|
53
|
+
"buttons",
|
|
54
|
+
"card",
|
|
55
|
+
"checkbox",
|
|
56
|
+
"command",
|
|
57
|
+
"copy-button",
|
|
58
|
+
"divider",
|
|
59
|
+
"drawer",
|
|
60
|
+
"empty-state",
|
|
61
|
+
"inputs",
|
|
62
|
+
"kbd",
|
|
63
|
+
"modal",
|
|
64
|
+
"popover",
|
|
65
|
+
"progress",
|
|
66
|
+
"radio-group",
|
|
67
|
+
"skeleton",
|
|
68
|
+
"spinner",
|
|
69
|
+
"table",
|
|
70
|
+
"tabs",
|
|
71
|
+
"timeline",
|
|
72
|
+
"toast",
|
|
73
|
+
"toggle",
|
|
74
|
+
"tooltip",
|
|
75
|
+
"tree-view"
|
|
76
|
+
],
|
|
77
|
+
"charts": [
|
|
78
|
+
"charts/area",
|
|
79
|
+
"charts/bar",
|
|
80
|
+
"charts/bubble",
|
|
81
|
+
"charts/funnel",
|
|
82
|
+
"charts/line",
|
|
83
|
+
"charts/pie",
|
|
84
|
+
"charts/radar",
|
|
85
|
+
"charts/scatter",
|
|
86
|
+
"charts/stacked-bar"
|
|
87
|
+
],
|
|
4
88
|
"components": [
|
|
5
89
|
"accordion",
|
|
6
90
|
"alert",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { buttonVariants } from './chunk-
|
|
1
|
+
import { buttonVariants } from './chunk-BPSW3SRE.mjs';
|
|
2
2
|
import { cn } from './chunk-4D54YOL6.mjs';
|
|
3
3
|
import { isValidElement, cloneElement } from 'react';
|
|
4
4
|
import { jsx } from 'react/jsx-runtime';
|
|
@@ -97,5 +97,5 @@ var Button = (props) => {
|
|
|
97
97
|
Button.displayName = "Button";
|
|
98
98
|
|
|
99
99
|
export { Button };
|
|
100
|
-
//# sourceMappingURL=chunk-
|
|
101
|
-
//# sourceMappingURL=chunk-
|
|
100
|
+
//# sourceMappingURL=chunk-2P4WPYC3.mjs.map
|
|
101
|
+
//# sourceMappingURL=chunk-2P4WPYC3.mjs.map
|