synthesisui 0.1.13 → 0.1.14

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.
@@ -0,0 +1,21 @@
1
+ import { DEFAULT_CONFIG, writeProjectConfig } from "../config.js";
2
+ /**
3
+ * Writes `_synthesisui/config.json` — where `synthesisui page` materializes
4
+ * pages and which framework to target. Committable; safe to re-run.
5
+ */
6
+ export async function init(opts) {
7
+ const root = opts.dir ?? process.cwd();
8
+ const target = opts.target === "general" ? "general" : "next";
9
+ const config = {
10
+ target,
11
+ pagesDir: opts.pagesDir ?? (target === "next" ? "app" : DEFAULT_CONFIG.pagesDir),
12
+ };
13
+ await writeProjectConfig(root, config);
14
+ console.log("✓ wrote _synthesisui/config.json");
15
+ console.log(` target: ${config.target}`);
16
+ console.log(` pagesDir: ${config.pagesDir}`);
17
+ console.log("");
18
+ console.log("Next steps:");
19
+ console.log(" • synthesisui add <slug> bring a design system in");
20
+ console.log(" • synthesisui page <slug> <template> materialize a full page");
21
+ }
@@ -0,0 +1,32 @@
1
+ import { mkdir, writeFile } from "node:fs/promises";
2
+ import { dirname, join } from "node:path";
3
+ import { readProjectConfig, resolveRegistry } from "../config.js";
4
+ import { fetchPage } from "../registry.js";
5
+ /**
6
+ * Materializes a whole page from a DS template into the project (hybrid
7
+ * codegen-first): the server codegens a deterministic file, we write it, and
8
+ * the agent refines it in place. The page uses the DS's `.ds-*` classes +
9
+ * inline token vars, so it re-vests once `tokens.css` is imported.
10
+ */
11
+ export async function page(slug, template, opts) {
12
+ const base = resolveRegistry(opts.registry);
13
+ const root = opts.dir ?? process.cwd();
14
+ const config = await readProjectConfig(root);
15
+ const target = opts.target === "general" || opts.target === "next"
16
+ ? opts.target
17
+ : config.target;
18
+ console.log(`→ generating "${template}" from "${slug}" (${target}) …`);
19
+ const generated = await fetchPage(base, slug, template, target, opts.version);
20
+ // --out wins; otherwise <pagesDir>/<filename> (e.g. app/dashboard.tsx).
21
+ const relPath = opts.out ?? join(config.pagesDir, generated.filename);
22
+ const outPath = join(root, relPath);
23
+ await mkdir(dirname(outPath), { recursive: true });
24
+ await writeFile(outPath, generated.code, "utf8");
25
+ console.log(`✓ wrote ${relPath} (${slug} v${generated.version})`);
26
+ console.log("");
27
+ console.log("Next steps:");
28
+ console.log(` • ensure the DS is installed: synthesisui add ${slug} (provides tokens.css)`);
29
+ console.log(` • @import "_synthesisui/ds/${slug}/tokens.css" in your global CSS`);
30
+ console.log(" • refine the file: wire real data, split into components, swap placeholders");
31
+ console.log(` • keep the data-ds="${slug}" wrapper and the ds-* classes (stays on-system)`);
32
+ }
package/dist/config.js CHANGED
@@ -35,3 +35,31 @@ export async function writeToken(token, registry) {
35
35
  mode: 0o600,
36
36
  });
37
37
  }
38
+ /** Project-level config (committed): `<root>/_synthesisui/config.json`. */
39
+ export const DEFAULT_CONFIG = {
40
+ target: "next",
41
+ pagesDir: "app",
42
+ };
43
+ const projectConfigPath = (root) => join(root, "_synthesisui", "config.json");
44
+ /** Reads the project config, falling back to defaults when absent/invalid. */
45
+ export async function readProjectConfig(root) {
46
+ try {
47
+ const raw = await readFile(projectConfigPath(root), "utf8");
48
+ const parsed = JSON.parse(raw);
49
+ return {
50
+ target: parsed.target === "general" ? "general" : "next",
51
+ pagesDir: typeof parsed.pagesDir === "string" && parsed.pagesDir
52
+ ? parsed.pagesDir
53
+ : DEFAULT_CONFIG.pagesDir,
54
+ };
55
+ }
56
+ catch {
57
+ return DEFAULT_CONFIG;
58
+ }
59
+ }
60
+ /** Writes the project config (committable, plain JSON). */
61
+ export async function writeProjectConfig(root, config) {
62
+ const path = projectConfigPath(root);
63
+ await mkdir(dirname(path), { recursive: true });
64
+ await writeFile(path, `${JSON.stringify(config, null, 2)}\n`, "utf8");
65
+ }
package/dist/guide.js CHANGED
@@ -100,6 +100,26 @@ its typographic identity. Load them once (any one approach):
100
100
  const artifactList = Object.keys(payload.artifacts)
101
101
  .map((f) => `\`${f}\``)
102
102
  .join(", ");
103
+ const layoutNames = Object.keys(doc.layouts ?? {});
104
+ const pagesSection = layoutNames.length > 0
105
+ ? `
106
+ ## Full pages (templates)
107
+
108
+ This system ships whole-page templates: ${layoutNames.map((n) => `\`${n}\``).join(", ")}.
109
+
110
+ Materialize one as a real file:
111
+ \`\`\`bash
112
+ synthesisui page ${slug} ${layoutNames[0]} # Next .tsx (default)
113
+ synthesisui page ${slug} ${layoutNames[0]} --target general # plain HTML
114
+ \`\`\`
115
+ It writes a **deterministic scaffold** using this DS's \`.ds-*\` classes + inline token vars. **Refine it
116
+ in place** — wire real data, split into components, swap the chart/icon/media placeholders — but keep the
117
+ \`data-ds="${slug}"\` wrapper and the \`.ds-*\` classes so it stays on-system. Run \`synthesisui init\` once
118
+ to set the target (next/general) and the output folder.
119
+
120
+ ---
121
+ `
122
+ : "";
103
123
  const hasRules = (payload.rules?.length ?? 0) > 0;
104
124
  const rulesNote = hasRules
105
125
  ? `
@@ -192,7 +212,7 @@ those, not the versioned ones. The pinned files for this version — ${artifactL
192
212
  \`_synthesisui/ds/${slug}/v${version}/\`.
193
213
 
194
214
  ---
195
-
215
+ ${pagesSection}
196
216
  ## Building with the system
197
217
 
198
218
  **This system is for building real product UI** — pages, layouts, dashboards, whole flows.
package/dist/index.js CHANGED
@@ -2,17 +2,21 @@
2
2
  import { add } from "./commands/add.js";
3
3
  import { advise } from "./commands/advise.js";
4
4
  import { generate } from "./commands/generate.js";
5
+ import { init } from "./commands/init.js";
5
6
  import { list } from "./commands/list.js";
6
7
  import { login } from "./commands/login.js";
8
+ import { page } from "./commands/page.js";
7
9
  import { RegistryError } from "./registry.js";
8
10
  const HELP = `synthesisui — bring SynthesisUI design systems into your project
9
11
 
10
12
  Usage:
11
- synthesisui login [options] connect the CLI to your account (device-flow)
12
- synthesisui list [options] list the published design systems
13
- synthesisui add <slug> [options] materialize a DS into _synthesisui/ds/<slug>/
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)
13
+ synthesisui login [options] connect the CLI to your account (device-flow)
14
+ synthesisui init [options] write _synthesisui/config.json (target + pages dir)
15
+ synthesisui list [options] list the published design systems
16
+ synthesisui add <slug> [options] materialize a DS into _synthesisui/ds/<slug>/
17
+ synthesisui page <slug> <template> materialize a whole page from a DS template
18
+ synthesisui advise "<value prop>" engagement-pattern proposals for this project (login required)
19
+ synthesisui generate "<desc>" generate a token-only component recipe for your DS (login required)
16
20
 
17
21
  Options:
18
22
  --registry <url> registry URL (or env SYNTHESISUI_REGISTRY_URL)
@@ -20,14 +24,18 @@ Options:
20
24
  --version <n> install a specific version (default: latest)
21
25
  --ds <slug> target design system for generate (default: the installed one)
22
26
  --name <name> preferred component name for generate
27
+ --target <t> page/init target: next | general (default: next)
28
+ --out <path> output path for the generated page (default: <pagesDir>/<file>)
23
29
  -h, --help this help
24
30
 
25
31
  Examples:
26
32
  synthesisui login
33
+ synthesisui init --target next
27
34
  synthesisui list
28
35
  synthesisui add halogen
29
36
  synthesisui add halogen --version 3
30
- synthesisui add halogen --registry http://localhost:3737
37
+ synthesisui page halogen dashboard-sidebar
38
+ synthesisui page halogen landing --out app/page.tsx
31
39
  synthesisui advise "habit-building app for tracking personal finances"
32
40
  synthesisui generate "an upgrade banner with a title, message and a primary CTA"
33
41
  `;
@@ -92,6 +100,33 @@ async function main() {
92
100
  case "login":
93
101
  await login({ registry });
94
102
  break;
103
+ case "init": {
104
+ const target = typeof flags.target === "string" ? flags.target : undefined;
105
+ await init({ dir, target });
106
+ break;
107
+ }
108
+ case "page": {
109
+ const slug = args[0];
110
+ const template = args[1];
111
+ if (!slug || !template) {
112
+ console.error("error: provide slug and template — `synthesisui page <slug> <template>`");
113
+ process.exitCode = 1;
114
+ return;
115
+ }
116
+ let version;
117
+ if (typeof flags.version === "string") {
118
+ version = Number.parseInt(flags.version.replace(/^v/i, ""), 10);
119
+ if (!Number.isInteger(version) || version < 1) {
120
+ console.error(`error: invalid --version "${flags.version}" — use an integer ≥ 1`);
121
+ process.exitCode = 1;
122
+ return;
123
+ }
124
+ }
125
+ const target = typeof flags.target === "string" ? flags.target : undefined;
126
+ const out = typeof flags.out === "string" ? flags.out : undefined;
127
+ await page(slug, template, { registry, dir, out, target, version });
128
+ break;
129
+ }
95
130
  case "advise": {
96
131
  const valueProp = args.join(" ").trim();
97
132
  if (!valueProp) {
package/dist/registry.js CHANGED
@@ -42,6 +42,27 @@ export async function fetchDesignSystem(base, slug, version) {
42
42
  }
43
43
  return (await res.json());
44
44
  }
45
+ /**
46
+ * Fetches a whole page generated from a DS template (`?page=&target=`). The
47
+ * server codegens it from `document.layouts[<template>]`; the CLI just writes it.
48
+ */
49
+ export async function fetchPage(base, slug, template, target, version) {
50
+ const url = new URL(`${base}/api/registry/ds/${encodeURIComponent(slug)}`);
51
+ url.searchParams.set("page", template);
52
+ url.searchParams.set("target", target);
53
+ if (version != null)
54
+ url.searchParams.set("version", String(version));
55
+ const res = await request(url.toString());
56
+ if (res.status === 404) {
57
+ const body = (await res.json().catch(() => ({})));
58
+ throw new RegistryError(body.message ??
59
+ `No template "${template}" in "${slug}". Run \`synthesisui list\` and check the DS templates.`);
60
+ }
61
+ if (!res.ok) {
62
+ throw new RegistryError(`Registry responded ${res.status} while generating "${template}".`);
63
+ }
64
+ return (await res.json());
65
+ }
45
66
  /**
46
67
  * Calls the hosted advisor (`POST /api/ai/advisor`). Gated + metered server-side:
47
68
  * 401 = not logged in, 429 = daily quota reached. Sends the Bearer token if present.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synthesisui",
3
- "version": "0.1.13",
3
+ "version": "0.1.14",
4
4
  "description": "Traz design systems do SynthesisUI para qualquer projeto (materializa em _local/ds/).",
5
5
  "type": "module",
6
6
  "bin": {