@nebutra/design-sync 0.1.1 → 0.1.2

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.
Files changed (39) hide show
  1. package/LICENSE +21 -676
  2. package/dist/cli/index.d.ts.map +1 -1
  3. package/dist/cli/index.js +32 -3
  4. package/dist/detect.d.ts.map +1 -1
  5. package/dist/detect.js +4 -2
  6. package/dist/factory.d.ts.map +1 -1
  7. package/dist/factory.js +5 -0
  8. package/dist/index.d.ts +5 -1
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +7 -1
  11. package/dist/io.js +1 -1
  12. package/dist/providers/design-md.d.ts +49 -0
  13. package/dist/providers/design-md.d.ts.map +1 -0
  14. package/dist/providers/design-md.js +264 -0
  15. package/dist/serialize/from-design-md.d.ts +80 -0
  16. package/dist/serialize/from-design-md.d.ts.map +1 -0
  17. package/dist/serialize/from-design-md.js +1329 -0
  18. package/dist/serialize/to-brand-package.d.ts +37 -0
  19. package/dist/serialize/to-brand-package.d.ts.map +1 -0
  20. package/dist/serialize/to-brand-package.js +87 -0
  21. package/dist/serialize/to-design-md.d.ts +42 -0
  22. package/dist/serialize/to-design-md.d.ts.map +1 -0
  23. package/dist/serialize/to-design-md.js +114 -0
  24. package/dist/serialize/to-design-md.prose.d.ts +55 -0
  25. package/dist/serialize/to-design-md.prose.d.ts.map +1 -0
  26. package/dist/serialize/to-design-md.prose.js +127 -0
  27. package/dist/serialize/to-design-md.resolve.d.ts +36 -0
  28. package/dist/serialize/to-design-md.resolve.d.ts.map +1 -0
  29. package/dist/serialize/to-design-md.resolve.js +248 -0
  30. package/dist/serialize/to-preview-html.d.ts +42 -0
  31. package/dist/serialize/to-preview-html.d.ts.map +1 -0
  32. package/dist/serialize/to-preview-html.js +250 -0
  33. package/dist/serialize/to-preview-html.template.d.ts +75 -0
  34. package/dist/serialize/to-preview-html.template.d.ts.map +1 -0
  35. package/dist/serialize/to-preview-html.template.js +267 -0
  36. package/dist/types.d.ts +22 -6
  37. package/dist/types.d.ts.map +1 -1
  38. package/package.json +21 -6
  39. package/src/cli/index.ts +36 -3
@@ -0,0 +1,267 @@
1
+ /**
2
+ * to-preview-html.template.ts
3
+ *
4
+ * HTML / CSS template layer for the DTCG → preview.html serializer.
5
+ *
6
+ * Exports:
7
+ * - escapeHtml — generic HTML-attribute / content escaping
8
+ * - cssAttrValue — escapes a token value for use inside style="…"
9
+ * - cssVarsDecl — turns a CssVarBlock into CSS custom-property lines
10
+ * - buildDocument — assembles the full <!DOCTYPE html> string
11
+ *
12
+ * Pure: no filesystem I/O, no token resolution.
13
+ */
14
+ // ─── HTML / CSS escaping ───────────────────────────────────────────────────────
15
+ /**
16
+ * Escape a string for safe insertion into HTML content or attribute values.
17
+ * Converts the five characters that have special meaning in HTML.
18
+ */
19
+ export function escapeHtml(value) {
20
+ return value
21
+ .replace(/&/g, "&amp;")
22
+ .replace(/</g, "&lt;")
23
+ .replace(/>/g, "&gt;")
24
+ .replace(/"/g, "&quot;")
25
+ .replace(/'/g, "&#x27;");
26
+ }
27
+ /**
28
+ * Escape a token-derived value for safe use inside a `style="…"` attribute.
29
+ *
30
+ * Strategy:
31
+ * 1. Strip CSS structural characters that could inject extra declarations or
32
+ * break the style attribute — specifically `;`, `{`, `}`, `<`, `>`.
33
+ * NOTE: We deliberately keep `"` in the string before step 2 so that
34
+ * font-family values like `"Geist", sans-serif` are preserved; `escapeHtml`
35
+ * will turn those `"` into `&quot;` which the browser decodes correctly.
36
+ * 2. HTML-escape the result so embedded quotes survive as `&quot;` and any
37
+ * remaining angle brackets become `&lt;`/`&gt;`.
38
+ *
39
+ * Example:
40
+ * cssAttrValue('"Geist", "Noto Sans SC", sans-serif')
41
+ * → '&quot;Geist&quot;, &quot;Noto Sans SC&quot;, sans-serif'
42
+ *
43
+ * cssAttrValue('red;display:none')
44
+ * → 'reddisplay:none' (semicolon stripped before HTML-escape)
45
+ */
46
+ export function cssAttrValue(raw) {
47
+ return escapeHtml(raw.replace(/[;{}<>]/g, ""));
48
+ }
49
+ /**
50
+ * Render a CssVarBlock as indented CSS custom-property declarations (no braces).
51
+ * Values come from the validated toHex path so they are hex strings, but we
52
+ * wrap in cssAttrValue for consistency (harmless for safe hex values).
53
+ */
54
+ export function cssVarsDecl(vars) {
55
+ return [
56
+ ` --color-primary: ${vars.primary};`,
57
+ ` --color-accent: ${vars.accent};`,
58
+ ` --color-tertiary: ${vars.tertiary};`,
59
+ ` --color-danger: ${vars.danger};`,
60
+ ` --color-warning: ${vars.warning};`,
61
+ ` --color-success: ${vars.success};`,
62
+ ` --color-background: ${vars.background};`,
63
+ ` --color-foreground: ${vars.foreground};`,
64
+ ` --preview-bg: ${vars.background};`,
65
+ ` --preview-fg: ${vars.foreground};`,
66
+ ].join("\n");
67
+ }
68
+ /**
69
+ * Assemble the full <!DOCTYPE html> preview document.
70
+ *
71
+ * Dark-mode CSS is emitted as TWO separate, valid rules:
72
+ * 1. `:root[data-theme="dark"]` — explicit toggle
73
+ * 2. `@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) }`
74
+ * — OS/browser preference (but not when the user has explicitly chosen light)
75
+ *
76
+ * This avoids the invalid CSS form of comma-joining a selector with an at-rule.
77
+ */
78
+ export function buildDocument(args) {
79
+ const { escapedName, lightVars, darkVars } = args;
80
+ const darkVarsDecl = cssVarsDecl(darkVars);
81
+ return `<!DOCTYPE html>
82
+ <html lang="en">
83
+ <head>
84
+ <meta charset="UTF-8" />
85
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
86
+ <title>${escapedName} — Design System Preview</title>
87
+ <style>
88
+ /* ── Light mode tokens (default) ────────────────────────── */
89
+ :root {
90
+ ${cssVarsDecl(lightVars)}
91
+ }
92
+
93
+ /* ── Dark mode tokens — explicit toggle ─────────────────── */
94
+ :root[data-theme="dark"] {
95
+ ${darkVarsDecl}
96
+ }
97
+
98
+ /* ── Dark mode tokens — OS/browser preference ───────────── */
99
+ @media (prefers-color-scheme: dark) {
100
+ :root:not([data-theme="light"]) {
101
+ ${darkVarsDecl}
102
+ }
103
+ }
104
+
105
+ /* ── Base reset ─────────────────────────────────────────── */
106
+ *, *::before, *::after { box-sizing: border-box; }
107
+
108
+ body {
109
+ margin: 0;
110
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
111
+ font-size: 16px;
112
+ line-height: 1.6;
113
+ background: var(--color-background);
114
+ color: var(--color-foreground);
115
+ padding: 2rem;
116
+ }
117
+
118
+ /* ── Layout ─────────────────────────────────────────────── */
119
+ .preview-header {
120
+ border-bottom: 1.5px solid rgba(128,128,128,0.18);
121
+ padding-bottom: 1.5rem;
122
+ margin-bottom: 2.5rem;
123
+ }
124
+ .preview-title {
125
+ font-size: 2rem;
126
+ font-weight: 700;
127
+ margin: 0 0 0.25rem;
128
+ background: linear-gradient(135deg, var(--color-primary), var(--color-accent));
129
+ -webkit-background-clip: text;
130
+ -webkit-text-fill-color: transparent;
131
+ background-clip: text;
132
+ }
133
+ .preview-subtitle {
134
+ font-size: 0.875rem;
135
+ opacity: 0.6;
136
+ margin: 0;
137
+ }
138
+
139
+ .preview-section {
140
+ margin-bottom: 3rem;
141
+ }
142
+ .section-title {
143
+ font-size: 1.125rem;
144
+ font-weight: 600;
145
+ margin: 0 0 1rem;
146
+ padding-bottom: 0.5rem;
147
+ border-bottom: 1px solid rgba(128,128,128,0.12);
148
+ }
149
+
150
+ /* ── Swatches ────────────────────────────────────────────── */
151
+ .swatch-grid {
152
+ display: flex;
153
+ flex-wrap: wrap;
154
+ gap: 1rem;
155
+ }
156
+ .swatch {
157
+ width: 120px;
158
+ }
159
+ .swatch-color {
160
+ width: 120px;
161
+ height: 80px;
162
+ border-radius: 8px;
163
+ display: flex;
164
+ align-items: flex-end;
165
+ padding: 0.4rem;
166
+ border: 1px solid rgba(0,0,0,0.06);
167
+ }
168
+ .swatch-hex {
169
+ font-size: 0.65rem;
170
+ font-family: ui-monospace, monospace;
171
+ font-weight: 600;
172
+ letter-spacing: 0.02em;
173
+ }
174
+ .swatch-label {
175
+ margin-top: 0.4rem;
176
+ font-size: 0.75rem;
177
+ font-weight: 500;
178
+ }
179
+
180
+ /* ── Typography ──────────────────────────────────────────── */
181
+ .type-stack {
182
+ display: flex;
183
+ flex-direction: column;
184
+ gap: 1.5rem;
185
+ }
186
+ .type-sample {
187
+ border-left: 3px solid var(--color-primary);
188
+ padding-left: 1rem;
189
+ }
190
+ .type-meta {
191
+ font-size: 0.7rem;
192
+ font-family: ui-monospace, monospace;
193
+ opacity: 0.55;
194
+ margin-bottom: 0.4rem;
195
+ }
196
+ .type-preview {
197
+ line-height: 1.3;
198
+ }
199
+
200
+ /* ── Components ──────────────────────────────────────────── */
201
+ .component-grid {
202
+ display: flex;
203
+ flex-wrap: wrap;
204
+ gap: 2rem;
205
+ align-items: flex-start;
206
+ }
207
+ .component-item {
208
+ display: flex;
209
+ flex-direction: column;
210
+ gap: 0.5rem;
211
+ }
212
+ .component-label {
213
+ font-size: 0.7rem;
214
+ font-family: ui-monospace, monospace;
215
+ opacity: 0.55;
216
+ font-weight: 500;
217
+ text-transform: uppercase;
218
+ letter-spacing: 0.06em;
219
+ }
220
+
221
+ /* ── Radius ──────────────────────────────────────────────── */
222
+ .radius-grid {
223
+ display: flex;
224
+ flex-wrap: wrap;
225
+ gap: 1.5rem;
226
+ align-items: flex-end;
227
+ }
228
+ .radius-item {
229
+ display: flex;
230
+ flex-direction: column;
231
+ align-items: center;
232
+ gap: 0.35rem;
233
+ }
234
+ .radius-demo {
235
+ width: 56px;
236
+ height: 56px;
237
+ background: var(--color-primary);
238
+ opacity: 0.8;
239
+ }
240
+ .radius-name {
241
+ font-size: 0.7rem;
242
+ font-weight: 600;
243
+ }
244
+ .radius-value {
245
+ font-size: 0.65rem;
246
+ font-family: ui-monospace, monospace;
247
+ opacity: 0.55;
248
+ }
249
+ </style>
250
+ </head>
251
+ <body>
252
+
253
+ <header class="preview-header">
254
+ <h1 class="preview-title">${escapedName}</h1>
255
+ <p class="preview-subtitle">Design System Preview &mdash; generated from DTCG token sets</p>
256
+ </header>
257
+
258
+ <main>
259
+ ${args.swatchesHtml}
260
+ ${args.typographyHtml}
261
+ ${args.componentsHtml}
262
+ ${args.radiusHtml}
263
+ </main>
264
+
265
+ </body>
266
+ </html>`;
267
+ }
package/dist/types.d.ts CHANGED
@@ -2,12 +2,13 @@ import { z } from "zod";
2
2
  /**
3
3
  * Supported design-sync backend providers.
4
4
  *
5
- * - `figma` — Figma + Tokens Studio plugin (DTCG via GitHub provider sync)
6
- * - `penpot` — Penpot REST API (self-hostable, China-friendly)
7
- * - `git-only` — No design tool; reads/writes local DTCG files directly
8
- * - `memory` — In-memory test fixture (CI / unit tests only)
5
+ * - `figma` — Figma + Tokens Studio plugin (DTCG via GitHub provider sync)
6
+ * - `penpot` — Penpot REST API (self-hostable, China-friendly)
7
+ * - `git-only` — No design tool; reads/writes local DTCG files directly
8
+ * - `memory` — In-memory test fixture (CI / unit tests only)
9
+ * - `design-md` — AI-native DESIGN.md format (@google/design.md) — markdown + YAML front matter
9
10
  */
10
- export type DesignSyncProviderType = "figma" | "penpot" | "git-only" | "memory";
11
+ export type DesignSyncProviderType = "figma" | "penpot" | "git-only" | "memory" | "design-md";
11
12
  /**
12
13
  * A W3C Design Tokens Community Group (DTCG) leaf token.
13
14
  * Every leaf MUST carry both `$value` and `$type`.
@@ -171,5 +172,20 @@ export interface MemoryProviderConfig extends BaseProviderConfig {
171
172
  /** Optional initial in-memory token sets (used in tests). */
172
173
  initialSets?: DesignTokenSet[];
173
174
  }
174
- export type DesignSyncConfig = FigmaProviderConfig | PenpotProviderConfig | GitOnlyProviderConfig | MemoryProviderConfig;
175
+ export interface DesignMdProviderConfig extends BaseProviderConfig {
176
+ provider: "design-md";
177
+ /** Absolute path to the DESIGN.md file. Defaults to `process.cwd()/DESIGN.md`. */
178
+ designMdPath?: string;
179
+ /**
180
+ * Design-system name emitted into the generated DESIGN.md front matter +
181
+ * Overview. Defaults to Nebutra branding.
182
+ */
183
+ name?: string;
184
+ /**
185
+ * Design-system description emitted into the generated DESIGN.md front matter +
186
+ * Overview. Defaults to Nebutra branding.
187
+ */
188
+ description?: string;
189
+ }
190
+ export type DesignSyncConfig = FigmaProviderConfig | PenpotProviderConfig | GitOnlyProviderConfig | MemoryProviderConfig | DesignMdProviderConfig;
175
191
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nebutra/design-sync",
3
- "version": "0.1.1",
4
- "description": "Provider-agnostic design-tool sync. Figma + Tokens Studio | Penpot | git-only | memory.",
3
+ "version": "0.1.2",
4
+ "description": "Provider-agnostic design-tool sync. Figma + Tokens Studio | Penpot | git-only | design-md | memory.",
5
5
  "private": false,
6
6
  "license": "MIT",
7
7
  "type": "module",
@@ -16,11 +16,12 @@
16
16
  "gaps": [
17
17
  "Figma push (Variables REST API) is dry-run; manual enable required",
18
18
  "Penpot adapter implements healthcheck + pull; push is dry-run scaffolding",
19
- "No real-time webhook listeners (manual pull/push only)"
19
+ "No real-time webhook listeners (manual pull/push only)",
20
+ "design-md provider depends on @google/design.md (alpha v0.x); pinned exactly — a 0.x bump may change linter rules and require re-validating the serializer output"
20
21
  ],
21
22
  "featureId": "design-sync",
22
23
  "category": "design",
23
- "summary": "Provider-agnostic design-tool sync (Figma | Penpot | git-only)"
24
+ "summary": "Provider-agnostic design-tool sync (Figma | Penpot | git-only | design-md)"
24
25
  },
25
26
  "main": "./dist/index.js",
26
27
  "types": "./dist/index.d.ts",
@@ -29,6 +30,14 @@
29
30
  "types": "./dist/index.d.ts",
30
31
  "import": "./dist/index.js"
31
32
  },
33
+ "./design-md": {
34
+ "types": "./dist/providers/design-md.d.ts",
35
+ "import": "./dist/providers/design-md.js"
36
+ },
37
+ "./serialize/from-design-md": {
38
+ "types": "./dist/serialize/from-design-md.d.ts",
39
+ "import": "./dist/serialize/from-design-md.js"
40
+ },
32
41
  "./figma": {
33
42
  "types": "./dist/providers/figma.d.ts",
34
43
  "import": "./dist/providers/figma.js"
@@ -48,6 +57,10 @@
48
57
  "./cli": {
49
58
  "types": "./dist/cli/index.d.ts",
50
59
  "import": "./dist/cli/index.js"
60
+ },
61
+ "./to-brand-package": {
62
+ "types": "./dist/serialize/to-brand-package.d.ts",
63
+ "import": "./dist/serialize/to-brand-package.js"
51
64
  }
52
65
  },
53
66
  "bin": {
@@ -58,11 +71,13 @@
58
71
  "README.md"
59
72
  ],
60
73
  "dependencies": {
74
+ "@google/design.md": "0.2.0",
61
75
  "zod": "^4.3.6",
62
- "@nebutra/logger": "0.1.1"
76
+ "@nebutra/logger": "0.1.2",
77
+ "@nebutra/tokens": "0.1.2"
63
78
  },
64
79
  "devDependencies": {
65
- "@types/node": "^22.19.15",
80
+ "@types/node": "^25.9.1",
66
81
  "tsx": "^4.21.0",
67
82
  "typescript": "^5.9.3",
68
83
  "vitest": "^4.1.4"
package/src/cli/index.ts CHANGED
@@ -17,6 +17,7 @@
17
17
 
18
18
  import { describeEnv } from "../detect";
19
19
  import { createDesignSync } from "../factory";
20
+ import { compileBrandFromTokenSets } from "../serialize/to-brand-package";
20
21
  import type { DesignSyncProviderType } from "../types";
21
22
 
22
23
  interface ParsedArgs {
@@ -25,6 +26,8 @@ interface ParsedArgs {
25
26
  json: boolean;
26
27
  themes: string[];
27
28
  provider: DesignSyncProviderType | undefined;
29
+ brandId: string | undefined;
30
+ brandName: string | undefined;
28
31
  }
29
32
 
30
33
  function parseArgs(argv: string[]): ParsedArgs {
@@ -33,6 +36,8 @@ function parseArgs(argv: string[]): ParsedArgs {
33
36
  let json = false;
34
37
  const themes: string[] = [];
35
38
  let provider: DesignSyncProviderType | undefined;
39
+ let brandId: string | undefined;
40
+ let brandName: string | undefined;
36
41
 
37
42
  for (let i = 0; i < rest.length; i++) {
38
43
  const arg = rest[i];
@@ -53,10 +58,14 @@ function parseArgs(argv: string[]): ParsedArgs {
53
58
  provider = next.trim() as DesignSyncProviderType;
54
59
  i++;
55
60
  }
61
+ } else if (arg === "--id") {
62
+ brandId = rest[++i];
63
+ } else if (arg === "--name") {
64
+ brandName = rest[++i];
56
65
  }
57
66
  }
58
67
 
59
- return { command, dryRun, json, themes, provider };
68
+ return { command, dryRun, json, themes, provider, brandId, brandName };
60
69
  }
61
70
 
62
71
  function emit(payload: unknown, json: boolean): void {
@@ -88,12 +97,15 @@ Commands:
88
97
  detect Print the resolved provider + env diagnostics
89
98
  healthcheck Run the provider's healthcheck()
90
99
  pull Pull design-tool → repo (DTCG JSON)
91
- push Push repo → design-tool (defaults to dry-run on figma/penpot)
100
+ push Push repo → design-tool (defaults to dry-run on figma/penpot/design-md)
101
+ brand Pull tokens then compile a Brand Package (Create Center)
92
102
  help Show this message
93
103
 
94
104
  Options:
95
- --provider <X> Override provider (figma|penpot|git-only|memory)
105
+ --provider <X> Override provider (figma|penpot|git-only|memory|design-md)
96
106
  --themes a,b Restrict to specific token sets
107
+ --id <id> Brand package id (for brand command)
108
+ --name <name> Brand package display name
97
109
  --dry-run Force dry-run on push
98
110
  --json Emit machine-readable JSON
99
111
  `;
@@ -149,6 +161,27 @@ export async function run(argv: string[] = process.argv): Promise<number> {
149
161
  return 0;
150
162
  }
151
163
 
164
+ if (args.command === "brand") {
165
+ const themes = args.themes.length > 0 ? { themes: args.themes } : {};
166
+ const pull = await provider.pull(themes);
167
+ const compiled = compileBrandFromTokenSets(pull.sets, {
168
+ ...(args.brandId ? { id: args.brandId } : {}),
169
+ ...(args.brandName ? { name: args.brandName } : {}),
170
+ });
171
+ emit(
172
+ args.json
173
+ ? {
174
+ provider: pull.provider,
175
+ brand: compiled.brand,
176
+ css: compiled.css,
177
+ warnings: compiled.warnings,
178
+ }
179
+ : `brand=${compiled.brand.id} recipe=${compiled.brand.recipe.buttonDefault} fonts=${compiled.brand.typography.faces?.length ?? 0} warnings=${compiled.warnings.length}`,
180
+ args.json,
181
+ );
182
+ return 0;
183
+ }
184
+
152
185
  emitError(`unknown command: ${args.command}\n\n${HELP_TEXT}`, args.json);
153
186
  return 2;
154
187
  } catch (error) {