aetherx-dt-ui 0.1.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/dist/cli/index.mjs +440 -0
- package/package.json +56 -0
- package/src/registry/components/badge/DtBadge.vue +94 -0
- package/src/registry/components/badge/index.ts +2 -0
- package/src/registry/components/button/DtButton.vue +176 -0
- package/src/registry/components/button/index.ts +2 -0
- package/src/registry/components/card/DtCard.vue +46 -0
- package/src/registry/components/card/DtCardContent.vue +15 -0
- package/src/registry/components/card/DtCardFooter.vue +17 -0
- package/src/registry/components/card/DtCardHeader.vue +31 -0
- package/src/registry/components/card/index.ts +4 -0
- package/src/registry/components/dialog/DtDialog.vue +41 -0
- package/src/registry/components/dialog/DtDialogContent.vue +140 -0
- package/src/registry/components/dialog/DtDialogFooter.vue +18 -0
- package/src/registry/components/dialog/DtDialogHeader.vue +32 -0
- package/src/registry/components/dialog/DtDialogTrigger.vue +20 -0
- package/src/registry/components/dialog/index.ts +5 -0
- package/src/registry/components/input/DtInput.vue +140 -0
- package/src/registry/components/input/index.ts +2 -0
- package/src/registry/components/select/DtSelect.vue +102 -0
- package/src/registry/components/select/DtSelectContent.vue +103 -0
- package/src/registry/components/select/DtSelectItem.vue +123 -0
- package/src/registry/components/select/DtSelectTrigger.vue +128 -0
- package/src/registry/components/select/index.ts +4 -0
- package/src/registry/docs/badge.md +171 -0
- package/src/registry/docs/button.md +184 -0
- package/src/registry/docs/card.md +199 -0
- package/src/registry/docs/dialog.md +282 -0
- package/src/registry/docs/input.md +168 -0
- package/src/registry/docs/select.md +346 -0
- package/src/registry/lib/utils.ts +4 -0
- package/src/registry/registry.json +52 -0
- package/src/registry/styles/base.css +143 -0
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import * as p from '@clack/prompts';
|
|
6
|
+
import pc from 'picocolors';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
import { execSync } from 'node:child_process';
|
|
9
|
+
|
|
10
|
+
const CONFIG_FILE = ".dtui.json";
|
|
11
|
+
function getConfigPath() {
|
|
12
|
+
return path.resolve(process.cwd(), CONFIG_FILE);
|
|
13
|
+
}
|
|
14
|
+
function configExists() {
|
|
15
|
+
return fs.existsSync(getConfigPath());
|
|
16
|
+
}
|
|
17
|
+
function readConfig() {
|
|
18
|
+
const configPath = getConfigPath();
|
|
19
|
+
if (!fs.existsSync(configPath)) {
|
|
20
|
+
throw new Error("No .dtui.json found. Run `npx dt-ui init` first.");
|
|
21
|
+
}
|
|
22
|
+
const raw = fs.readFileSync(configPath, "utf-8");
|
|
23
|
+
return JSON.parse(raw);
|
|
24
|
+
}
|
|
25
|
+
function writeConfig(config) {
|
|
26
|
+
const configPath = getConfigPath();
|
|
27
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
28
|
+
}
|
|
29
|
+
function detectFramework() {
|
|
30
|
+
const cwd = process.cwd();
|
|
31
|
+
if (fs.existsSync(path.join(cwd, "nuxt.config.ts")) || fs.existsSync(path.join(cwd, "nuxt.config.js"))) {
|
|
32
|
+
return "nuxt";
|
|
33
|
+
}
|
|
34
|
+
if (fs.existsSync(path.join(cwd, "vite.config.ts")) || fs.existsSync(path.join(cwd, "vite.config.js"))) {
|
|
35
|
+
return "vue-vite";
|
|
36
|
+
}
|
|
37
|
+
return "vue";
|
|
38
|
+
}
|
|
39
|
+
function detectPackageManager() {
|
|
40
|
+
const cwd = process.cwd();
|
|
41
|
+
if (fs.existsSync(path.join(cwd, "bun.lockb")) || fs.existsSync(path.join(cwd, "bun.lock"))) {
|
|
42
|
+
return "bun";
|
|
43
|
+
}
|
|
44
|
+
if (fs.existsSync(path.join(cwd, "pnpm-lock.yaml"))) {
|
|
45
|
+
return "pnpm";
|
|
46
|
+
}
|
|
47
|
+
if (fs.existsSync(path.join(cwd, "yarn.lock"))) {
|
|
48
|
+
return "yarn";
|
|
49
|
+
}
|
|
50
|
+
return "npm";
|
|
51
|
+
}
|
|
52
|
+
function getInstallCommand(pm, deps) {
|
|
53
|
+
const depStr = deps.join(" ");
|
|
54
|
+
switch (pm) {
|
|
55
|
+
case "pnpm":
|
|
56
|
+
return `pnpm add ${depStr}`;
|
|
57
|
+
case "yarn":
|
|
58
|
+
return `yarn add ${depStr}`;
|
|
59
|
+
case "bun":
|
|
60
|
+
return `bun add ${depStr}`;
|
|
61
|
+
default:
|
|
62
|
+
return `npm install ${depStr}`;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function getRegistryDir() {
|
|
67
|
+
const thisFile = fileURLToPath(import.meta.url);
|
|
68
|
+
const cliDir = path.dirname(thisFile);
|
|
69
|
+
const candidates = [
|
|
70
|
+
// When running from built dist/
|
|
71
|
+
path.resolve(cliDir, "../../src/registry"),
|
|
72
|
+
// When running from src/cli/utils/
|
|
73
|
+
path.resolve(cliDir, "../../registry"),
|
|
74
|
+
// When running via unbuild stub
|
|
75
|
+
path.resolve(cliDir, "../../../src/registry")
|
|
76
|
+
];
|
|
77
|
+
for (const candidate of candidates) {
|
|
78
|
+
if (fs.existsSync(path.join(candidate, "registry.json"))) {
|
|
79
|
+
return candidate;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
throw new Error("Could not find registry directory. Make sure dt-ui is installed correctly.");
|
|
83
|
+
}
|
|
84
|
+
function loadRegistry() {
|
|
85
|
+
const registryDir = getRegistryDir();
|
|
86
|
+
const registryPath = path.join(registryDir, "registry.json");
|
|
87
|
+
const raw = fs.readFileSync(registryPath, "utf-8");
|
|
88
|
+
return JSON.parse(raw);
|
|
89
|
+
}
|
|
90
|
+
function getComponentFiles(componentName) {
|
|
91
|
+
const registryDir = getRegistryDir();
|
|
92
|
+
const registry = loadRegistry();
|
|
93
|
+
const component = registry.components[componentName];
|
|
94
|
+
if (!component) {
|
|
95
|
+
throw new Error(`Component "${componentName}" not found in registry.`);
|
|
96
|
+
}
|
|
97
|
+
return component.files.map((fileName) => {
|
|
98
|
+
const filePath = path.join(registryDir, "components", componentName, fileName);
|
|
99
|
+
return {
|
|
100
|
+
name: fileName,
|
|
101
|
+
content: fs.readFileSync(filePath, "utf-8")
|
|
102
|
+
};
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
function getStylesContent() {
|
|
106
|
+
const registryDir = getRegistryDir();
|
|
107
|
+
return fs.readFileSync(path.join(registryDir, "styles", "base.css"), "utf-8");
|
|
108
|
+
}
|
|
109
|
+
function getLibContent() {
|
|
110
|
+
const registryDir = getRegistryDir();
|
|
111
|
+
return fs.readFileSync(path.join(registryDir, "lib", "utils.ts"), "utf-8");
|
|
112
|
+
}
|
|
113
|
+
function getDocContent(docsPath) {
|
|
114
|
+
const registryDir = getRegistryDir();
|
|
115
|
+
const fullPath = path.join(registryDir, docsPath);
|
|
116
|
+
if (!fs.existsSync(fullPath)) {
|
|
117
|
+
return "";
|
|
118
|
+
}
|
|
119
|
+
return fs.readFileSync(fullPath, "utf-8");
|
|
120
|
+
}
|
|
121
|
+
function getAllComponentNames() {
|
|
122
|
+
const registry = loadRegistry();
|
|
123
|
+
return Object.keys(registry.components);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const AGENT_HEADER = `# DT UI \u2014 Component Reference
|
|
127
|
+
|
|
128
|
+
This project uses dt-ui components. Below is the complete API reference for all installed components. Components use pure CSS with CSS custom properties for theming. No Tailwind or CSS framework required.
|
|
129
|
+
|
|
130
|
+
## Theming
|
|
131
|
+
|
|
132
|
+
All components use CSS custom properties prefixed with \`--dt-\` defined in \`base.css\`. Override them in your project's CSS to customize the look.
|
|
133
|
+
|
|
134
|
+
### Available Tokens
|
|
135
|
+
|
|
136
|
+
| Token | Default (Light) | Description |
|
|
137
|
+
|-------|-----------------|-------------|
|
|
138
|
+
| \`--dt-primary\` | \`hsl(220 72% 50%)\` | Brand/primary color |
|
|
139
|
+
| \`--dt-primary-hover\` | \`hsl(220 72% 42%)\` | Primary hover state |
|
|
140
|
+
| \`--dt-primary-foreground\` | \`hsl(0 0% 100%)\` | Text on primary bg |
|
|
141
|
+
| \`--dt-secondary\` | \`hsl(220 14% 94%)\` | Secondary background |
|
|
142
|
+
| \`--dt-secondary-foreground\` | \`hsl(220 10% 20%)\` | Text on secondary bg |
|
|
143
|
+
| \`--dt-destructive\` | \`hsl(0 72% 51%)\` | Destructive/danger color |
|
|
144
|
+
| \`--dt-success\` | \`hsl(142 72% 40%)\` | Success color |
|
|
145
|
+
| \`--dt-warning\` | \`hsl(38 92% 50%)\` | Warning color |
|
|
146
|
+
| \`--dt-border\` | \`hsl(220 13% 88%)\` | Border color |
|
|
147
|
+
| \`--dt-ring\` | \`hsl(220 72% 50%)\` | Focus ring color |
|
|
148
|
+
| \`--dt-radius-sm\` | \`0.25rem\` | Small border radius |
|
|
149
|
+
| \`--dt-radius-md\` | \`0.375rem\` | Medium border radius |
|
|
150
|
+
| \`--dt-radius-lg\` | \`0.5rem\` | Large border radius |
|
|
151
|
+
| \`--dt-shadow-sm\` | \`0 1px 2px...\` | Small shadow |
|
|
152
|
+
| \`--dt-shadow-md\` | \`0 4px 6px...\` | Medium shadow |
|
|
153
|
+
| \`--dt-shadow-lg\` | \`0 10px 15px...\` | Large shadow |
|
|
154
|
+
|
|
155
|
+
### Dark Mode
|
|
156
|
+
|
|
157
|
+
Add the \`dark\` class to your \`<html>\` or \`<body>\` element. All tokens automatically adjust.
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Components
|
|
162
|
+
|
|
163
|
+
`;
|
|
164
|
+
function getAgentMdPath() {
|
|
165
|
+
return path.resolve(process.cwd(), "AGENT.md");
|
|
166
|
+
}
|
|
167
|
+
function createAgentMd() {
|
|
168
|
+
fs.writeFileSync(getAgentMdPath(), AGENT_HEADER, "utf-8");
|
|
169
|
+
}
|
|
170
|
+
function appendComponentDoc(componentName) {
|
|
171
|
+
const registry = loadRegistry();
|
|
172
|
+
const component = registry.components[componentName];
|
|
173
|
+
if (!component) return;
|
|
174
|
+
const docContent = getDocContent(component.docs);
|
|
175
|
+
if (!docContent) return;
|
|
176
|
+
const agentPath = getAgentMdPath();
|
|
177
|
+
if (!fs.existsSync(agentPath)) {
|
|
178
|
+
createAgentMd();
|
|
179
|
+
}
|
|
180
|
+
const existing = fs.readFileSync(agentPath, "utf-8");
|
|
181
|
+
if (existing.includes(`### ${component.name}`)) return;
|
|
182
|
+
const separator = "\n---\n\n";
|
|
183
|
+
fs.appendFileSync(agentPath, docContent + separator, "utf-8");
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async function initCommand() {
|
|
187
|
+
p.intro(pc.bold(pc.cyan("dt-ui init")));
|
|
188
|
+
if (configExists()) {
|
|
189
|
+
const overwrite = await p.confirm({
|
|
190
|
+
message: "A .dtui.json config already exists. Overwrite?",
|
|
191
|
+
initialValue: false
|
|
192
|
+
});
|
|
193
|
+
if (p.isCancel(overwrite) || !overwrite) {
|
|
194
|
+
p.cancel("Init cancelled.");
|
|
195
|
+
process.exit(0);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
const detectedFramework = detectFramework();
|
|
199
|
+
const answers = await p.group(
|
|
200
|
+
{
|
|
201
|
+
framework: () => p.select({
|
|
202
|
+
message: "Which framework does your project use?",
|
|
203
|
+
initialValue: detectedFramework,
|
|
204
|
+
options: [
|
|
205
|
+
{ value: "nuxt", label: "Nuxt", hint: detectedFramework === "nuxt" ? "detected" : void 0 },
|
|
206
|
+
{ value: "vue-vite", label: "Vue + Vite", hint: detectedFramework === "vue-vite" ? "detected" : void 0 },
|
|
207
|
+
{ value: "vue", label: "Vue (plain)", hint: detectedFramework === "vue" ? "detected" : void 0 }
|
|
208
|
+
]
|
|
209
|
+
}),
|
|
210
|
+
componentsDir: () => p.text({
|
|
211
|
+
message: "Where should components be installed?",
|
|
212
|
+
initialValue: "src/components/ui",
|
|
213
|
+
placeholder: "src/components/ui"
|
|
214
|
+
}),
|
|
215
|
+
libDir: () => p.text({
|
|
216
|
+
message: "Where should lib utils go?",
|
|
217
|
+
initialValue: "src/lib",
|
|
218
|
+
placeholder: "src/lib"
|
|
219
|
+
}),
|
|
220
|
+
agent: () => p.confirm({
|
|
221
|
+
message: "Include AI agent documentation (AGENT.md)?",
|
|
222
|
+
initialValue: true
|
|
223
|
+
})
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
onCancel: () => {
|
|
227
|
+
p.cancel("Init cancelled.");
|
|
228
|
+
process.exit(0);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
);
|
|
232
|
+
const stylesDir = "src/styles";
|
|
233
|
+
const config = {
|
|
234
|
+
framework: answers.framework,
|
|
235
|
+
componentsDir: answers.componentsDir,
|
|
236
|
+
libDir: answers.libDir,
|
|
237
|
+
stylesDir,
|
|
238
|
+
installedComponents: [],
|
|
239
|
+
agent: answers.agent
|
|
240
|
+
};
|
|
241
|
+
const s = p.spinner();
|
|
242
|
+
writeConfig(config);
|
|
243
|
+
s.start("Copying base.css...");
|
|
244
|
+
const stylesPath = path.resolve(process.cwd(), stylesDir);
|
|
245
|
+
fs.mkdirSync(stylesPath, { recursive: true });
|
|
246
|
+
fs.writeFileSync(
|
|
247
|
+
path.join(stylesPath, "base.css"),
|
|
248
|
+
getStylesContent(),
|
|
249
|
+
"utf-8"
|
|
250
|
+
);
|
|
251
|
+
s.stop("base.css copied");
|
|
252
|
+
s.start("Copying lib/utils.ts...");
|
|
253
|
+
const libPath = path.resolve(process.cwd(), answers.libDir);
|
|
254
|
+
fs.mkdirSync(libPath, { recursive: true });
|
|
255
|
+
fs.writeFileSync(
|
|
256
|
+
path.join(libPath, "utils.ts"),
|
|
257
|
+
getLibContent(),
|
|
258
|
+
"utf-8"
|
|
259
|
+
);
|
|
260
|
+
s.stop("lib/utils.ts copied");
|
|
261
|
+
if (answers.agent) {
|
|
262
|
+
s.start("Creating AGENT.md...");
|
|
263
|
+
createAgentMd();
|
|
264
|
+
s.stop("AGENT.md created");
|
|
265
|
+
}
|
|
266
|
+
p.note(
|
|
267
|
+
[
|
|
268
|
+
`${pc.bold("Next steps:")}`,
|
|
269
|
+
"",
|
|
270
|
+
`1. Import base.css in your main entry file:`,
|
|
271
|
+
` ${pc.cyan("import './styles/base.css'")}`,
|
|
272
|
+
"",
|
|
273
|
+
`2. Add your first component:`,
|
|
274
|
+
` ${pc.cyan("npx dt-ui add button")}`,
|
|
275
|
+
"",
|
|
276
|
+
`3. Use it in your Vue components:`,
|
|
277
|
+
` ${pc.cyan('<DtButton variant="default">Click me</DtButton>')}`
|
|
278
|
+
].join("\n"),
|
|
279
|
+
"Setup complete!"
|
|
280
|
+
);
|
|
281
|
+
p.outro(pc.green("dt-ui initialized successfully!"));
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
async function addCommand(componentNames) {
|
|
285
|
+
p.intro(pc.bold(pc.cyan("dt-ui add")));
|
|
286
|
+
if (!configExists()) {
|
|
287
|
+
p.cancel("No .dtui.json found. Run `npx dt-ui init` first.");
|
|
288
|
+
process.exit(1);
|
|
289
|
+
}
|
|
290
|
+
const config = readConfig();
|
|
291
|
+
const registry = loadRegistry();
|
|
292
|
+
const allNames = getAllComponentNames();
|
|
293
|
+
if (!componentNames || componentNames.length === 0) {
|
|
294
|
+
const selected = await p.multiselect({
|
|
295
|
+
message: "Which components would you like to add?",
|
|
296
|
+
options: allNames.map((name) => ({
|
|
297
|
+
value: name,
|
|
298
|
+
label: registry.components[name].name,
|
|
299
|
+
hint: config.installedComponents.includes(name) ? "installed" : void 0
|
|
300
|
+
})),
|
|
301
|
+
required: true
|
|
302
|
+
});
|
|
303
|
+
if (p.isCancel(selected)) {
|
|
304
|
+
p.cancel("Cancelled.");
|
|
305
|
+
process.exit(0);
|
|
306
|
+
}
|
|
307
|
+
componentNames = selected;
|
|
308
|
+
}
|
|
309
|
+
const invalid = componentNames.filter((n) => !registry.components[n]);
|
|
310
|
+
if (invalid.length > 0) {
|
|
311
|
+
p.cancel(
|
|
312
|
+
`Unknown component(s): ${invalid.map((n) => pc.red(n)).join(", ")}. Run ${pc.cyan("npx dt-ui list")} to see available components.`
|
|
313
|
+
);
|
|
314
|
+
process.exit(1);
|
|
315
|
+
}
|
|
316
|
+
const toInstall = /* @__PURE__ */ new Set();
|
|
317
|
+
const depQueue = [...componentNames];
|
|
318
|
+
while (depQueue.length > 0) {
|
|
319
|
+
const name = depQueue.pop();
|
|
320
|
+
if (toInstall.has(name)) continue;
|
|
321
|
+
toInstall.add(name);
|
|
322
|
+
const comp = registry.components[name];
|
|
323
|
+
for (const dep of comp.internalDeps) {
|
|
324
|
+
if (!toInstall.has(dep) && !config.installedComponents.includes(dep)) {
|
|
325
|
+
depQueue.push(dep);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
const extraDeps = [...toInstall].filter((n) => !componentNames.includes(n));
|
|
330
|
+
if (extraDeps.length > 0) {
|
|
331
|
+
const confirmDeps = await p.confirm({
|
|
332
|
+
message: `The following dependencies are also required: ${extraDeps.map((n) => pc.cyan(n)).join(", ")}. Install them too?`,
|
|
333
|
+
initialValue: true
|
|
334
|
+
});
|
|
335
|
+
if (p.isCancel(confirmDeps)) {
|
|
336
|
+
p.cancel("Cancelled.");
|
|
337
|
+
process.exit(0);
|
|
338
|
+
}
|
|
339
|
+
if (!confirmDeps) {
|
|
340
|
+
for (const dep of extraDeps) {
|
|
341
|
+
toInstall.delete(dep);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
const alreadyInstalled = [...toInstall].filter(
|
|
346
|
+
(n) => config.installedComponents.includes(n)
|
|
347
|
+
);
|
|
348
|
+
if (alreadyInstalled.length > 0) {
|
|
349
|
+
const overwrite = await p.confirm({
|
|
350
|
+
message: `${alreadyInstalled.map((n) => pc.yellow(n)).join(", ")} already installed. Overwrite?`,
|
|
351
|
+
initialValue: false
|
|
352
|
+
});
|
|
353
|
+
if (p.isCancel(overwrite) || !overwrite) {
|
|
354
|
+
for (const name of alreadyInstalled) {
|
|
355
|
+
toInstall.delete(name);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
if (toInstall.size === 0) {
|
|
360
|
+
p.cancel("Nothing to install.");
|
|
361
|
+
process.exit(0);
|
|
362
|
+
}
|
|
363
|
+
const s = p.spinner();
|
|
364
|
+
const allPeerDeps = /* @__PURE__ */ new Set();
|
|
365
|
+
const installed = [];
|
|
366
|
+
for (const componentName of toInstall) {
|
|
367
|
+
const comp = registry.components[componentName];
|
|
368
|
+
s.start(`Installing ${pc.cyan(comp.name)}...`);
|
|
369
|
+
const files = getComponentFiles(componentName);
|
|
370
|
+
const targetDir = path.resolve(
|
|
371
|
+
process.cwd(),
|
|
372
|
+
config.componentsDir,
|
|
373
|
+
componentName
|
|
374
|
+
);
|
|
375
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
376
|
+
for (const file of files) {
|
|
377
|
+
fs.writeFileSync(path.join(targetDir, file.name), file.content, "utf-8");
|
|
378
|
+
}
|
|
379
|
+
for (const dep of comp.peerDeps) {
|
|
380
|
+
allPeerDeps.add(dep);
|
|
381
|
+
}
|
|
382
|
+
if (!config.installedComponents.includes(componentName)) {
|
|
383
|
+
config.installedComponents.push(componentName);
|
|
384
|
+
}
|
|
385
|
+
if (config.agent) {
|
|
386
|
+
appendComponentDoc(componentName);
|
|
387
|
+
}
|
|
388
|
+
installed.push(componentName);
|
|
389
|
+
s.stop(`${pc.green("\u2713")} ${comp.name} installed`);
|
|
390
|
+
}
|
|
391
|
+
if (allPeerDeps.size > 0) {
|
|
392
|
+
const pm = detectPackageManager();
|
|
393
|
+
const cmd = getInstallCommand(pm, [...allPeerDeps]);
|
|
394
|
+
s.start(`Installing peer dependencies: ${[...allPeerDeps].join(", ")}...`);
|
|
395
|
+
try {
|
|
396
|
+
execSync(cmd, { cwd: process.cwd(), stdio: "pipe" });
|
|
397
|
+
s.stop("Peer dependencies installed");
|
|
398
|
+
} catch {
|
|
399
|
+
s.stop(pc.yellow("Failed to install peer dependencies. Please install manually:"));
|
|
400
|
+
p.log.warn(pc.dim(cmd));
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
writeConfig(config);
|
|
404
|
+
p.note(
|
|
405
|
+
installed.map((name) => {
|
|
406
|
+
const comp = registry.components[name];
|
|
407
|
+
return `${pc.green("\u2713")} ${comp.name} \u2192 ${config.componentsDir}/${name}/`;
|
|
408
|
+
}).join("\n"),
|
|
409
|
+
"Installed components"
|
|
410
|
+
);
|
|
411
|
+
p.outro(pc.green(`${installed.length} component(s) added successfully!`));
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
async function listCommand() {
|
|
415
|
+
p.intro(pc.bold(pc.cyan("dt-ui components")));
|
|
416
|
+
const registry = loadRegistry();
|
|
417
|
+
const installed = configExists() ? readConfig().installedComponents : [];
|
|
418
|
+
const components = Object.entries(registry.components);
|
|
419
|
+
const rows = components.map(([key, comp]) => {
|
|
420
|
+
const isInstalled = installed.includes(key);
|
|
421
|
+
const status = isInstalled ? pc.green("\u2713") : pc.dim("\u25CB");
|
|
422
|
+
const name = isInstalled ? pc.green(comp.name) : comp.name;
|
|
423
|
+
const deps = comp.internalDeps.length > 0 ? pc.dim(` (requires: ${comp.internalDeps.join(", ")})`) : "";
|
|
424
|
+
return ` ${status} ${name.padEnd(isInstalled ? 30 : 20)} ${pc.dim(comp.description)}${deps}`;
|
|
425
|
+
});
|
|
426
|
+
p.note(rows.join("\n"), `${components.length} components available`);
|
|
427
|
+
if (!configExists()) {
|
|
428
|
+
p.log.info(`Run ${pc.cyan("npx dt-ui init")} to get started.`);
|
|
429
|
+
} else {
|
|
430
|
+
p.log.info(`Run ${pc.cyan("npx dt-ui add <component>")} to install.`);
|
|
431
|
+
}
|
|
432
|
+
p.outro("");
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const program = new Command();
|
|
436
|
+
program.name("dt-ui").description("DT UI \u2014 Lightweight, customizable Vue components for DT projects").version("0.1.0");
|
|
437
|
+
program.command("init").description("Initialize dt-ui in your project").action(initCommand);
|
|
438
|
+
program.command("add").description("Add a component to your project").argument("[components...]", "Component names to add").action(addCommand);
|
|
439
|
+
program.command("list").description("List all available components").action(listCommand);
|
|
440
|
+
program.parse();
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "aetherx-dt-ui",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Lightweight, customizable Vue components for DT projects",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"dt-ui": "dist/cli/index.mjs"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"src/registry"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "unbuild",
|
|
15
|
+
"dev": "unbuild --stub",
|
|
16
|
+
"docs:dev": "vitepress dev docs",
|
|
17
|
+
"docs:build": "vitepress build docs",
|
|
18
|
+
"docs:preview": "vitepress preview docs"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@clack/prompts": "^0.9.1",
|
|
22
|
+
"commander": "^13.1.0",
|
|
23
|
+
"picocolors": "^1.1.1"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "^22.13.0",
|
|
27
|
+
"typescript": "^5.7.0",
|
|
28
|
+
"unbuild": "^3.3.1",
|
|
29
|
+
"vitepress": "^1.6.3",
|
|
30
|
+
"vue": "^3.5.0"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"vue": "^3.3.0"
|
|
34
|
+
},
|
|
35
|
+
"peerDependenciesMeta": {
|
|
36
|
+
"vue": {
|
|
37
|
+
"optional": true
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"keywords": [
|
|
41
|
+
"vue",
|
|
42
|
+
"components",
|
|
43
|
+
"ui",
|
|
44
|
+
"design-system",
|
|
45
|
+
"dt"
|
|
46
|
+
],
|
|
47
|
+
"license": "MIT",
|
|
48
|
+
"repository": {
|
|
49
|
+
"type": "git",
|
|
50
|
+
"url": "git+https://github.com/ashomurodov/dt-ui.git"
|
|
51
|
+
},
|
|
52
|
+
"homepage": "https://github.com/ashomurodov/dt-ui#readme",
|
|
53
|
+
"bugs": {
|
|
54
|
+
"url": "https://github.com/ashomurodov/dt-ui/issues"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
|
|
4
|
+
export type BadgeVariant = 'default' | 'secondary' | 'outline' | 'destructive' | 'success' | 'warning'
|
|
5
|
+
export type BadgeSize = 'sm' | 'default'
|
|
6
|
+
|
|
7
|
+
const props = withDefaults(defineProps<{
|
|
8
|
+
variant?: BadgeVariant
|
|
9
|
+
size?: BadgeSize
|
|
10
|
+
dot?: boolean
|
|
11
|
+
}>(), {
|
|
12
|
+
variant: 'default',
|
|
13
|
+
size: 'default',
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
const classes = computed(() => [
|
|
17
|
+
'dt-badge',
|
|
18
|
+
`dt-badge--${props.variant}`,
|
|
19
|
+
`dt-badge--${props.size}`,
|
|
20
|
+
props.dot && 'dt-badge--dot',
|
|
21
|
+
].filter(Boolean))
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<template>
|
|
25
|
+
<span :class="classes" v-bind="$attrs">
|
|
26
|
+
<span v-if="dot" class="dt-badge__dot" aria-hidden="true" />
|
|
27
|
+
<slot />
|
|
28
|
+
</span>
|
|
29
|
+
</template>
|
|
30
|
+
|
|
31
|
+
<style scoped>
|
|
32
|
+
.dt-badge {
|
|
33
|
+
display: inline-flex;
|
|
34
|
+
align-items: center;
|
|
35
|
+
gap: var(--dt-space-1);
|
|
36
|
+
font-weight: 500;
|
|
37
|
+
border-radius: var(--dt-radius-full);
|
|
38
|
+
white-space: nowrap;
|
|
39
|
+
transition: background-color var(--dt-transition-base), color var(--dt-transition-base);
|
|
40
|
+
border: 1px solid transparent;
|
|
41
|
+
line-height: 1;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* Sizes */
|
|
45
|
+
.dt-badge--sm {
|
|
46
|
+
padding: 0.125rem 0.5rem;
|
|
47
|
+
font-size: var(--dt-font-size-xs);
|
|
48
|
+
}
|
|
49
|
+
.dt-badge--default {
|
|
50
|
+
padding: 0.25rem 0.625rem;
|
|
51
|
+
font-size: var(--dt-font-size-sm);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/* Variants */
|
|
55
|
+
.dt-badge--default {
|
|
56
|
+
background-color: var(--dt-primary);
|
|
57
|
+
color: var(--dt-primary-foreground);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.dt-badge--secondary {
|
|
61
|
+
background-color: var(--dt-secondary);
|
|
62
|
+
color: var(--dt-secondary-foreground);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.dt-badge--outline {
|
|
66
|
+
border-color: var(--dt-border);
|
|
67
|
+
background-color: transparent;
|
|
68
|
+
color: var(--dt-foreground);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.dt-badge--destructive {
|
|
72
|
+
background-color: var(--dt-destructive);
|
|
73
|
+
color: var(--dt-destructive-foreground);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.dt-badge--success {
|
|
77
|
+
background-color: var(--dt-success);
|
|
78
|
+
color: var(--dt-success-foreground);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.dt-badge--warning {
|
|
82
|
+
background-color: var(--dt-warning);
|
|
83
|
+
color: var(--dt-warning-foreground);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/* Dot */
|
|
87
|
+
.dt-badge__dot {
|
|
88
|
+
width: 0.375rem;
|
|
89
|
+
height: 0.375rem;
|
|
90
|
+
border-radius: var(--dt-radius-full);
|
|
91
|
+
background-color: currentColor;
|
|
92
|
+
flex-shrink: 0;
|
|
93
|
+
}
|
|
94
|
+
</style>
|