@ansh-ui/cli 0.2.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/LICENSE +21 -0
- package/dist/cli.js +680 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +161 -0
- package/dist/index.js +371 -0
- package/dist/index.js.map +1 -0
- package/package.json +48 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs';
|
|
2
|
+
import { join, dirname, resolve } from 'path';
|
|
3
|
+
import { effects, behaviours, parseColor, rgbToOklch, createScale, checkContrast, toOklchString, toHex, harmony, generatePalette, createGradient } from '@ansh-ui/core';
|
|
4
|
+
|
|
5
|
+
// src/commands/add.ts
|
|
6
|
+
function findRoot(cwd) {
|
|
7
|
+
let current = resolve(cwd);
|
|
8
|
+
for (let depth = 0; depth < 10; depth++) {
|
|
9
|
+
if (existsSync(join(current, "package.json"))) return current;
|
|
10
|
+
const parent = resolve(current, "..");
|
|
11
|
+
if (parent === current) break;
|
|
12
|
+
current = parent;
|
|
13
|
+
}
|
|
14
|
+
return resolve(cwd);
|
|
15
|
+
}
|
|
16
|
+
function readJson(path) {
|
|
17
|
+
try {
|
|
18
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
19
|
+
} catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function detectProject(cwd = process.cwd()) {
|
|
24
|
+
const root = findRoot(cwd);
|
|
25
|
+
const pkg = readJson(join(root, "package.json")) ?? {};
|
|
26
|
+
const deps = {
|
|
27
|
+
...pkg.dependencies ?? {},
|
|
28
|
+
...pkg.devDependencies ?? {}
|
|
29
|
+
};
|
|
30
|
+
const framework = deps.nuxt ? "nuxt" : deps.next ? "next" : deps.vue ? "vue" : deps.react ? "react" : "unknown";
|
|
31
|
+
const typescript = Boolean(deps.typescript) || existsSync(join(root, "tsconfig.json"));
|
|
32
|
+
const packageManager = existsSync(join(root, "pnpm-lock.yaml")) ? "pnpm" : existsSync(join(root, "bun.lockb")) || existsSync(join(root, "bun.lock")) ? "bun" : existsSync(join(root, "yarn.lock")) ? "yarn" : "npm";
|
|
33
|
+
return {
|
|
34
|
+
root,
|
|
35
|
+
framework,
|
|
36
|
+
typescript,
|
|
37
|
+
componentsDir: detectComponentsDir(root, framework),
|
|
38
|
+
alias: detectAlias(root),
|
|
39
|
+
packageManager,
|
|
40
|
+
installed: Boolean(
|
|
41
|
+
deps["@ansh-ui/react"] || deps["@ansh-ui/vue"] || deps["@ansh-ui/nuxt"] || deps["@ansh-ui/core"]
|
|
42
|
+
)
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function detectComponentsDir(root, framework) {
|
|
46
|
+
const candidates = framework === "nuxt" ? ["components", "app/components", "src/components"] : ["src/components", "components", "app/components", "src/lib/components", "app/ui", "src/ui"];
|
|
47
|
+
for (const candidate of candidates) {
|
|
48
|
+
if (existsSync(join(root, candidate))) return candidate;
|
|
49
|
+
}
|
|
50
|
+
if (existsSync(join(root, "src"))) return "src/components";
|
|
51
|
+
if (framework === "nuxt") return "components";
|
|
52
|
+
return "components";
|
|
53
|
+
}
|
|
54
|
+
function detectAlias(root) {
|
|
55
|
+
for (const file of ["tsconfig.json", "jsconfig.json"]) {
|
|
56
|
+
const config = readJson(join(root, file));
|
|
57
|
+
if (!config) continue;
|
|
58
|
+
const compilerOptions = config.compilerOptions;
|
|
59
|
+
const paths = compilerOptions?.paths;
|
|
60
|
+
if (!paths) continue;
|
|
61
|
+
for (const pattern of Object.keys(paths)) {
|
|
62
|
+
if (pattern.endsWith("/*")) return pattern.slice(0, -1);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
var COMPONENT_NAMES = {
|
|
68
|
+
aurora: "AuroraBackground",
|
|
69
|
+
"mesh-gradient": "MeshGradient",
|
|
70
|
+
"animated-gradient": "AnimatedGradient",
|
|
71
|
+
"particle-field": "ParticleField",
|
|
72
|
+
constellation: "Constellation",
|
|
73
|
+
meteors: "MeteorShower",
|
|
74
|
+
rain: "Rain",
|
|
75
|
+
snow: "Snow",
|
|
76
|
+
fireflies: "Fireflies",
|
|
77
|
+
waves: "Waves",
|
|
78
|
+
blob: "LiquidBlob",
|
|
79
|
+
"noise-layer": "NoiseLayer",
|
|
80
|
+
spotlight: "Spotlight",
|
|
81
|
+
magnetic: "MagneticButton",
|
|
82
|
+
tilt: "TiltCard",
|
|
83
|
+
ripple: "Ripple",
|
|
84
|
+
marquee: "Marquee",
|
|
85
|
+
"scroll-reveal": "ScrollReveal",
|
|
86
|
+
parallax: "Parallax",
|
|
87
|
+
"split-text": "SplitText"
|
|
88
|
+
};
|
|
89
|
+
function componentNameFor(name) {
|
|
90
|
+
return COMPONENT_NAMES[name] ?? name.replace(/(^|-)([a-z])/g, (_, __, letter) => letter.toUpperCase());
|
|
91
|
+
}
|
|
92
|
+
function listCatalogue(filter) {
|
|
93
|
+
const entries = [];
|
|
94
|
+
if (filter !== "behaviour") {
|
|
95
|
+
for (const [name, definition] of Object.entries(effects)) {
|
|
96
|
+
entries.push({
|
|
97
|
+
name,
|
|
98
|
+
kind: "effect",
|
|
99
|
+
component: componentNameFor(name),
|
|
100
|
+
options: describeOptions(definition.defaults)
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (filter !== "effect") {
|
|
105
|
+
for (const [name, definition] of Object.entries(behaviours)) {
|
|
106
|
+
entries.push({
|
|
107
|
+
name,
|
|
108
|
+
kind: "behaviour",
|
|
109
|
+
component: componentNameFor(name),
|
|
110
|
+
options: describeOptions(definition.defaults)
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
115
|
+
}
|
|
116
|
+
function describeOptions(defaults) {
|
|
117
|
+
return Object.entries(defaults).map(([name, value]) => ({ name, default: value })).sort((a, b) => a.name.localeCompare(b.name));
|
|
118
|
+
}
|
|
119
|
+
function findEntry(query) {
|
|
120
|
+
const needle = query.toLowerCase();
|
|
121
|
+
return listCatalogue().find(
|
|
122
|
+
(entry) => entry.name.toLowerCase() === needle || entry.component.toLowerCase() === needle
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// src/commands/add.ts
|
|
127
|
+
function addComponent(options) {
|
|
128
|
+
const project = detectProject(options.cwd);
|
|
129
|
+
const entry = findEntry(options.name);
|
|
130
|
+
if (!entry) {
|
|
131
|
+
throw new Error(
|
|
132
|
+
`Unknown component "${options.name}". Run \`ansh list\` to see what's available.`
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
if (project.framework === "unknown") {
|
|
136
|
+
throw new Error(
|
|
137
|
+
"Could not detect a React, Next, Vue or Nuxt project. Run this from your project root, or install the framework first."
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
const isVue = project.framework === "vue" || project.framework === "nuxt";
|
|
141
|
+
const componentName = options.as ?? entry.component;
|
|
142
|
+
const extension = isVue ? "vue" : project.typescript ? "tsx" : "jsx";
|
|
143
|
+
const directory = options.dir ?? project.componentsDir;
|
|
144
|
+
const path = join(project.root, directory, `${componentName}.${extension}`);
|
|
145
|
+
const contents = isVue ? renderVue(componentName, entry.component, entry.options, project) : renderReact(componentName, entry.component, entry.options, project);
|
|
146
|
+
const missingDependencies = missingFor(project);
|
|
147
|
+
if (options.dryRun) {
|
|
148
|
+
return { path, contents, written: false, missingDependencies };
|
|
149
|
+
}
|
|
150
|
+
if (existsSync(path) && !options.force) {
|
|
151
|
+
throw new Error(`${path} already exists. Pass --force to overwrite.`);
|
|
152
|
+
}
|
|
153
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
154
|
+
writeFileSync(path, contents, "utf8");
|
|
155
|
+
return { path, contents, written: true, missingDependencies };
|
|
156
|
+
}
|
|
157
|
+
function missingFor(project) {
|
|
158
|
+
if (project.installed) return [];
|
|
159
|
+
switch (project.framework) {
|
|
160
|
+
case "nuxt":
|
|
161
|
+
return ["@ansh-ui/nuxt"];
|
|
162
|
+
case "vue":
|
|
163
|
+
return ["@ansh-ui/vue", "@ansh-ui/tokens"];
|
|
164
|
+
default:
|
|
165
|
+
return ["@ansh-ui/react", "@ansh-ui/tokens"];
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
function literal(value) {
|
|
169
|
+
if (typeof value === "string") return JSON.stringify(value);
|
|
170
|
+
if (Array.isArray(value)) return `[${value.map(literal).join(", ")}]`;
|
|
171
|
+
if (value === null) return "null";
|
|
172
|
+
if (typeof value === "object") return JSON.stringify(value);
|
|
173
|
+
return String(value);
|
|
174
|
+
}
|
|
175
|
+
var INTERESTING_OPTIONS = /* @__PURE__ */ new Set([
|
|
176
|
+
"colors",
|
|
177
|
+
"speed",
|
|
178
|
+
"intensity",
|
|
179
|
+
"opacity",
|
|
180
|
+
"blur",
|
|
181
|
+
"seed",
|
|
182
|
+
"strength",
|
|
183
|
+
"radius",
|
|
184
|
+
"max",
|
|
185
|
+
"distance",
|
|
186
|
+
"amount",
|
|
187
|
+
"count"
|
|
188
|
+
]);
|
|
189
|
+
function pickDefaults(options) {
|
|
190
|
+
return options.filter((option) => INTERESTING_OPTIONS.has(option.name));
|
|
191
|
+
}
|
|
192
|
+
function renderReact(componentName, sourceComponent, options, project) {
|
|
193
|
+
const ts = project.typescript;
|
|
194
|
+
const defaults = pickDefaults(options);
|
|
195
|
+
const defaultLines = defaults.map((option) => ` ${option.name}: ${literal(option.default)},`).join("\n");
|
|
196
|
+
const propsType = ts ? `
|
|
197
|
+
type ${componentName}Props = ComponentProps<typeof ${sourceComponent}>
|
|
198
|
+
` : "";
|
|
199
|
+
const signature = ts ? `export function ${componentName}({ children, ...props }: ${componentName}Props) {` : `export function ${componentName}({ children, ...props }) {`;
|
|
200
|
+
const typeImport = ts ? `import type { ComponentProps } from 'react'
|
|
201
|
+
` : "";
|
|
202
|
+
return `${typeImport}import { ${sourceComponent} } from '@ansh-ui/react'
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* ${componentName} \u2014 a project-local wrapper around Ansh's ${sourceComponent}.
|
|
206
|
+
*
|
|
207
|
+
* Edit the defaults below to match your design system. Every option remains overridable per
|
|
208
|
+
* instance, because props spread after the defaults.
|
|
209
|
+
*
|
|
210
|
+
* Generated by \`ansh add\`. Safe to edit \u2014 it will not be regenerated unless you ask.
|
|
211
|
+
*/
|
|
212
|
+
const defaults = {
|
|
213
|
+
${defaultLines}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
${propsType}
|
|
217
|
+
${signature}
|
|
218
|
+
return (
|
|
219
|
+
<${sourceComponent} {...defaults} {...props}>
|
|
220
|
+
{children}
|
|
221
|
+
</${sourceComponent}>
|
|
222
|
+
)
|
|
223
|
+
}
|
|
224
|
+
`;
|
|
225
|
+
}
|
|
226
|
+
function renderVue(componentName, sourceComponent, options, project) {
|
|
227
|
+
const defaults = pickDefaults(options);
|
|
228
|
+
const defaultLines = defaults.map((option) => ` ${option.name}: ${literal(option.default)},`).join("\n");
|
|
229
|
+
const lang = project.typescript ? ' lang="ts"' : "";
|
|
230
|
+
const importLine = project.framework === "nuxt" ? `// ${sourceComponent} is auto-imported by @ansh-ui/nuxt.` : `import { ${sourceComponent} } from '@ansh-ui/vue'`;
|
|
231
|
+
return `<script setup${lang}>
|
|
232
|
+
${importLine}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* ${componentName} \u2014 a project-local wrapper around Ansh's ${sourceComponent}.
|
|
236
|
+
*
|
|
237
|
+
* Edit the defaults below to match your design system. Every option remains overridable per
|
|
238
|
+
* instance, because \`$attrs\` is bound after the defaults.
|
|
239
|
+
*
|
|
240
|
+
* Generated by \`ansh add\`. Safe to edit \u2014 it will not be regenerated unless you ask.
|
|
241
|
+
*/
|
|
242
|
+
const defaults = {
|
|
243
|
+
${defaultLines}
|
|
244
|
+
}
|
|
245
|
+
</script>
|
|
246
|
+
|
|
247
|
+
<template>
|
|
248
|
+
<${sourceComponent} v-bind="{ ...defaults, ...$attrs }">
|
|
249
|
+
<slot />
|
|
250
|
+
</${sourceComponent}>
|
|
251
|
+
</template>
|
|
252
|
+
`;
|
|
253
|
+
}
|
|
254
|
+
var TAILWIND_KEYS = ["50", "100", "200", "300", "400", "500", "600", "700", "800", "900", "950"];
|
|
255
|
+
function generateTheme(options) {
|
|
256
|
+
const base = parseColor(options.color);
|
|
257
|
+
const baseLightness = rgbToOklch(base).l;
|
|
258
|
+
const scale = createScale(base, {
|
|
259
|
+
steps: 11,
|
|
260
|
+
hueShift: options.hueShift ?? 0,
|
|
261
|
+
chromaFalloff: options.chromaFalloff ?? 0.35
|
|
262
|
+
});
|
|
263
|
+
const unsafeOnLight = [];
|
|
264
|
+
const unsafeOnDark = [];
|
|
265
|
+
const lines = [];
|
|
266
|
+
let nearestStep = "500";
|
|
267
|
+
let nearestDistance = Infinity;
|
|
268
|
+
scale.colors.forEach((color, index) => {
|
|
269
|
+
const key = TAILWIND_KEYS[index];
|
|
270
|
+
const onLight = checkContrast(color, "#ffffff");
|
|
271
|
+
const onDark = checkContrast(color, "#0b0b0f");
|
|
272
|
+
if (!onLight.passes) unsafeOnLight.push(key);
|
|
273
|
+
if (!onDark.passes) unsafeOnDark.push(key);
|
|
274
|
+
const distance = Math.abs(rgbToOklch(color).l - baseLightness);
|
|
275
|
+
if (distance < nearestDistance) {
|
|
276
|
+
nearestDistance = distance;
|
|
277
|
+
nearestStep = key;
|
|
278
|
+
}
|
|
279
|
+
lines.push(
|
|
280
|
+
` --ansh-brand-${key}: ${toOklchString(color)}; /* ${toHex(color)} on-white ${onLight.ratio.toFixed(2)}:1 on-black ${onDark.ratio.toFixed(2)}:1 */`
|
|
281
|
+
);
|
|
282
|
+
});
|
|
283
|
+
let accentLines = "";
|
|
284
|
+
if (options.withAccent) {
|
|
285
|
+
const [, accent] = harmony(base, "splitComplementary");
|
|
286
|
+
const accentScale = createScale(accent, { steps: 11 });
|
|
287
|
+
accentLines = "\n /* Accent \u2014 split-complementary partner to the brand hue. */\n" + accentScale.colors.map((color, index) => ` --ansh-accent-${TAILWIND_KEYS[index]}: ${toOklchString(color)};`).join("\n") + "\n";
|
|
288
|
+
}
|
|
289
|
+
const css = `/**
|
|
290
|
+
* Ansh theme override.
|
|
291
|
+
*
|
|
292
|
+
* Generated by \`ansh theme ${options.color}\`.
|
|
293
|
+
* Import this *after* \`@ansh-ui/tokens/ansh.css\` so it wins in the cascade.
|
|
294
|
+
*
|
|
295
|
+
* Contrast ratios are annotated per step. Steps marked unsafe cannot legally hold body text on the
|
|
296
|
+
* corresponding surface \u2014 use them for fills, borders and large display type instead.
|
|
297
|
+
*/
|
|
298
|
+
|
|
299
|
+
:root {
|
|
300
|
+
${lines.join("\n")}
|
|
301
|
+
${accentLines}
|
|
302
|
+
/* Wire the semantic accent tokens to the generated scale. */
|
|
303
|
+
--ansh-accent-subtle: var(--ansh-brand-50);
|
|
304
|
+
--ansh-accent-muted: var(--ansh-brand-100);
|
|
305
|
+
--ansh-accent-default: var(--ansh-brand-600);
|
|
306
|
+
--ansh-accent-hover: var(--ansh-brand-700);
|
|
307
|
+
--ansh-accent-active: var(--ansh-brand-800);
|
|
308
|
+
--ansh-accent-text: var(--ansh-brand-700);
|
|
309
|
+
--ansh-border-focus: var(--ansh-brand-600);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Dark theme.
|
|
314
|
+
*
|
|
315
|
+
* Not an inversion. Accents step *lighter* rather than mirroring their light-mode index, because
|
|
316
|
+
* saturated colour reads as less vivid against a dark background (Helmholtz\u2013Kohlrausch).
|
|
317
|
+
*/
|
|
318
|
+
[data-theme='dark'] {
|
|
319
|
+
--ansh-accent-subtle: color-mix(in oklab, var(--ansh-brand-950) 40%, transparent);
|
|
320
|
+
--ansh-accent-muted: var(--ansh-brand-950);
|
|
321
|
+
--ansh-accent-default: var(--ansh-brand-500);
|
|
322
|
+
--ansh-accent-hover: var(--ansh-brand-400);
|
|
323
|
+
--ansh-accent-active: var(--ansh-brand-300);
|
|
324
|
+
--ansh-accent-text: var(--ansh-brand-300);
|
|
325
|
+
--ansh-border-focus: var(--ansh-brand-400);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
@media (prefers-color-scheme: dark) {
|
|
329
|
+
:root:not([data-theme]) {
|
|
330
|
+
--ansh-accent-subtle: color-mix(in oklab, var(--ansh-brand-950) 40%, transparent);
|
|
331
|
+
--ansh-accent-muted: var(--ansh-brand-950);
|
|
332
|
+
--ansh-accent-default: var(--ansh-brand-500);
|
|
333
|
+
--ansh-accent-hover: var(--ansh-brand-400);
|
|
334
|
+
--ansh-accent-active: var(--ansh-brand-300);
|
|
335
|
+
--ansh-accent-text: var(--ansh-brand-300);
|
|
336
|
+
--ansh-border-focus: var(--ansh-brand-400);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
`;
|
|
340
|
+
if (options.out) {
|
|
341
|
+
mkdirSync(dirname(options.out), { recursive: true });
|
|
342
|
+
writeFileSync(options.out, css, "utf8");
|
|
343
|
+
}
|
|
344
|
+
return { css, unsafeOnLight, unsafeOnDark, nearestStep };
|
|
345
|
+
}
|
|
346
|
+
function generatePaletteReport(options = {}) {
|
|
347
|
+
const parsed = options.colors && options.colors.length > 0 ? options.colors.map((color) => parseColor(color)) : generatePalette({
|
|
348
|
+
count: options.count ?? 5,
|
|
349
|
+
seed: options.seed ?? 1,
|
|
350
|
+
lightness: options.lightness ?? 0.65,
|
|
351
|
+
saturation: options.saturation ?? 0.85,
|
|
352
|
+
spread: options.spread ?? 120
|
|
353
|
+
});
|
|
354
|
+
const colors = parsed.map((color) => {
|
|
355
|
+
const lch = rgbToOklch(color);
|
|
356
|
+
return {
|
|
357
|
+
hex: toHex(color),
|
|
358
|
+
oklch: toOklchString(color),
|
|
359
|
+
lightness: Number(lch.l.toFixed(4)),
|
|
360
|
+
chroma: Number(lch.c.toFixed(4)),
|
|
361
|
+
hue: Number(lch.h.toFixed(2))
|
|
362
|
+
};
|
|
363
|
+
});
|
|
364
|
+
const gradientSamples = options.gradient === false || parsed.length < 2 ? [] : createGradient(parsed, { space: "oklch" }).sample(9).map((color) => toHex(color));
|
|
365
|
+
const snippet = `colors={[${colors.map((c) => `'${c.hex}'`).join(", ")}]}`;
|
|
366
|
+
return { colors, gradientSamples, snippet };
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
export { addComponent, detectProject, generatePaletteReport, generateTheme, listCatalogue };
|
|
370
|
+
//# sourceMappingURL=index.js.map
|
|
371
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/project.ts","../src/commands/list.ts","../src/commands/add.ts","../src/commands/theme.ts","../src/commands/palette.ts"],"names":["join","existsSync","mkdirSync","dirname","writeFileSync","parseColor","rgbToOklch","toHex","toOklchString"],"mappings":"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","file":"index.js","sourcesContent":["import { existsSync, readFileSync } from 'node:fs'\nimport { join, resolve } from 'node:path'\n\n/**\n * Project detection.\n *\n * The CLI needs to know which framework it is generating for, whether the project uses TypeScript,\n * and where components live. Asking the user all of that on every invocation is friction for\n * information that is sitting in `package.json` and on disk — so we detect it, and only prompt for\n * what we genuinely cannot infer.\n */\n\nexport type Framework = 'react' | 'vue' | 'nuxt' | 'next' | 'unknown'\n\nexport interface ProjectInfo {\n root: string\n framework: Framework\n typescript: boolean\n /** Where generated components should go, relative to `root`. */\n componentsDir: string\n /** Detected import alias for the source root, e.g. `'@/'`. `null` when none is configured. */\n alias: string | null\n packageManager: 'pnpm' | 'yarn' | 'npm' | 'bun'\n /** `true` when the relevant `@ansh-ui/*` package is already a dependency. */\n installed: boolean\n}\n\n/** Walk up from `cwd` looking for a `package.json`, so the CLI works from a subdirectory. */\nfunction findRoot(cwd: string): string {\n let current = resolve(cwd)\n for (let depth = 0; depth < 10; depth++) {\n if (existsSync(join(current, 'package.json'))) return current\n const parent = resolve(current, '..')\n if (parent === current) break\n current = parent\n }\n return resolve(cwd)\n}\n\nfunction readJson(path: string): Record<string, unknown> | null {\n try {\n return JSON.parse(readFileSync(path, 'utf8')) as Record<string, unknown>\n } catch {\n return null\n }\n}\n\nexport function detectProject(cwd = process.cwd()): ProjectInfo {\n const root = findRoot(cwd)\n const pkg = readJson(join(root, 'package.json')) ?? {}\n\n const deps: Record<string, string> = {\n ...((pkg.dependencies as Record<string, string>) ?? {}),\n ...((pkg.devDependencies as Record<string, string>) ?? {}),\n }\n\n // Order matters: Nuxt projects also depend on Vue, and Next projects on React. Checking the\n // meta-framework first gives the more specific — and more useful — answer.\n const framework: Framework = deps.nuxt\n ? 'nuxt'\n : deps.next\n ? 'next'\n : deps.vue\n ? 'vue'\n : deps.react\n ? 'react'\n : 'unknown'\n\n const typescript = Boolean(deps.typescript) || existsSync(join(root, 'tsconfig.json'))\n\n const packageManager: ProjectInfo['packageManager'] = existsSync(join(root, 'pnpm-lock.yaml'))\n ? 'pnpm'\n : existsSync(join(root, 'bun.lockb')) || existsSync(join(root, 'bun.lock'))\n ? 'bun'\n : existsSync(join(root, 'yarn.lock'))\n ? 'yarn'\n : 'npm'\n\n return {\n root,\n framework,\n typescript,\n componentsDir: detectComponentsDir(root, framework),\n alias: detectAlias(root),\n packageManager,\n installed: Boolean(\n deps['@ansh-ui/react'] ||\n deps['@ansh-ui/vue'] ||\n deps['@ansh-ui/nuxt'] ||\n deps['@ansh-ui/core'],\n ),\n }\n}\n\n/**\n * Find where components already live, rather than imposing a convention.\n *\n * Dropping a file into `src/components` in a project that uses `app/ui` is the kind of small\n * disrespect that makes a tool feel foreign. Candidates are ordered by how strong a signal they are.\n */\nfunction detectComponentsDir(root: string, framework: Framework): string {\n const candidates =\n framework === 'nuxt'\n ? ['components', 'app/components', 'src/components']\n : ['src/components', 'components', 'app/components', 'src/lib/components', 'app/ui', 'src/ui']\n\n for (const candidate of candidates) {\n if (existsSync(join(root, candidate))) return candidate\n }\n\n // Nothing exists yet: pick the convention that matches the project's existing shape.\n if (existsSync(join(root, 'src'))) return 'src/components'\n if (framework === 'nuxt') return 'components'\n return 'components'\n}\n\n/**\n * Read the source alias from `tsconfig.json` so generated imports match the project's style.\n *\n * A generated file that writes `../../lib/utils` in a project where everything else says\n * `@/lib/utils` is immediately identifiable as machine-written.\n */\nfunction detectAlias(root: string): string | null {\n for (const file of ['tsconfig.json', 'jsconfig.json']) {\n const config = readJson(join(root, file))\n if (!config) continue\n\n const compilerOptions = config.compilerOptions as Record<string, unknown> | undefined\n const paths = compilerOptions?.paths as Record<string, string[]> | undefined\n if (!paths) continue\n\n for (const pattern of Object.keys(paths)) {\n // Match a plain wildcard alias like `\"@/*\"` or `\"~/*\"` — the overwhelmingly common shape.\n if (pattern.endsWith('/*')) return pattern.slice(0, -1)\n }\n }\n return null\n}\n\n/** The install command for the detected package manager. */\nexport function installCommand(info: ProjectInfo, packages: string[]): string {\n const list = packages.join(' ')\n switch (info.packageManager) {\n case 'pnpm':\n return `pnpm add ${list}`\n case 'yarn':\n return `yarn add ${list}`\n case 'bun':\n return `bun add ${list}`\n default:\n return `npm install ${list}`\n }\n}\n","import { behaviours, effects } from '@ansh-ui/core'\n\n/**\n * Catalogue enumeration.\n *\n * Reads the registry rather than a hardcoded list, so a new effect appears in `ansh list` the\n * moment it is defined — no second place to update, and no possibility of the CLI advertising\n * something that doesn't exist or omitting something that does.\n */\n\nexport interface CatalogueEntry {\n name: string\n kind: 'effect' | 'behaviour'\n /** The component name in `@ansh-ui/react` and `@ansh-ui/vue`. */\n component: string\n /** Every option, with its default value, read from the definition. */\n options: Array<{ name: string; default: unknown }>\n}\n\n/**\n * Effect and behaviour names map to component names that read better than a mechanical\n * transformation would produce — `meteors` → `MeteorShower`, `blob` → `LiquidBlob`. Where a\n * definition isn't listed here, the name is PascalCased.\n */\nconst COMPONENT_NAMES: Record<string, string> = {\n aurora: 'AuroraBackground',\n 'mesh-gradient': 'MeshGradient',\n 'animated-gradient': 'AnimatedGradient',\n 'particle-field': 'ParticleField',\n constellation: 'Constellation',\n meteors: 'MeteorShower',\n rain: 'Rain',\n snow: 'Snow',\n fireflies: 'Fireflies',\n waves: 'Waves',\n blob: 'LiquidBlob',\n 'noise-layer': 'NoiseLayer',\n spotlight: 'Spotlight',\n magnetic: 'MagneticButton',\n tilt: 'TiltCard',\n ripple: 'Ripple',\n marquee: 'Marquee',\n 'scroll-reveal': 'ScrollReveal',\n parallax: 'Parallax',\n 'split-text': 'SplitText',\n}\n\nexport function componentNameFor(name: string): string {\n return (\n COMPONENT_NAMES[name] ??\n name.replace(/(^|-)([a-z])/g, (_, __, letter: string) => letter.toUpperCase())\n )\n}\n\nexport function listCatalogue(filter?: 'effect' | 'behaviour'): CatalogueEntry[] {\n const entries: CatalogueEntry[] = []\n\n if (filter !== 'behaviour') {\n for (const [name, definition] of Object.entries(effects)) {\n entries.push({\n name,\n kind: 'effect',\n component: componentNameFor(name),\n options: describeOptions(definition.defaults as Record<string, unknown>),\n })\n }\n }\n\n if (filter !== 'effect') {\n for (const [name, definition] of Object.entries(behaviours)) {\n entries.push({\n name,\n kind: 'behaviour',\n component: componentNameFor(name),\n options: describeOptions(definition.defaults as Record<string, unknown>),\n })\n }\n }\n\n return entries.sort((a, b) => a.name.localeCompare(b.name))\n}\n\n/**\n * Turn a `defaults` object into a documented option list.\n *\n * This is why {@link import('@ansh-ui/core').EffectDefinition}'s `defaults` is typed\n * `Required<Options>`: it makes \"every option has a documented default\" a compile-time guarantee,\n * which in turn makes this function complete by construction rather than by diligence.\n */\nfunction describeOptions(defaults: Record<string, unknown>): CatalogueEntry['options'] {\n return Object.entries(defaults)\n .map(([name, value]) => ({ name, default: value }))\n .sort((a, b) => a.name.localeCompare(b.name))\n}\n\n/** Look up a single entry by effect/behaviour name or by component name, case-insensitively. */\nexport function findEntry(query: string): CatalogueEntry | undefined {\n const needle = query.toLowerCase()\n return listCatalogue().find(\n (entry) => entry.name.toLowerCase() === needle || entry.component.toLowerCase() === needle,\n )\n}\n","import { existsSync, mkdirSync, writeFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { type ProjectInfo, detectProject } from '../project'\nimport { findEntry } from './list'\n\n/**\n * `ansh add <name>` — generate a local, editable wrapper for a component.\n *\n * ## Why a wrapper rather than a source copy\n *\n * shadcn/ui popularised copying component source into the project, and for *unstyled primitives*\n * that is exactly right: the value is in the markup and the styling, and you want to own both.\n *\n * Ansh is a different shape. The value is in the engine — an 800-line curl-noise particle system,\n * a spring solver, a colour pipeline — and copying that into an application would be actively\n * harmful: it would never receive a bug fix, and it would bloat the repository with code nobody on\n * the team intends to maintain.\n *\n * What developers actually want to own is the **configuration**: which colours, what intensity, what\n * it is called in their design system. So that is what this generates — a thin, fully-typed local\n * component with the project's own defaults baked in, importing the engine from the package.\n *\n * The result: `<HeroBackground />` in application code, one file to edit when the brand palette\n * changes, and engine fixes arrive through a normal dependency upgrade.\n */\n\nexport interface AddOptions {\n /** Effect or behaviour name, or component name. Case-insensitive. */\n name: string\n /** Local component name. Defaults to the catalogue's component name. */\n as?: string\n /** Target directory, relative to the project root. Defaults to the detected components dir. */\n dir?: string\n /** Overwrite an existing file. Default `false`. */\n force?: boolean\n /** Print the file instead of writing it. Default `false`. */\n dryRun?: boolean\n cwd?: string\n}\n\nexport interface AddResult {\n path: string\n contents: string\n written: boolean\n /** Packages the project needs but doesn't have. */\n missingDependencies: string[]\n}\n\nexport function addComponent(options: AddOptions): AddResult {\n const project = detectProject(options.cwd)\n const entry = findEntry(options.name)\n\n if (!entry) {\n throw new Error(\n `Unknown component \"${options.name}\". Run \\`ansh list\\` to see what's available.`,\n )\n }\n\n if (project.framework === 'unknown') {\n throw new Error(\n 'Could not detect a React, Next, Vue or Nuxt project. ' +\n 'Run this from your project root, or install the framework first.',\n )\n }\n\n const isVue = project.framework === 'vue' || project.framework === 'nuxt'\n const componentName = options.as ?? entry.component\n const extension = isVue ? 'vue' : project.typescript ? 'tsx' : 'jsx'\n\n const directory = options.dir ?? project.componentsDir\n const path = join(project.root, directory, `${componentName}.${extension}`)\n\n const contents = isVue\n ? renderVue(componentName, entry.component, entry.options, project)\n : renderReact(componentName, entry.component, entry.options, project)\n\n const missingDependencies = missingFor(project)\n\n if (options.dryRun) {\n return { path, contents, written: false, missingDependencies }\n }\n\n if (existsSync(path) && !options.force) {\n throw new Error(`${path} already exists. Pass --force to overwrite.`)\n }\n\n mkdirSync(dirname(path), { recursive: true })\n writeFileSync(path, contents, 'utf8')\n\n return { path, contents, written: true, missingDependencies }\n}\n\nfunction missingFor(project: ProjectInfo): string[] {\n if (project.installed) return []\n switch (project.framework) {\n case 'nuxt':\n return ['@ansh-ui/nuxt']\n case 'vue':\n return ['@ansh-ui/vue', '@ansh-ui/tokens']\n default:\n return ['@ansh-ui/react', '@ansh-ui/tokens']\n }\n}\n\n/**\n * Serialise a default value as source.\n *\n * Only the options worth surfacing are emitted — see {@link INTERESTING_OPTIONS}. Emitting all\n * twenty would produce a wall of noise that obscures the three an author will actually change.\n */\nfunction literal(value: unknown): string {\n if (typeof value === 'string') return JSON.stringify(value)\n if (Array.isArray(value)) return `[${value.map(literal).join(', ')}]`\n if (value === null) return 'null'\n if (typeof value === 'object') return JSON.stringify(value)\n return String(value)\n}\n\n/**\n * Options included in generated source.\n *\n * These are the ones that carry design intent — the knobs a designer asks to change. Structural and\n * performance options are omitted from the generated defaults but remain available as props, since\n * the wrapper spreads everything through.\n */\nconst INTERESTING_OPTIONS = new Set([\n 'colors',\n 'speed',\n 'intensity',\n 'opacity',\n 'blur',\n 'seed',\n 'strength',\n 'radius',\n 'max',\n 'distance',\n 'amount',\n 'count',\n])\n\nfunction pickDefaults(options: Array<{ name: string; default: unknown }>) {\n return options.filter((option) => INTERESTING_OPTIONS.has(option.name))\n}\n\nfunction renderReact(\n componentName: string,\n sourceComponent: string,\n options: Array<{ name: string; default: unknown }>,\n project: ProjectInfo,\n): string {\n const ts = project.typescript\n const defaults = pickDefaults(options)\n\n const defaultLines = defaults\n .map((option) => ` ${option.name}: ${literal(option.default)},`)\n .join('\\n')\n\n const propsType = ts\n ? `\\ntype ${componentName}Props = ComponentProps<typeof ${sourceComponent}>\\n`\n : ''\n\n const signature = ts\n ? `export function ${componentName}({ children, ...props }: ${componentName}Props) {`\n : `export function ${componentName}({ children, ...props }) {`\n\n const typeImport = ts ? `import type { ComponentProps } from 'react'\\n` : ''\n\n return `${typeImport}import { ${sourceComponent} } from '@ansh-ui/react'\n\n/**\n * ${componentName} — a project-local wrapper around Ansh's ${sourceComponent}.\n *\n * Edit the defaults below to match your design system. Every option remains overridable per\n * instance, because props spread after the defaults.\n *\n * Generated by \\`ansh add\\`. Safe to edit — it will not be regenerated unless you ask.\n */\nconst defaults = {\n${defaultLines}\n}\n\n${propsType}\n${signature}\n return (\n <${sourceComponent} {...defaults} {...props}>\n {children}\n </${sourceComponent}>\n )\n}\n`\n}\n\nfunction renderVue(\n componentName: string,\n sourceComponent: string,\n options: Array<{ name: string; default: unknown }>,\n project: ProjectInfo,\n): string {\n const defaults = pickDefaults(options)\n const defaultLines = defaults\n .map((option) => ` ${option.name}: ${literal(option.default)},`)\n .join('\\n')\n\n const lang = project.typescript ? ' lang=\"ts\"' : ''\n\n // Nuxt auto-imports Ansh components, so an explicit import there would be redundant — and\n // would shadow the auto-import in a way that breaks if the module's prefix option changes.\n const importLine =\n project.framework === 'nuxt'\n ? `// ${sourceComponent} is auto-imported by @ansh-ui/nuxt.`\n : `import { ${sourceComponent} } from '@ansh-ui/vue'`\n\n return `<script setup${lang}>\n${importLine}\n\n/**\n * ${componentName} — a project-local wrapper around Ansh's ${sourceComponent}.\n *\n * Edit the defaults below to match your design system. Every option remains overridable per\n * instance, because \\`$attrs\\` is bound after the defaults.\n *\n * Generated by \\`ansh add\\`. Safe to edit — it will not be regenerated unless you ask.\n */\nconst defaults = {\n${defaultLines}\n}\n</script>\n\n<template>\n <${sourceComponent} v-bind=\"{ ...defaults, ...$attrs }\">\n <slot />\n </${sourceComponent}>\n</template>\n`\n}\n","import { mkdirSync, writeFileSync } from 'node:fs'\nimport { dirname } from 'node:path'\nimport {\n checkContrast,\n createScale,\n harmony,\n parseColor,\n rgbToOklch,\n toHex,\n toOklchString,\n} from '@ansh-ui/core'\n\n/**\n * `ansh theme <color>` — generate a token override stylesheet from a brand colour.\n *\n * The job this does that hand-authoring doesn't: it **checks the result**. An 11-step scale\n * generated from a brand colour will, for certain hues, produce steps that fail contrast against\n * white or against each other — a saturated yellow at step 600 is a classic offender. Generating the\n * scale is easy; noticing that step 600 can't legally hold body text is the part people skip.\n *\n * So the emitted stylesheet carries contrast annotations as comments, and the report flags any step\n * that cannot be used for text.\n */\n\nexport interface ThemeOptions {\n /** Brand colour in any CSS syntax. */\n color: string\n /** Where to write the stylesheet. Prints to stdout when omitted. */\n out?: string\n /** Hue drift across the scale, in degrees. `0` keeps a single hue. Default `0`. */\n hueShift?: number\n /** Chroma falloff at the extremes, in `[0, 1]`. Default `0.35`. */\n chromaFalloff?: number\n /** Also emit a harmonious accent scale. Default `false`. */\n withAccent?: boolean\n}\n\nexport interface ThemeReport {\n css: string\n /** Steps whose contrast against white is below 4.5:1 — unusable for body text on light surfaces. */\n unsafeOnLight: string[]\n /** Steps whose contrast against near-black is below 4.5:1. */\n unsafeOnDark: string[]\n /** The step closest in lightness to the input colour. */\n nearestStep: string\n}\n\nconst TAILWIND_KEYS = ['50', '100', '200', '300', '400', '500', '600', '700', '800', '900', '950']\n\nexport function generateTheme(options: ThemeOptions): ThemeReport {\n const base = parseColor(options.color)\n const baseLightness = rgbToOklch(base).l\n\n const scale = createScale(base, {\n steps: 11,\n hueShift: options.hueShift ?? 0,\n chromaFalloff: options.chromaFalloff ?? 0.35,\n })\n\n const unsafeOnLight: string[] = []\n const unsafeOnDark: string[] = []\n\n const lines: string[] = []\n let nearestStep = '500'\n let nearestDistance = Infinity\n\n scale.colors.forEach((color, index) => {\n const key = TAILWIND_KEYS[index]\n const onLight = checkContrast(color, '#ffffff')\n const onDark = checkContrast(color, '#0b0b0f')\n\n if (!onLight.passes) unsafeOnLight.push(key)\n if (!onDark.passes) unsafeOnDark.push(key)\n\n const distance = Math.abs(rgbToOklch(color).l - baseLightness)\n if (distance < nearestDistance) {\n nearestDistance = distance\n nearestStep = key\n }\n\n // The comment carries the numbers, so a designer reviewing the file can see at a glance which\n // steps are safe for text without opening a contrast checker.\n lines.push(\n ` --ansh-brand-${key}: ${toOklchString(color)};` +\n ` /* ${toHex(color)} on-white ${onLight.ratio.toFixed(2)}:1` +\n ` on-black ${onDark.ratio.toFixed(2)}:1 */`,\n )\n })\n\n let accentLines = ''\n if (options.withAccent) {\n // A split-complementary partner: far enough from the brand hue to be clearly a different\n // colour, close enough to still look related. The straight complement is usually too harsh for\n // UI, which is why it isn't the default choice here.\n const [, accent] = harmony(base, 'splitComplementary')\n const accentScale = createScale(accent, { steps: 11 })\n accentLines =\n '\\n /* Accent — split-complementary partner to the brand hue. */\\n' +\n accentScale.colors\n .map((color, index) => ` --ansh-accent-${TAILWIND_KEYS[index]}: ${toOklchString(color)};`)\n .join('\\n') +\n '\\n'\n }\n\n const css = `/**\n * Ansh theme override.\n *\n * Generated by \\`ansh theme ${options.color}\\`.\n * Import this *after* \\`@ansh-ui/tokens/ansh.css\\` so it wins in the cascade.\n *\n * Contrast ratios are annotated per step. Steps marked unsafe cannot legally hold body text on the\n * corresponding surface — use them for fills, borders and large display type instead.\n */\n\n:root {\n${lines.join('\\n')}\n${accentLines}\n /* Wire the semantic accent tokens to the generated scale. */\n --ansh-accent-subtle: var(--ansh-brand-50);\n --ansh-accent-muted: var(--ansh-brand-100);\n --ansh-accent-default: var(--ansh-brand-600);\n --ansh-accent-hover: var(--ansh-brand-700);\n --ansh-accent-active: var(--ansh-brand-800);\n --ansh-accent-text: var(--ansh-brand-700);\n --ansh-border-focus: var(--ansh-brand-600);\n}\n\n/**\n * Dark theme.\n *\n * Not an inversion. Accents step *lighter* rather than mirroring their light-mode index, because\n * saturated colour reads as less vivid against a dark background (Helmholtz–Kohlrausch).\n */\n[data-theme='dark'] {\n --ansh-accent-subtle: color-mix(in oklab, var(--ansh-brand-950) 40%, transparent);\n --ansh-accent-muted: var(--ansh-brand-950);\n --ansh-accent-default: var(--ansh-brand-500);\n --ansh-accent-hover: var(--ansh-brand-400);\n --ansh-accent-active: var(--ansh-brand-300);\n --ansh-accent-text: var(--ansh-brand-300);\n --ansh-border-focus: var(--ansh-brand-400);\n}\n\n@media (prefers-color-scheme: dark) {\n :root:not([data-theme]) {\n --ansh-accent-subtle: color-mix(in oklab, var(--ansh-brand-950) 40%, transparent);\n --ansh-accent-muted: var(--ansh-brand-950);\n --ansh-accent-default: var(--ansh-brand-500);\n --ansh-accent-hover: var(--ansh-brand-400);\n --ansh-accent-active: var(--ansh-brand-300);\n --ansh-accent-text: var(--ansh-brand-300);\n --ansh-border-focus: var(--ansh-brand-400);\n }\n}\n`\n\n if (options.out) {\n mkdirSync(dirname(options.out), { recursive: true })\n writeFileSync(options.out, css, 'utf8')\n }\n\n return { css, unsafeOnLight, unsafeOnDark, nearestStep }\n}\n","import {\n createGradient,\n generatePalette,\n parseColor,\n perceivedLightness,\n rgbToOklch,\n toHex,\n toOklchString,\n} from '@ansh-ui/core'\n\n/**\n * `ansh palette` — generate and inspect palettes.\n *\n * Two modes:\n *\n * - **Generate** — a seeded palette from scratch. The same seed always produces the same palette,\n * so a designer who likes seed 4821 can pin it and get that palette back next week.\n * - **Inspect** — report a supplied palette's OKLCH decomposition and the gradient it produces.\n * The useful part is seeing the *interpolated midpoints*: a palette can look fine as swatches and\n * produce a muddy gradient, and this shows that before it ships.\n */\n\nexport interface PaletteReportOptions {\n /** Colours to inspect. Generates a new palette when omitted. */\n colors?: string[]\n /** Number of colours to generate. Default `5`. */\n count?: number\n seed?: number | string\n /** Overall lightness in `[0, 1]`. Default `0.65`. */\n lightness?: number\n /** Chroma as a fraction of the maximum available. Default `0.85`. */\n saturation?: number\n /** Hue span in degrees. Default `120`. */\n spread?: number\n /** Also print interpolated gradient midpoints. Default `true`. */\n gradient?: boolean\n}\n\nexport interface PaletteReport {\n colors: Array<{ hex: string; oklch: string; lightness: number; chroma: number; hue: number }>\n /** Interpolated samples across the palette, showing what a gradient through it looks like. */\n gradientSamples: string[]\n /** Copy-pasteable literal for a `colors` prop. */\n snippet: string\n}\n\nexport function generatePaletteReport(options: PaletteReportOptions = {}): PaletteReport {\n const parsed =\n options.colors && options.colors.length > 0\n ? options.colors.map((color) => parseColor(color))\n : generatePalette({\n count: options.count ?? 5,\n seed: options.seed ?? 1,\n lightness: options.lightness ?? 0.65,\n saturation: options.saturation ?? 0.85,\n spread: options.spread ?? 120,\n })\n\n const colors = parsed.map((color) => {\n const lch = rgbToOklch(color)\n return {\n hex: toHex(color),\n oklch: toOklchString(color),\n lightness: Number(lch.l.toFixed(4)),\n chroma: Number(lch.c.toFixed(4)),\n hue: Number(lch.h.toFixed(2)),\n }\n })\n\n // Sampled in OKLCH rather than OKLab: for a gradient *through a palette* the goal is maximum\n // vividness across the transitions, which is what travelling the hue circle gives.\n const gradientSamples =\n options.gradient === false || parsed.length < 2\n ? []\n : createGradient(parsed, { space: 'oklch' })\n .sample(9)\n .map((color) => toHex(color))\n\n const snippet = `colors={[${colors.map((c) => `'${c.hex}'`).join(', ')}]}`\n\n return { colors, gradientSamples, snippet }\n}\n\n/**\n * Sort a palette by perceived lightness and report the ordering.\n *\n * Useful for layered effects, where the draw order should generally run dark to light so brighter\n * colours land on top and read as nearer.\n */\nexport function orderByLightness(colors: string[]): Array<{ hex: string; lightness: number }> {\n return colors\n .map((color) => ({ hex: toHex(parseColor(color)), lightness: perceivedLightness(color) }))\n .sort((a, b) => a.lightness - b.lightness)\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ansh-ui/cli",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "The Ansh CLI — scaffold components, generate themes and palettes, and inspect the catalogue.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"cli",
|
|
9
|
+
"ansh",
|
|
10
|
+
"scaffold",
|
|
11
|
+
"generator",
|
|
12
|
+
"design-tokens"
|
|
13
|
+
],
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/jithin003/ui-library.git",
|
|
17
|
+
"directory": "packages/cli"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist"
|
|
21
|
+
],
|
|
22
|
+
"bin": {
|
|
23
|
+
"ansh": "./dist/cli.js"
|
|
24
|
+
},
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"import": "./dist/index.js"
|
|
29
|
+
},
|
|
30
|
+
"./package.json": "./package.json"
|
|
31
|
+
},
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@ansh-ui/core": "0.2.0",
|
|
35
|
+
"@ansh-ui/tokens": "0.2.0"
|
|
36
|
+
},
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18.18.0"
|
|
39
|
+
},
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public"
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "tsup",
|
|
45
|
+
"dev": "tsup --watch",
|
|
46
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
47
|
+
}
|
|
48
|
+
}
|