@devfellowship/components 1.2.1 → 1.2.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/dist/cli.js +737 -0
- package/dist/index.cjs +31 -18
- package/dist/index.js +31 -18
- package/package.json +21 -3
package/dist/cli.js
ADDED
|
@@ -0,0 +1,737 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli/index.ts
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
|
|
6
|
+
// src/cli/commands/init.ts
|
|
7
|
+
import fs from "fs-extra";
|
|
8
|
+
import path from "path";
|
|
9
|
+
import chalk2 from "chalk";
|
|
10
|
+
import ora from "ora";
|
|
11
|
+
import prompts from "prompts";
|
|
12
|
+
|
|
13
|
+
// src/cli/types/config.ts
|
|
14
|
+
import { z } from "zod";
|
|
15
|
+
var configSchema = z.object({
|
|
16
|
+
typescript: z.boolean().default(true),
|
|
17
|
+
aliases: z.object({
|
|
18
|
+
components: z.string().default("@/components/dfl"),
|
|
19
|
+
hooks: z.string().default("@/hooks"),
|
|
20
|
+
providers: z.string().default("@/providers"),
|
|
21
|
+
pages: z.string().default("@/pages")
|
|
22
|
+
}),
|
|
23
|
+
registry: z.string().default("https://raw.githubusercontent.com/taigfs/dfl-components-cli/main/registry")
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// src/cli/utils/logger.ts
|
|
27
|
+
import chalk from "chalk";
|
|
28
|
+
var logger = {
|
|
29
|
+
info: (message) => console.log(chalk.blue("info"), message),
|
|
30
|
+
success: (message) => console.log(chalk.green("\u2713"), message),
|
|
31
|
+
warn: (message) => console.log(chalk.yellow("warn"), message),
|
|
32
|
+
error: (message) => console.log(chalk.red("error"), message),
|
|
33
|
+
break: () => console.log("")
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// src/cli/commands/init.ts
|
|
37
|
+
async function init(options) {
|
|
38
|
+
const cwd = path.resolve(options.cwd);
|
|
39
|
+
logger.info("Initializing dfl-components configuration...");
|
|
40
|
+
logger.break();
|
|
41
|
+
const configPath = path.join(cwd, "dfl-components.json");
|
|
42
|
+
if (await fs.pathExists(configPath)) {
|
|
43
|
+
const { overwrite } = await prompts({
|
|
44
|
+
type: "confirm",
|
|
45
|
+
name: "overwrite",
|
|
46
|
+
message: "dfl-components.json already exists. Overwrite?",
|
|
47
|
+
initial: false
|
|
48
|
+
});
|
|
49
|
+
if (!overwrite) {
|
|
50
|
+
logger.warn("Aborted.");
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
let config;
|
|
55
|
+
if (options.yes) {
|
|
56
|
+
config = configSchema.parse({
|
|
57
|
+
aliases: {}
|
|
58
|
+
});
|
|
59
|
+
} else {
|
|
60
|
+
const answers = await prompts([
|
|
61
|
+
{
|
|
62
|
+
type: "confirm",
|
|
63
|
+
name: "typescript",
|
|
64
|
+
message: "Would you like to use TypeScript?",
|
|
65
|
+
initial: true
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
type: "text",
|
|
69
|
+
name: "componentsPath",
|
|
70
|
+
message: "Where would you like to install components?",
|
|
71
|
+
initial: "@/components/dfl"
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
type: "text",
|
|
75
|
+
name: "hooksPath",
|
|
76
|
+
message: "Where would you like to install hooks?",
|
|
77
|
+
initial: "@/hooks"
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
type: "text",
|
|
81
|
+
name: "providersPath",
|
|
82
|
+
message: "Where would you like to install providers?",
|
|
83
|
+
initial: "@/providers"
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
type: "text",
|
|
87
|
+
name: "pagesPath",
|
|
88
|
+
message: "Where would you like to install pages?",
|
|
89
|
+
initial: "@/pages"
|
|
90
|
+
}
|
|
91
|
+
]);
|
|
92
|
+
if (!answers.typescript) {
|
|
93
|
+
logger.warn("Aborted.");
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
config = configSchema.parse({
|
|
97
|
+
typescript: answers.typescript,
|
|
98
|
+
aliases: {
|
|
99
|
+
components: answers.componentsPath,
|
|
100
|
+
hooks: answers.hooksPath,
|
|
101
|
+
providers: answers.providersPath,
|
|
102
|
+
pages: answers.pagesPath
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
const spinner = ora("Writing configuration...").start();
|
|
107
|
+
try {
|
|
108
|
+
await fs.writeJson(configPath, config, { spaces: 2 });
|
|
109
|
+
spinner.succeed("Configuration saved to dfl-components.json");
|
|
110
|
+
logger.break();
|
|
111
|
+
logger.success("Project initialized successfully!");
|
|
112
|
+
logger.break();
|
|
113
|
+
console.log("You can now add components:");
|
|
114
|
+
console.log(chalk2.cyan(" npx dfl-components add button"));
|
|
115
|
+
console.log(chalk2.cyan(" npx dfl-components add card input"));
|
|
116
|
+
console.log(chalk2.cyan(" npx dfl-components add auth-pages"));
|
|
117
|
+
logger.break();
|
|
118
|
+
} catch (error) {
|
|
119
|
+
spinner.fail("Failed to write configuration");
|
|
120
|
+
throw error;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// src/cli/commands/add.ts
|
|
125
|
+
import fs2 from "fs-extra";
|
|
126
|
+
import path2 from "path";
|
|
127
|
+
import chalk3 from "chalk";
|
|
128
|
+
import ora2 from "ora";
|
|
129
|
+
import prompts2 from "prompts";
|
|
130
|
+
|
|
131
|
+
// src/cli/utils/get-config.ts
|
|
132
|
+
import { cosmiconfig } from "cosmiconfig";
|
|
133
|
+
async function getConfig(cwd) {
|
|
134
|
+
const explorer = cosmiconfig("dfl-components", {
|
|
135
|
+
searchPlaces: ["dfl-components.json"]
|
|
136
|
+
});
|
|
137
|
+
const result = await explorer.search(cwd);
|
|
138
|
+
if (!result) {
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
return configSchema.parse(result.config);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// src/cli/utils/get-registry.ts
|
|
145
|
+
import fetch2 from "node-fetch";
|
|
146
|
+
async function getRegistry(registryUrl) {
|
|
147
|
+
const response = await fetch2(`${registryUrl}/registry.json`);
|
|
148
|
+
if (!response.ok) {
|
|
149
|
+
throw new Error(`Failed to fetch registry: ${response.statusText}`);
|
|
150
|
+
}
|
|
151
|
+
return response.json();
|
|
152
|
+
}
|
|
153
|
+
function getComponentFolderByCategory(category) {
|
|
154
|
+
switch (category) {
|
|
155
|
+
case "Hooks":
|
|
156
|
+
return "hooks";
|
|
157
|
+
case "Providers":
|
|
158
|
+
return "providers";
|
|
159
|
+
case "Pages":
|
|
160
|
+
return "pages";
|
|
161
|
+
default:
|
|
162
|
+
return "components";
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// src/cli/utils/resolve-alias.ts
|
|
167
|
+
function resolveAlias(aliasPath) {
|
|
168
|
+
if (aliasPath.startsWith("@/")) {
|
|
169
|
+
return aliasPath.replace("@/", "src/");
|
|
170
|
+
}
|
|
171
|
+
return aliasPath;
|
|
172
|
+
}
|
|
173
|
+
function getTargetPath(category, aliases) {
|
|
174
|
+
switch (category) {
|
|
175
|
+
case "Hooks":
|
|
176
|
+
return resolveAlias(aliases.hooks);
|
|
177
|
+
case "Providers":
|
|
178
|
+
return resolveAlias(aliases.providers);
|
|
179
|
+
case "Pages":
|
|
180
|
+
return resolveAlias(aliases.pages);
|
|
181
|
+
default:
|
|
182
|
+
return resolveAlias(aliases.components);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// src/cli/commands/add.ts
|
|
187
|
+
async function add(components, options) {
|
|
188
|
+
const cwd = path2.resolve(options.cwd);
|
|
189
|
+
const config = await getConfig(cwd);
|
|
190
|
+
if (!config) {
|
|
191
|
+
logger.error("No dfl-components.json found.");
|
|
192
|
+
console.log("Run", chalk3.cyan("npx dfl-components init"), "first.");
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const spinner = ora2("Fetching registry...").start();
|
|
196
|
+
try {
|
|
197
|
+
let resolveDeps2 = function(name) {
|
|
198
|
+
if (allComponents.has(name)) return;
|
|
199
|
+
allComponents.add(name);
|
|
200
|
+
const item = registry.items.find((i) => i.name === name);
|
|
201
|
+
if (item?.registryDependencies) {
|
|
202
|
+
for (const dep of item.registryDependencies) {
|
|
203
|
+
resolveDeps2(dep);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
var resolveDeps = resolveDeps2;
|
|
208
|
+
const registry = await getRegistry(config.registry);
|
|
209
|
+
let toInstall;
|
|
210
|
+
if (options.all) {
|
|
211
|
+
toInstall = registry.items.map((item) => item.name);
|
|
212
|
+
} else if (components.length === 0) {
|
|
213
|
+
spinner.stop();
|
|
214
|
+
const { selected } = await prompts2({
|
|
215
|
+
type: "multiselect",
|
|
216
|
+
name: "selected",
|
|
217
|
+
message: "Which components would you like to add?",
|
|
218
|
+
choices: registry.items.map((item) => ({
|
|
219
|
+
title: `${item.title} (${item.category})`,
|
|
220
|
+
value: item.name,
|
|
221
|
+
description: item.description
|
|
222
|
+
})),
|
|
223
|
+
min: 1
|
|
224
|
+
});
|
|
225
|
+
if (!selected || selected.length === 0) {
|
|
226
|
+
logger.warn("No components selected.");
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
toInstall = selected;
|
|
230
|
+
spinner.start("Fetching components...");
|
|
231
|
+
} else {
|
|
232
|
+
toInstall = components;
|
|
233
|
+
}
|
|
234
|
+
const validComponents = [];
|
|
235
|
+
for (const name of toInstall) {
|
|
236
|
+
const item = registry.items.find((i) => i.name === name);
|
|
237
|
+
if (!item) {
|
|
238
|
+
logger.warn(`Component "${name}" not found in registry. Skipping.`);
|
|
239
|
+
} else {
|
|
240
|
+
validComponents.push(name);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
if (validComponents.length === 0) {
|
|
244
|
+
spinner.fail("No valid components to install.");
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
const allComponents = /* @__PURE__ */ new Set();
|
|
248
|
+
for (const component of validComponents) {
|
|
249
|
+
resolveDeps2(component);
|
|
250
|
+
}
|
|
251
|
+
spinner.text = `Installing ${allComponents.size} component(s)...`;
|
|
252
|
+
const installed = [];
|
|
253
|
+
const errors = [];
|
|
254
|
+
for (const componentName of allComponents) {
|
|
255
|
+
const registryItem = registry.items.find((i) => i.name === componentName);
|
|
256
|
+
if (!registryItem) continue;
|
|
257
|
+
try {
|
|
258
|
+
const folder = getComponentFolderByCategory(registryItem.category);
|
|
259
|
+
const url = `${config.registry}/${folder}/${componentName}.json`;
|
|
260
|
+
const response = await fetch(url);
|
|
261
|
+
if (!response.ok) {
|
|
262
|
+
throw new Error(`Failed to fetch: ${response.statusText}`);
|
|
263
|
+
}
|
|
264
|
+
const componentData = await response.json();
|
|
265
|
+
for (const file of componentData.files) {
|
|
266
|
+
const targetDir = file.target ? path2.dirname(path2.join(cwd, "src", file.target)) : path2.join(cwd, getTargetPath(registryItem.category, config.aliases));
|
|
267
|
+
const targetPath = file.target ? path2.join(cwd, "src", file.target) : path2.join(targetDir, file.path);
|
|
268
|
+
if (await fs2.pathExists(targetPath)) {
|
|
269
|
+
if (!options.overwrite && !options.yes) {
|
|
270
|
+
spinner.stop();
|
|
271
|
+
const { overwrite } = await prompts2({
|
|
272
|
+
type: "confirm",
|
|
273
|
+
name: "overwrite",
|
|
274
|
+
message: `${path2.relative(cwd, targetPath)} already exists. Overwrite?`,
|
|
275
|
+
initial: false
|
|
276
|
+
});
|
|
277
|
+
if (!overwrite) {
|
|
278
|
+
logger.info(`Skipping ${path2.basename(targetPath)}`);
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
spinner.start();
|
|
282
|
+
} else if (!options.overwrite) {
|
|
283
|
+
logger.info(`Skipping ${path2.basename(targetPath)} (use --overwrite to replace)`);
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
await fs2.ensureDir(path2.dirname(targetPath));
|
|
288
|
+
await fs2.writeFile(targetPath, file.content);
|
|
289
|
+
installed.push(targetPath);
|
|
290
|
+
}
|
|
291
|
+
} catch (error) {
|
|
292
|
+
errors.push(`${componentName}: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
spinner.succeed(`Installed ${installed.length} file(s)`);
|
|
296
|
+
if (installed.length > 0) {
|
|
297
|
+
logger.break();
|
|
298
|
+
console.log(chalk3.green("Files created:"));
|
|
299
|
+
for (const file of installed) {
|
|
300
|
+
console.log(chalk3.gray(" -"), path2.relative(cwd, file));
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
if (errors.length > 0) {
|
|
304
|
+
logger.break();
|
|
305
|
+
console.log(chalk3.red("Errors:"));
|
|
306
|
+
for (const error of errors) {
|
|
307
|
+
console.log(chalk3.gray(" -"), error);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
logger.break();
|
|
311
|
+
} catch (error) {
|
|
312
|
+
spinner.fail("Failed to add components");
|
|
313
|
+
throw error;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// src/cli/ux-paths/commands/init.ts
|
|
318
|
+
import { mkdirSync, writeFileSync, existsSync } from "fs";
|
|
319
|
+
import { resolve, basename } from "path";
|
|
320
|
+
import chalk4 from "chalk";
|
|
321
|
+
function registerInit(program2) {
|
|
322
|
+
program2.command("init").description("Bootstrap a .dfl-ux-paths/ directory in the current cwd.").option("--app-id <id>", "App identifier (defaults to current dir name).").option("--force", "Overwrite an existing flows.json if present.", false).action((opts) => {
|
|
323
|
+
const cwd = process.cwd();
|
|
324
|
+
const dir = resolve(cwd, ".dfl-ux-paths");
|
|
325
|
+
const flowsPath = resolve(dir, "flows.json");
|
|
326
|
+
const appId = opts.appId || basename(cwd);
|
|
327
|
+
if (existsSync(flowsPath) && !opts.force) {
|
|
328
|
+
console.error(
|
|
329
|
+
chalk4.yellow(`${flowsPath} already exists. Use --force to overwrite.`)
|
|
330
|
+
);
|
|
331
|
+
process.exit(1);
|
|
332
|
+
}
|
|
333
|
+
mkdirSync(dir, { recursive: true });
|
|
334
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
335
|
+
const stub = {
|
|
336
|
+
schema_version: "1.0.0",
|
|
337
|
+
app_id: appId,
|
|
338
|
+
app_version: `${today}-0000000`,
|
|
339
|
+
generated_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
340
|
+
tech_stack: [],
|
|
341
|
+
screens: [],
|
|
342
|
+
flows: []
|
|
343
|
+
};
|
|
344
|
+
writeFileSync(flowsPath, JSON.stringify(stub, null, 2) + "\n", "utf8");
|
|
345
|
+
console.log(chalk4.green("Created"), flowsPath);
|
|
346
|
+
console.log(
|
|
347
|
+
chalk4.gray(
|
|
348
|
+
"Next steps: populate screens/flows, then run `dfl-components ux-paths validate` and `dfl-components ux-paths generate-mermaid`."
|
|
349
|
+
)
|
|
350
|
+
);
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// src/cli/ux-paths/commands/validate.ts
|
|
355
|
+
import { readFileSync, existsSync as existsSync2 } from "fs";
|
|
356
|
+
import { resolve as resolve2 } from "path";
|
|
357
|
+
import Ajv2020 from "ajv/dist/2020.js";
|
|
358
|
+
import addFormats from "ajv-formats";
|
|
359
|
+
import chalk5 from "chalk";
|
|
360
|
+
|
|
361
|
+
// src/cli/ux-paths/lib/load-schema.ts
|
|
362
|
+
var SCHEMA_URL = "https://raw.githubusercontent.com/devfellowship/dfl-ux-paths/main/schema/v1.json";
|
|
363
|
+
var cached = null;
|
|
364
|
+
async function loadSchemaV1() {
|
|
365
|
+
if (cached) return cached;
|
|
366
|
+
let res;
|
|
367
|
+
try {
|
|
368
|
+
res = await fetch(SCHEMA_URL);
|
|
369
|
+
} catch (err) {
|
|
370
|
+
throw new Error(
|
|
371
|
+
`Unable to fetch the UX Paths schema from ${SCHEMA_URL}: ${err.message}`
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
if (!res.ok) {
|
|
375
|
+
throw new Error(
|
|
376
|
+
`Unable to fetch the UX Paths schema from ${SCHEMA_URL}: HTTP ${res.status} ${res.statusText}`
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
cached = await res.json();
|
|
380
|
+
return cached;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// src/cli/ux-paths/commands/validate.ts
|
|
384
|
+
function registerValidate(program2) {
|
|
385
|
+
program2.command("validate [path]").description("Validate a flows.json against the DFL UX Paths v1 schema.").action(async (maybePath) => {
|
|
386
|
+
const path3 = resolve2(process.cwd(), maybePath || ".dfl-ux-paths/flows.json");
|
|
387
|
+
if (!existsSync2(path3)) {
|
|
388
|
+
console.error(chalk5.red("File not found:"), path3);
|
|
389
|
+
process.exit(1);
|
|
390
|
+
}
|
|
391
|
+
let doc;
|
|
392
|
+
try {
|
|
393
|
+
doc = JSON.parse(readFileSync(path3, "utf8"));
|
|
394
|
+
} catch (err) {
|
|
395
|
+
console.error(chalk5.red("Invalid JSON:"), err.message);
|
|
396
|
+
process.exit(1);
|
|
397
|
+
}
|
|
398
|
+
const ajv = new Ajv2020({ allErrors: true, strict: false });
|
|
399
|
+
addFormats(ajv);
|
|
400
|
+
let schema;
|
|
401
|
+
try {
|
|
402
|
+
schema = await loadSchemaV1();
|
|
403
|
+
} catch (err) {
|
|
404
|
+
console.error(chalk5.red("Schema error:"), err.message);
|
|
405
|
+
process.exit(1);
|
|
406
|
+
}
|
|
407
|
+
const validate = ajv.compile(schema);
|
|
408
|
+
const ok = validate(doc);
|
|
409
|
+
if (ok) {
|
|
410
|
+
console.log(chalk5.green("OK"), path3, "conforms to schema v1.");
|
|
411
|
+
process.exit(0);
|
|
412
|
+
}
|
|
413
|
+
console.error(chalk5.red("FAIL"), path3);
|
|
414
|
+
for (const err of validate.errors ?? []) {
|
|
415
|
+
console.error(
|
|
416
|
+
chalk5.yellow(" -"),
|
|
417
|
+
err.instancePath || "<root>",
|
|
418
|
+
err.message,
|
|
419
|
+
err.params ? JSON.stringify(err.params) : ""
|
|
420
|
+
);
|
|
421
|
+
}
|
|
422
|
+
process.exit(1);
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// src/cli/ux-paths/commands/generate-mermaid.ts
|
|
427
|
+
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync3 } from "fs";
|
|
428
|
+
import { resolve as resolve3, dirname } from "path";
|
|
429
|
+
import chalk6 from "chalk";
|
|
430
|
+
|
|
431
|
+
// src/cli/ux-paths/lib/json-to-mermaid.ts
|
|
432
|
+
function jsonToMermaid(doc) {
|
|
433
|
+
const lines = [];
|
|
434
|
+
lines.push("graph TD");
|
|
435
|
+
lines.push(` %% app_id: ${doc.app_id}`);
|
|
436
|
+
lines.push(` %% app_version: ${doc.app_version}`);
|
|
437
|
+
lines.push(` %% schema_version: ${doc.schema_version}`);
|
|
438
|
+
if (doc.test_metadata?.base_url) {
|
|
439
|
+
lines.push(` %% base_url: ${doc.test_metadata.base_url}`);
|
|
440
|
+
}
|
|
441
|
+
for (const screen of doc.screens) {
|
|
442
|
+
const lockPrefix = screen.prerequisites?.auth_required ? "\u{1F512} " : "";
|
|
443
|
+
lines.push(
|
|
444
|
+
` ${sanitizeId(screen.id)}["${escapeLabel(lockPrefix + screen.name)}"]`
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
for (const screen of doc.screens) {
|
|
448
|
+
if (screen.navigation_path && screen.navigation_path.length > 0) {
|
|
449
|
+
const summary = screen.navigation_path.map((s) => `${s.action} ${s.selector}`).join(" -> ");
|
|
450
|
+
lines.push(` %% nav[${sanitizeId(screen.id)}]: ${summary}`);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
for (const screen of doc.screens) {
|
|
454
|
+
for (const action of screen.actions ?? []) {
|
|
455
|
+
if (action.next_screen) {
|
|
456
|
+
lines.push(
|
|
457
|
+
` ${sanitizeId(screen.id)} -->|${escapeLabel(action.label)}| ${sanitizeId(
|
|
458
|
+
action.next_screen
|
|
459
|
+
)}`
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
for (const flow of doc.flows) {
|
|
465
|
+
const flowKey = sanitizeId(`flow_${flow.name}`);
|
|
466
|
+
lines.push(` subgraph ${flowKey}["Flow: ${escapeLabel(flow.name)}"]`);
|
|
467
|
+
const nodes = flowStepsToNodes(flow.start, flow.steps);
|
|
468
|
+
for (let i = 0; i < nodes.length - 1; i++) {
|
|
469
|
+
const from = nodes[i];
|
|
470
|
+
const to = nodes[i + 1];
|
|
471
|
+
if (to.label) {
|
|
472
|
+
lines.push(
|
|
473
|
+
` ${sanitizeId(from.id)} -.->|${escapeLabel(to.label)}| ${sanitizeId(to.id)}`
|
|
474
|
+
);
|
|
475
|
+
} else {
|
|
476
|
+
lines.push(` ${sanitizeId(from.id)} -.-> ${sanitizeId(to.id)}`);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
if (nodes.length === 1) {
|
|
480
|
+
lines.push(` ${sanitizeId(nodes[0].id)}`);
|
|
481
|
+
}
|
|
482
|
+
lines.push(" end");
|
|
483
|
+
}
|
|
484
|
+
if (doc.dead_code && doc.dead_code.length > 0) {
|
|
485
|
+
lines.push(" %% --- dead code ---");
|
|
486
|
+
for (const entry of doc.dead_code) {
|
|
487
|
+
lines.push(` %% dead: ${entry.component} \u2014 ${entry.reason}`);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
return lines.join("\n") + "\n";
|
|
491
|
+
}
|
|
492
|
+
function flowStepsToNodes(start, steps) {
|
|
493
|
+
const out = [{ id: start }];
|
|
494
|
+
for (const step of steps) {
|
|
495
|
+
if (typeof step === "string") {
|
|
496
|
+
out.push({ id: step });
|
|
497
|
+
} else {
|
|
498
|
+
const id = step.target_screen ?? step.screen;
|
|
499
|
+
const labelParts = [];
|
|
500
|
+
if (step.action) labelParts.push(step.action);
|
|
501
|
+
if (step.selector) labelParts.push(step.selector);
|
|
502
|
+
const label = labelParts.length > 0 ? labelParts.join(": ") : void 0;
|
|
503
|
+
out.push({ id, label });
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
return out;
|
|
507
|
+
}
|
|
508
|
+
function sanitizeId(raw) {
|
|
509
|
+
return raw.replace(/[^A-Za-z0-9_]/g, "_");
|
|
510
|
+
}
|
|
511
|
+
function escapeLabel(raw) {
|
|
512
|
+
return raw.replace(/"/g, '\\"');
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// src/cli/ux-paths/commands/generate-mermaid.ts
|
|
516
|
+
var HEADER = "%% AUTO-GENERATED by dfl-components ux-paths \u2014 do not edit by hand.\n";
|
|
517
|
+
function registerGenerateMermaid(program2) {
|
|
518
|
+
program2.command("generate-mermaid [path]").description("Generate a Mermaid graph from a flows.json file.").option("-o, --out <file>", "Output path (defaults to <dir>/flows.mmd).").action((maybePath, opts) => {
|
|
519
|
+
const path3 = resolve3(process.cwd(), maybePath || ".dfl-ux-paths/flows.json");
|
|
520
|
+
if (!existsSync3(path3)) {
|
|
521
|
+
console.error(chalk6.red("File not found:"), path3);
|
|
522
|
+
process.exit(1);
|
|
523
|
+
}
|
|
524
|
+
const doc = JSON.parse(readFileSync2(path3, "utf8"));
|
|
525
|
+
const out = opts.out ? resolve3(process.cwd(), opts.out) : resolve3(dirname(path3), "flows.mmd");
|
|
526
|
+
const body = HEADER + jsonToMermaid(doc);
|
|
527
|
+
writeFileSync2(out, body, "utf8");
|
|
528
|
+
console.log(chalk6.green("Wrote"), out);
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// src/cli/ux-paths/commands/diff.ts
|
|
533
|
+
import { readFileSync as readFileSync3, existsSync as existsSync4 } from "fs";
|
|
534
|
+
import { resolve as resolve4 } from "path";
|
|
535
|
+
import chalk7 from "chalk";
|
|
536
|
+
|
|
537
|
+
// src/cli/ux-paths/lib/flows-diff.ts
|
|
538
|
+
function flowsDiff(a, b) {
|
|
539
|
+
const aScreenIds = new Set(a.screens.map((s) => s.id));
|
|
540
|
+
const bScreenIds = new Set(b.screens.map((s) => s.id));
|
|
541
|
+
const screensAdded = [...bScreenIds].filter((id) => !aScreenIds.has(id)).sort();
|
|
542
|
+
const screensRemoved = [...aScreenIds].filter((id) => !bScreenIds.has(id)).sort();
|
|
543
|
+
const screensCommon = [...aScreenIds].filter((id) => bScreenIds.has(id)).sort();
|
|
544
|
+
const aActionKeys = collectActionKeys(a);
|
|
545
|
+
const bActionKeys = collectActionKeys(b);
|
|
546
|
+
const actionsAdded = [...bActionKeys].filter((k) => !aActionKeys.has(k)).sort();
|
|
547
|
+
const actionsRemoved = [...aActionKeys].filter((k) => !bActionKeys.has(k)).sort();
|
|
548
|
+
const aFlowsByName = new Map(a.flows.map((f) => [f.name, f]));
|
|
549
|
+
const bFlowsByName = new Map(b.flows.map((f) => [f.name, f]));
|
|
550
|
+
const flowsAdded = [...bFlowsByName.keys()].filter((n) => !aFlowsByName.has(n)).sort();
|
|
551
|
+
const flowsRemoved = [...aFlowsByName.keys()].filter((n) => !bFlowsByName.has(n)).sort();
|
|
552
|
+
const flowsChanged = [];
|
|
553
|
+
for (const name of [...aFlowsByName.keys()].sort()) {
|
|
554
|
+
const aFlow = aFlowsByName.get(name);
|
|
555
|
+
const bFlow = bFlowsByName.get(name);
|
|
556
|
+
if (!bFlow) continue;
|
|
557
|
+
const reasons = [];
|
|
558
|
+
if (aFlow.start !== bFlow.start) {
|
|
559
|
+
reasons.push(`start: ${aFlow.start} \u2192 ${bFlow.start}`);
|
|
560
|
+
}
|
|
561
|
+
if (!stepArraysEqual(aFlow.steps, bFlow.steps)) {
|
|
562
|
+
reasons.push(`steps changed (${aFlow.steps.length} \u2192 ${bFlow.steps.length})`);
|
|
563
|
+
}
|
|
564
|
+
if (!arraysEqual(aFlow.actions ?? [], bFlow.actions ?? [])) {
|
|
565
|
+
reasons.push("actions changed");
|
|
566
|
+
}
|
|
567
|
+
if (reasons.length > 0) {
|
|
568
|
+
flowsChanged.push({ name, reason: reasons.join("; ") });
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
return {
|
|
572
|
+
screens: { added: screensAdded, removed: screensRemoved, common: screensCommon },
|
|
573
|
+
actions: { added: actionsAdded, removed: actionsRemoved },
|
|
574
|
+
flows: { added: flowsAdded, removed: flowsRemoved, changed: flowsChanged }
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
function collectActionKeys(doc) {
|
|
578
|
+
const out = /* @__PURE__ */ new Set();
|
|
579
|
+
for (const screen of doc.screens) {
|
|
580
|
+
for (const action of screen.actions ?? []) {
|
|
581
|
+
out.add(`${screen.id}.${action.id}`);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
return out;
|
|
585
|
+
}
|
|
586
|
+
function arraysEqual(a, b) {
|
|
587
|
+
if (a.length !== b.length) return false;
|
|
588
|
+
for (let i = 0; i < a.length; i++) {
|
|
589
|
+
if (a[i] !== b[i]) return false;
|
|
590
|
+
}
|
|
591
|
+
return true;
|
|
592
|
+
}
|
|
593
|
+
function stepArraysEqual(a, b) {
|
|
594
|
+
if (a.length !== b.length) return false;
|
|
595
|
+
for (let i = 0; i < a.length; i++) {
|
|
596
|
+
if (stepKey(a[i]) !== stepKey(b[i])) return false;
|
|
597
|
+
}
|
|
598
|
+
return true;
|
|
599
|
+
}
|
|
600
|
+
function stepKey(step) {
|
|
601
|
+
if (typeof step === "string") return step;
|
|
602
|
+
return step.target_screen ?? step.screen;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// src/cli/ux-paths/commands/diff.ts
|
|
606
|
+
function registerDiff(program2) {
|
|
607
|
+
program2.command("diff <a> <b>").description("Diff screens/actions/flows between two flows.json files (a = baseline).").option("--json", "Emit machine-readable JSON instead of human-readable output.", false).action((aPath, bPath, opts) => {
|
|
608
|
+
const a = loadDoc(aPath);
|
|
609
|
+
const b = loadDoc(bPath);
|
|
610
|
+
const result = flowsDiff(a, b);
|
|
611
|
+
if (opts.json) {
|
|
612
|
+
console.log(JSON.stringify(result, null, 2));
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
console.log(chalk7.bold(`Diff ${a.app_id}@${a.app_version} \u2192 ${b.app_id}@${b.app_version}`));
|
|
616
|
+
printSection("Screens added", result.screens.added, chalk7.green);
|
|
617
|
+
printSection("Screens removed", result.screens.removed, chalk7.red);
|
|
618
|
+
printSection("Actions added", result.actions.added, chalk7.green);
|
|
619
|
+
printSection("Actions removed", result.actions.removed, chalk7.red);
|
|
620
|
+
printSection("Flows added", result.flows.added, chalk7.green);
|
|
621
|
+
printSection("Flows removed", result.flows.removed, chalk7.red);
|
|
622
|
+
if (result.flows.changed.length > 0) {
|
|
623
|
+
console.log(chalk7.yellow("\nFlows changed:"));
|
|
624
|
+
for (const ch of result.flows.changed) {
|
|
625
|
+
console.log(` - ${ch.name}: ${ch.reason}`);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
function loadDoc(p) {
|
|
631
|
+
const path3 = resolve4(process.cwd(), p);
|
|
632
|
+
if (!existsSync4(path3)) {
|
|
633
|
+
console.error(chalk7.red("File not found:"), path3);
|
|
634
|
+
process.exit(1);
|
|
635
|
+
}
|
|
636
|
+
return JSON.parse(readFileSync3(path3, "utf8"));
|
|
637
|
+
}
|
|
638
|
+
function printSection(label, items, color) {
|
|
639
|
+
if (items.length === 0) return;
|
|
640
|
+
console.log(color(`
|
|
641
|
+
${label} (${items.length}):`));
|
|
642
|
+
for (const item of items) {
|
|
643
|
+
console.log(` - ${item}`);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// src/cli/ux-paths/commands/stamp.ts
|
|
648
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync5 } from "fs";
|
|
649
|
+
import { resolve as resolve5 } from "path";
|
|
650
|
+
import { execSync } from "child_process";
|
|
651
|
+
import chalk8 from "chalk";
|
|
652
|
+
|
|
653
|
+
// src/cli/ux-paths/lib/preserve-format.ts
|
|
654
|
+
function replaceTopLevelStringField(raw, key, newValue) {
|
|
655
|
+
const re = new RegExp(`("${escapeRegExp(key)}"\\s*:\\s*")((?:[^"\\\\]|\\\\.)*)(")`);
|
|
656
|
+
if (!re.test(raw)) return raw;
|
|
657
|
+
return raw.replace(re, (_m, pre, _old, post) => {
|
|
658
|
+
return pre + escapeJsonStringValue(newValue) + post;
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
function stampPreserveFormat(raw, fields) {
|
|
662
|
+
let out = raw;
|
|
663
|
+
out = replaceTopLevelStringField(out, "app_version", fields.app_version);
|
|
664
|
+
out = replaceTopLevelStringField(out, "generated_at", fields.generated_at);
|
|
665
|
+
return out;
|
|
666
|
+
}
|
|
667
|
+
function escapeRegExp(s) {
|
|
668
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
669
|
+
}
|
|
670
|
+
function escapeJsonStringValue(s) {
|
|
671
|
+
const json = JSON.stringify(s);
|
|
672
|
+
return json.slice(1, -1);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
// src/cli/ux-paths/commands/stamp.ts
|
|
676
|
+
function registerStamp(program2) {
|
|
677
|
+
program2.command("stamp [path]").description("Refresh app_version to YYYY-MM-DD-<git-sha-short> and bump generated_at.").option(
|
|
678
|
+
"--no-preserve-format",
|
|
679
|
+
"Reformat the whole file (legacy). By default, stamp surgically updates only app_version/generated_at, preserving the existing formatting (minimal diff)."
|
|
680
|
+
).action((maybePath, opts) => {
|
|
681
|
+
const path3 = resolve5(process.cwd(), maybePath || ".dfl-ux-paths/flows.json");
|
|
682
|
+
if (!existsSync5(path3)) {
|
|
683
|
+
console.error(chalk8.red("File not found:"), path3);
|
|
684
|
+
process.exit(1);
|
|
685
|
+
}
|
|
686
|
+
const raw = readFileSync4(path3, "utf8");
|
|
687
|
+
const doc = JSON.parse(raw);
|
|
688
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
689
|
+
let sha = "0000000";
|
|
690
|
+
try {
|
|
691
|
+
sha = execSync("git rev-parse --short=7 HEAD", { stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
692
|
+
if (!sha) sha = "0000000";
|
|
693
|
+
} catch {
|
|
694
|
+
}
|
|
695
|
+
const newVersion = `${today}-${sha}`;
|
|
696
|
+
const newGeneratedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
697
|
+
const preserve = opts.preserveFormat !== false;
|
|
698
|
+
let output;
|
|
699
|
+
if (preserve) {
|
|
700
|
+
output = stampPreserveFormat(raw, {
|
|
701
|
+
app_version: newVersion,
|
|
702
|
+
generated_at: newGeneratedAt
|
|
703
|
+
});
|
|
704
|
+
} else {
|
|
705
|
+
const updated = {
|
|
706
|
+
...doc,
|
|
707
|
+
app_version: newVersion,
|
|
708
|
+
generated_at: newGeneratedAt
|
|
709
|
+
};
|
|
710
|
+
output = JSON.stringify(updated, null, 2) + "\n";
|
|
711
|
+
}
|
|
712
|
+
writeFileSync3(path3, output, "utf8");
|
|
713
|
+
console.log(chalk8.green("Stamped"), path3, preserve ? chalk8.gray("(minimal diff)") : "");
|
|
714
|
+
console.log(chalk8.gray(` app_version: ${newVersion}`));
|
|
715
|
+
console.log(chalk8.gray(` generated_at: ${newGeneratedAt}`));
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
// src/cli/ux-paths/index.ts
|
|
720
|
+
function registerUxPaths(program2) {
|
|
721
|
+
const group = program2.command("ux-paths").description("Versioned, schema-validated per-app user-flow mapping (DFL UX Paths).");
|
|
722
|
+
registerInit(group);
|
|
723
|
+
registerValidate(group);
|
|
724
|
+
registerGenerateMermaid(group);
|
|
725
|
+
registerDiff(group);
|
|
726
|
+
registerStamp(group);
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
// src/cli/index.ts
|
|
730
|
+
var program = new Command();
|
|
731
|
+
program.name("dfl-components").description(
|
|
732
|
+
"DevFellowship components CLI \u2014 add shared components AND map app UX paths (folds the dfl-ux-paths CLI). Shipped as the `dfl-components` bin of @devfellowship/components."
|
|
733
|
+
).version("1.0.0");
|
|
734
|
+
program.command("init").description("Initialize your project with dfl-components configuration").option("-y, --yes", "Skip prompts and use defaults").option("-c, --cwd <path>", "Working directory", process.cwd()).action(init);
|
|
735
|
+
program.command("add").description("Add a component to your project").argument("[components...]", "Components to add").option("-y, --yes", "Skip confirmation prompts").option("-o, --overwrite", "Overwrite existing files").option("-c, --cwd <path>", "Working directory", process.cwd()).option("-a, --all", "Add all available components").action(add);
|
|
736
|
+
registerUxPaths(program);
|
|
737
|
+
program.parse();
|
package/dist/index.cjs
CHANGED
|
@@ -167,7 +167,7 @@ __export(src_exports, {
|
|
|
167
167
|
InputOTPGroup: () => InputOTPGroup,
|
|
168
168
|
InputOTPSeparator: () => InputOTPSeparator,
|
|
169
169
|
InputOTPSlot: () => InputOTPSlot,
|
|
170
|
-
Kbd: () =>
|
|
170
|
+
Kbd: () => Kbd2,
|
|
171
171
|
Label: () => Label3,
|
|
172
172
|
LoginPage: () => LoginPage,
|
|
173
173
|
LoginScreen: () => LoginScreen,
|
|
@@ -492,13 +492,37 @@ var Spinner = () => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
|
492
492
|
]
|
|
493
493
|
}
|
|
494
494
|
);
|
|
495
|
+
var Kbd = ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
496
|
+
"kbd",
|
|
497
|
+
{
|
|
498
|
+
className: cn(
|
|
499
|
+
"ml-1 inline-flex items-center justify-center font-mono uppercase tracking-[0.04em]",
|
|
500
|
+
"text-[10.5px] text-[var(--s-ink-muted)]",
|
|
501
|
+
"min-w-[18px] h-[18px] px-1 rounded-[var(--p-radius-sm)]",
|
|
502
|
+
"border border-[var(--s-border-subtle)] bg-[var(--s-surface-raised)]"
|
|
503
|
+
),
|
|
504
|
+
children
|
|
505
|
+
}
|
|
506
|
+
);
|
|
495
507
|
var Button = React3.forwardRef(
|
|
496
508
|
({ className, variant, size, asChild = false, loading = false, disabled, kbd, children, ...props }, ref) => {
|
|
497
|
-
const
|
|
509
|
+
const classes = cn(buttonVariants({ variant, size, className }));
|
|
510
|
+
if (asChild) {
|
|
511
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
512
|
+
import_react_slot.Slot,
|
|
513
|
+
{
|
|
514
|
+
className: classes,
|
|
515
|
+
ref,
|
|
516
|
+
"data-loading": loading ? "" : void 0,
|
|
517
|
+
...props,
|
|
518
|
+
children
|
|
519
|
+
}
|
|
520
|
+
);
|
|
521
|
+
}
|
|
498
522
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
499
|
-
|
|
523
|
+
"button",
|
|
500
524
|
{
|
|
501
|
-
className:
|
|
525
|
+
className: classes,
|
|
502
526
|
ref,
|
|
503
527
|
disabled: disabled || loading,
|
|
504
528
|
"data-loading": loading ? "" : void 0,
|
|
@@ -506,18 +530,7 @@ var Button = React3.forwardRef(
|
|
|
506
530
|
children: [
|
|
507
531
|
loading ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Spinner, {}) : null,
|
|
508
532
|
children,
|
|
509
|
-
kbd ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
510
|
-
"kbd",
|
|
511
|
-
{
|
|
512
|
-
className: cn(
|
|
513
|
-
"ml-1 inline-flex items-center justify-center font-mono uppercase tracking-[0.04em]",
|
|
514
|
-
"text-[10.5px] text-[var(--s-ink-muted)]",
|
|
515
|
-
"min-w-[18px] h-[18px] px-1 rounded-[var(--p-radius-sm)]",
|
|
516
|
-
"border border-[var(--s-border-subtle)] bg-[var(--s-surface-raised)]"
|
|
517
|
-
),
|
|
518
|
-
children: kbd
|
|
519
|
-
}
|
|
520
|
-
) : null
|
|
533
|
+
kbd ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Kbd, { children: kbd }) : null
|
|
521
534
|
]
|
|
522
535
|
}
|
|
523
536
|
);
|
|
@@ -2270,8 +2283,8 @@ var kbdVariants = (0, import_class_variance_authority6.cva)(
|
|
|
2270
2283
|
}
|
|
2271
2284
|
}
|
|
2272
2285
|
);
|
|
2273
|
-
var
|
|
2274
|
-
|
|
2286
|
+
var Kbd2 = React18.forwardRef(({ className, size, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("kbd", { ref, className: cn(kbdVariants({ size }), className), ...props }));
|
|
2287
|
+
Kbd2.displayName = "Kbd";
|
|
2275
2288
|
|
|
2276
2289
|
// src/components/menubar.tsx
|
|
2277
2290
|
var MenubarPrimitive = __toESM(require("@radix-ui/react-menubar"), 1);
|
package/dist/index.js
CHANGED
|
@@ -166,13 +166,37 @@ var Spinner = () => /* @__PURE__ */ jsxs2(
|
|
|
166
166
|
]
|
|
167
167
|
}
|
|
168
168
|
);
|
|
169
|
+
var Kbd = ({ children }) => /* @__PURE__ */ jsx3(
|
|
170
|
+
"kbd",
|
|
171
|
+
{
|
|
172
|
+
className: cn(
|
|
173
|
+
"ml-1 inline-flex items-center justify-center font-mono uppercase tracking-[0.04em]",
|
|
174
|
+
"text-[10.5px] text-[var(--s-ink-muted)]",
|
|
175
|
+
"min-w-[18px] h-[18px] px-1 rounded-[var(--p-radius-sm)]",
|
|
176
|
+
"border border-[var(--s-border-subtle)] bg-[var(--s-surface-raised)]"
|
|
177
|
+
),
|
|
178
|
+
children
|
|
179
|
+
}
|
|
180
|
+
);
|
|
169
181
|
var Button = React3.forwardRef(
|
|
170
182
|
({ className, variant, size, asChild = false, loading = false, disabled, kbd, children, ...props }, ref) => {
|
|
171
|
-
const
|
|
183
|
+
const classes = cn(buttonVariants({ variant, size, className }));
|
|
184
|
+
if (asChild) {
|
|
185
|
+
return /* @__PURE__ */ jsx3(
|
|
186
|
+
Slot,
|
|
187
|
+
{
|
|
188
|
+
className: classes,
|
|
189
|
+
ref,
|
|
190
|
+
"data-loading": loading ? "" : void 0,
|
|
191
|
+
...props,
|
|
192
|
+
children
|
|
193
|
+
}
|
|
194
|
+
);
|
|
195
|
+
}
|
|
172
196
|
return /* @__PURE__ */ jsxs2(
|
|
173
|
-
|
|
197
|
+
"button",
|
|
174
198
|
{
|
|
175
|
-
className:
|
|
199
|
+
className: classes,
|
|
176
200
|
ref,
|
|
177
201
|
disabled: disabled || loading,
|
|
178
202
|
"data-loading": loading ? "" : void 0,
|
|
@@ -180,18 +204,7 @@ var Button = React3.forwardRef(
|
|
|
180
204
|
children: [
|
|
181
205
|
loading ? /* @__PURE__ */ jsx3(Spinner, {}) : null,
|
|
182
206
|
children,
|
|
183
|
-
kbd ? /* @__PURE__ */ jsx3(
|
|
184
|
-
"kbd",
|
|
185
|
-
{
|
|
186
|
-
className: cn(
|
|
187
|
-
"ml-1 inline-flex items-center justify-center font-mono uppercase tracking-[0.04em]",
|
|
188
|
-
"text-[10.5px] text-[var(--s-ink-muted)]",
|
|
189
|
-
"min-w-[18px] h-[18px] px-1 rounded-[var(--p-radius-sm)]",
|
|
190
|
-
"border border-[var(--s-border-subtle)] bg-[var(--s-surface-raised)]"
|
|
191
|
-
),
|
|
192
|
-
children: kbd
|
|
193
|
-
}
|
|
194
|
-
) : null
|
|
207
|
+
kbd ? /* @__PURE__ */ jsx3(Kbd, { children: kbd }) : null
|
|
195
208
|
]
|
|
196
209
|
}
|
|
197
210
|
);
|
|
@@ -1944,8 +1957,8 @@ var kbdVariants = cva6(
|
|
|
1944
1957
|
}
|
|
1945
1958
|
}
|
|
1946
1959
|
);
|
|
1947
|
-
var
|
|
1948
|
-
|
|
1960
|
+
var Kbd2 = React18.forwardRef(({ className, size, ...props }, ref) => /* @__PURE__ */ jsx26("kbd", { ref, className: cn(kbdVariants({ size }), className), ...props }));
|
|
1961
|
+
Kbd2.displayName = "Kbd";
|
|
1949
1962
|
|
|
1950
1963
|
// src/components/menubar.tsx
|
|
1951
1964
|
import * as MenubarPrimitive from "@radix-ui/react-menubar";
|
|
@@ -5242,7 +5255,7 @@ export {
|
|
|
5242
5255
|
InputOTPGroup,
|
|
5243
5256
|
InputOTPSeparator,
|
|
5244
5257
|
InputOTPSlot,
|
|
5245
|
-
Kbd,
|
|
5258
|
+
Kbd2 as Kbd,
|
|
5246
5259
|
Label3 as Label,
|
|
5247
5260
|
LoginPage,
|
|
5248
5261
|
LoginScreen,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devfellowship/components",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.3",
|
|
4
4
|
"description": "DFL Design System — UI components, hooks, utils and providers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": [
|
|
@@ -35,11 +35,16 @@
|
|
|
35
35
|
"main": "./dist/index.cjs",
|
|
36
36
|
"module": "./dist/index.js",
|
|
37
37
|
"types": "./dist/index.d.ts",
|
|
38
|
+
"bin": {
|
|
39
|
+
"dfl-components": "./dist/cli.js"
|
|
40
|
+
},
|
|
38
41
|
"files": [
|
|
39
42
|
"dist"
|
|
40
43
|
],
|
|
41
44
|
"scripts": {
|
|
42
|
-
"build": "tsup",
|
|
45
|
+
"build": "tsup && tsup --config tsup.cli.config.ts",
|
|
46
|
+
"build:lib": "tsup",
|
|
47
|
+
"build:cli": "tsup --config tsup.cli.config.ts",
|
|
43
48
|
"build:watch": "tsup --watch",
|
|
44
49
|
"typecheck": "tsc --noEmit",
|
|
45
50
|
"test": "vitest run",
|
|
@@ -72,6 +77,9 @@
|
|
|
72
77
|
"@tailwindcss/vite": "^4.2.2",
|
|
73
78
|
"@testing-library/jest-dom": "^6.9.1",
|
|
74
79
|
"@testing-library/react": "^16.3.2",
|
|
80
|
+
"@types/fs-extra": "^11.0.4",
|
|
81
|
+
"@types/node": "^20.0.0",
|
|
82
|
+
"@types/prompts": "^2.4.9",
|
|
75
83
|
"@types/react": "^18.3.28",
|
|
76
84
|
"@types/react-dom": "^18.3.7",
|
|
77
85
|
"@vitejs/plugin-react": "^4.7.0",
|
|
@@ -114,20 +122,30 @@
|
|
|
114
122
|
"@radix-ui/react-toggle-group": "^1.1.11",
|
|
115
123
|
"@radix-ui/react-tooltip": "^1.1.4",
|
|
116
124
|
"@testing-library/dom": "^10.4.1",
|
|
125
|
+
"ajv": "^8.17.1",
|
|
126
|
+
"ajv-formats": "^3.0.1",
|
|
127
|
+
"chalk": "^5.3.0",
|
|
117
128
|
"class-variance-authority": "^0.7.1",
|
|
118
129
|
"clsx": "^2.1.1",
|
|
119
130
|
"cmdk": "^1.1.1",
|
|
131
|
+
"commander": "^12.0.0",
|
|
132
|
+
"cosmiconfig": "^9.0.0",
|
|
120
133
|
"date-fns": "^3.6.0",
|
|
121
134
|
"embla-carousel-react": "^8.6.0",
|
|
135
|
+
"fs-extra": "^11.2.0",
|
|
122
136
|
"input-otp": "^1.4.2",
|
|
123
137
|
"lucide-react": "^0.462.0",
|
|
124
138
|
"next-themes": "^0.4.6",
|
|
139
|
+
"node-fetch": "^3.3.2",
|
|
140
|
+
"ora": "^8.0.1",
|
|
141
|
+
"prompts": "^2.4.2",
|
|
125
142
|
"react-day-picker": "^8.10.1",
|
|
126
143
|
"react-hook-form": "^7.72.1",
|
|
127
144
|
"react-resizable-panels": "^2.1.9",
|
|
128
145
|
"recharts": "^2.15.0",
|
|
129
146
|
"sonner": "^2.0.7",
|
|
130
147
|
"tailwind-merge": "^2.5.2",
|
|
131
|
-
"vaul": "^1.1.2"
|
|
148
|
+
"vaul": "^1.1.2",
|
|
149
|
+
"zod": "^3.23.8"
|
|
132
150
|
}
|
|
133
151
|
}
|