synthesisui 0.1.10 → 0.1.12
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/commands/generate.js +53 -0
- package/dist/guide.js +17 -0
- package/dist/index.js +17 -0
- package/dist/registry.js +26 -0
- package/package.json +1 -1
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { mkdir, readdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { resolveRegistry } from "../config.js";
|
|
4
|
+
import { postGenerate, RegistryError } from "../registry.js";
|
|
5
|
+
/** Slugs materialized under `_synthesisui/ds/` in the project. */
|
|
6
|
+
async function installedSlugs(root) {
|
|
7
|
+
try {
|
|
8
|
+
const entries = await readdir(join(root, "_synthesisui", "ds"), {
|
|
9
|
+
withFileTypes: true,
|
|
10
|
+
});
|
|
11
|
+
return entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return [];
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Generates a token-only component recipe for the project's design system
|
|
19
|
+
* (chat-gen PRO, hosted) and materializes it additively under
|
|
20
|
+
* `_synthesisui/ds/<slug>/generated/`. The recipe wears the DS by construction;
|
|
21
|
+
* the prompt lives server-side. Nothing else in the project is touched.
|
|
22
|
+
*/
|
|
23
|
+
export async function generate(description, opts) {
|
|
24
|
+
const base = resolveRegistry(opts.registry);
|
|
25
|
+
const root = opts.dir ?? process.cwd();
|
|
26
|
+
let slug = opts.ds;
|
|
27
|
+
if (!slug) {
|
|
28
|
+
const slugs = await installedSlugs(root);
|
|
29
|
+
if (slugs.length === 1) {
|
|
30
|
+
slug = slugs[0];
|
|
31
|
+
}
|
|
32
|
+
else if (slugs.length === 0) {
|
|
33
|
+
throw new RegistryError("No design system installed here. Run `synthesisui add <slug>` first, or pass --ds <slug>.");
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
throw new RegistryError(`Multiple design systems installed (${slugs.join(", ")}). Pick one with --ds <slug>.`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
console.log(`→ generating a component for "${slug}" at ${base} …`);
|
|
40
|
+
const res = await postGenerate(base, { slug, description, name: opts.name });
|
|
41
|
+
const dir = join(root, "_synthesisui", "ds", slug, "generated");
|
|
42
|
+
await mkdir(dir, { recursive: true });
|
|
43
|
+
await writeFile(join(dir, `${res.name}.json`), `${JSON.stringify(res.recipe, null, 2)}\n`, "utf8");
|
|
44
|
+
await writeFile(join(dir, `${res.name}.css`), `${res.css}\n`, "utf8");
|
|
45
|
+
const tries = `${res.tries} ${res.tries === 1 ? "try" : "tries"}`;
|
|
46
|
+
console.log(`✓ ${res.name} generated (${res.model}, ${tries})`);
|
|
47
|
+
console.log(` → _synthesisui/ds/${slug}/generated/${res.name}.{json,css}`);
|
|
48
|
+
console.log("");
|
|
49
|
+
console.log("Use it:");
|
|
50
|
+
console.log(` • @import "_synthesisui/ds/${slug}/generated/${res.name}.css" in your CSS`);
|
|
51
|
+
console.log(` • <div data-ds="${slug}"><div class="ds-${res.name}">…</div></div>`);
|
|
52
|
+
console.log(` (${res.usage.inputTokens} in / ${res.usage.outputTokens} out tokens — recipe is additive; nothing else changed)`);
|
|
53
|
+
}
|
package/dist/guide.js
CHANGED
|
@@ -94,6 +94,9 @@ its typographic identity. Load them once (any one approach):
|
|
|
94
94
|
const hasTailwind = "theme.css" in payload.artifacts;
|
|
95
95
|
const hasParts = Object.values(components).some((r) => r.parts && Object.keys(r.parts).length > 0);
|
|
96
96
|
const componentLines = Object.entries(components).map(([cname, recipe]) => componentEntry(cname, recipe));
|
|
97
|
+
// Engagement blocks (gamification library) — category apart from core components.
|
|
98
|
+
const blockEntries = Object.entries(doc.blocks ?? {});
|
|
99
|
+
const blockLines = blockEntries.map(([bname, recipe]) => componentEntry(bname, recipe));
|
|
97
100
|
const artifactList = Object.keys(payload.artifacts)
|
|
98
101
|
.map((f) => `\`${f}\``)
|
|
99
102
|
.join(", ");
|
|
@@ -278,7 +281,21 @@ Each recipe becomes a \`.ds-<name>\` class (inside the \`[data-ds="${slug}"]\` s
|
|
|
278
281
|
multi-part components expose \`.ds-<name>-<part>\` classes (listed under each).
|
|
279
282
|
|
|
280
283
|
${componentLines.join("\n\n")}
|
|
284
|
+
${blockEntries.length
|
|
285
|
+
? `
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## Engagement blocks (optional)
|
|
281
289
|
|
|
290
|
+
A small gamification library the AI advisor (\`synthesisui advise\`) can propose — same
|
|
291
|
+
\`.ds-<name>\` recipe shape as the components above, token-only so they wear the system. Use them
|
|
292
|
+
**only where they fit the product** (progress, retention, recognition); they're a library to compose
|
|
293
|
+
from, not a default — and lean against over-gamifying a serious B2B product. Each is a \`.ds-<name>\`
|
|
294
|
+
class inside the \`[data-ds="${slug}"]\` scope; multi-part ones expose \`.ds-<name>-<part>\`.
|
|
295
|
+
|
|
296
|
+
${blockLines.join("\n\n")}
|
|
297
|
+
`
|
|
298
|
+
: ""}
|
|
282
299
|
---
|
|
283
300
|
|
|
284
301
|
_Full canonical source of truth (including values and keyframes) in \`design-system.json\`._
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { add } from "./commands/add.js";
|
|
3
3
|
import { advise } from "./commands/advise.js";
|
|
4
|
+
import { generate } from "./commands/generate.js";
|
|
4
5
|
import { list } from "./commands/list.js";
|
|
5
6
|
import { login } from "./commands/login.js";
|
|
6
7
|
import { RegistryError } from "./registry.js";
|
|
@@ -11,11 +12,14 @@ Usage:
|
|
|
11
12
|
synthesisui list [options] list the published design systems
|
|
12
13
|
synthesisui add <slug> [options] materialize a DS into _synthesisui/ds/<slug>/
|
|
13
14
|
synthesisui advise "<value prop>" engagement-pattern proposals for this project (login required)
|
|
15
|
+
synthesisui generate "<desc>" generate a token-only component recipe for your DS (login required)
|
|
14
16
|
|
|
15
17
|
Options:
|
|
16
18
|
--registry <url> registry URL (or env SYNTHESISUI_REGISTRY_URL)
|
|
17
19
|
--dir <path> consumer project root (default: current directory)
|
|
18
20
|
--version <n> install a specific version (default: latest)
|
|
21
|
+
--ds <slug> target design system for generate (default: the installed one)
|
|
22
|
+
--name <name> preferred component name for generate
|
|
19
23
|
-h, --help this help
|
|
20
24
|
|
|
21
25
|
Examples:
|
|
@@ -25,6 +29,7 @@ Examples:
|
|
|
25
29
|
synthesisui add halogen --version 3
|
|
26
30
|
synthesisui add halogen --registry http://localhost:3737
|
|
27
31
|
synthesisui advise "habit-building app for tracking personal finances"
|
|
32
|
+
synthesisui generate "an upgrade banner with a title, message and a primary CTA"
|
|
28
33
|
`;
|
|
29
34
|
/** Extracts simple `--flag value` pairs and the remaining positionals. */
|
|
30
35
|
function parseFlags(argv) {
|
|
@@ -97,6 +102,18 @@ async function main() {
|
|
|
97
102
|
await advise(valueProp, { registry, dir });
|
|
98
103
|
break;
|
|
99
104
|
}
|
|
105
|
+
case "generate": {
|
|
106
|
+
const description = args.join(" ").trim();
|
|
107
|
+
if (!description) {
|
|
108
|
+
console.error('error: describe the component — `synthesisui generate "<description>"`');
|
|
109
|
+
process.exitCode = 1;
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const ds = typeof flags.ds === "string" ? flags.ds : undefined;
|
|
113
|
+
const name = typeof flags.name === "string" ? flags.name : undefined;
|
|
114
|
+
await generate(description, { registry, dir, ds, name });
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
100
117
|
default:
|
|
101
118
|
console.error(`unknown command: "${command}"\n`);
|
|
102
119
|
console.log(HELP);
|
package/dist/registry.js
CHANGED
|
@@ -68,3 +68,29 @@ export async function postAdvisor(base, context) {
|
|
|
68
68
|
}
|
|
69
69
|
return (await res.json());
|
|
70
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Gera uma recipe token-only (chat-gen PRO) via `POST /api/ai/generate`.
|
|
73
|
+
* Gated + metered server-side: 401 = sem login, 429 = cota diária estourada.
|
|
74
|
+
*/
|
|
75
|
+
export async function postGenerate(base, payload) {
|
|
76
|
+
let res;
|
|
77
|
+
try {
|
|
78
|
+
res = await fetch(`${base}/api/ai/generate`, {
|
|
79
|
+
method: "POST",
|
|
80
|
+
headers: { "content-type": "application/json", ...(await authHeaders()) },
|
|
81
|
+
body: JSON.stringify(payload),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
throw new RegistryError(`Could not reach the registry at ${base}. ` +
|
|
86
|
+
`Check the URL (--registry / SYNTHESISUI_REGISTRY_URL) and your connection.`);
|
|
87
|
+
}
|
|
88
|
+
if (res.status === 401) {
|
|
89
|
+
throw new RegistryError("Not authenticated. Run `synthesisui login` first.");
|
|
90
|
+
}
|
|
91
|
+
if (!res.ok) {
|
|
92
|
+
const body = (await res.json().catch(() => ({})));
|
|
93
|
+
throw new RegistryError(body.message ?? `Generate responded ${res.status}.`);
|
|
94
|
+
}
|
|
95
|
+
return (await res.json());
|
|
96
|
+
}
|