@nebutra/design-sync 0.1.1 → 2.0.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.
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 +48 -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 +143 -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 +48 -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 +22 -6
  39. package/src/cli/index.ts +36 -3
@@ -0,0 +1,37 @@
1
+ /**
2
+ * DTCG token sets (design-sync pull result) → Brand Package.
3
+ *
4
+ * Create Center path:
5
+ * getDesignSync().pull() → compileBrandFromTokenSets() → applyBrandPackage()
6
+ */
7
+ import { type BrandPackage, type CompileResult } from "@nebutra/tokens/brand-package";
8
+ import type { DesignTokenSet, DesignTokenTree } from "../types";
9
+ export interface ToBrandPackageOptions {
10
+ id?: string;
11
+ name?: string;
12
+ /** Raw DESIGN.md text for recipe inference */
13
+ designMd?: string;
14
+ }
15
+ /** Deep-merge DTCG trees (later sets win on leaf collision). */
16
+ export declare function mergeTokenTrees(trees: DesignTokenTree[]): DesignTokenTree;
17
+ /**
18
+ * Normalize design-sync token sets into the flat Refero-like shape
19
+ * expected by compileReferoTokens (color/font/radius/surface at top level).
20
+ */
21
+ export declare function tokenSetsToReferoShape(sets: DesignTokenSet[]): Record<string, unknown>;
22
+ /** Compile Brand Package + CSS from design-sync pull token sets. */
23
+ export declare function compileBrandFromTokenSets(sets: DesignTokenSet[], options?: ToBrandPackageOptions): CompileResult;
24
+ /** Convenience: only CSS string */
25
+ export declare function serializeToBrandCss(sets: DesignTokenSet[], options?: ToBrandPackageOptions): {
26
+ brand: BrandPackage;
27
+ css: string;
28
+ warnings: string[];
29
+ };
30
+ /**
31
+ * Full Create Center pipeline step after pull:
32
+ * const { brand, css, warnings } = await pullAndCompileBrand()
33
+ */
34
+ export declare function pullAndCompileBrand(pull: () => Promise<{
35
+ sets: DesignTokenSet[];
36
+ }>, options?: ToBrandPackageOptions): Promise<CompileResult>;
37
+ //# sourceMappingURL=to-brand-package.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"to-brand-package.d.ts","sourceRoot":"","sources":["../../src/serialize/to-brand-package.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,aAAa,EAGnB,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEhE,MAAM,WAAW,qBAAqB;IACpC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAMD,gEAAgE;AAChE,wBAAgB,eAAe,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,eAAe,CAMzE;AA8BD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,cAAc,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAQtF;AAED,oEAAoE;AACpE,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,cAAc,EAAE,EACtB,OAAO,GAAE,qBAA0B,GAClC,aAAa,CAQf;AAED,mCAAmC;AACnC,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,cAAc,EAAE,EACtB,OAAO,GAAE,qBAA0B,GAClC;IAAE,KAAK,EAAE,YAAY,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,CAO1D;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,MAAM,OAAO,CAAC;IAAE,IAAI,EAAE,cAAc,EAAE,CAAA;CAAE,CAAC,EAC/C,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,aAAa,CAAC,CAGxB"}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * DTCG token sets (design-sync pull result) → Brand Package.
3
+ *
4
+ * Create Center path:
5
+ * getDesignSync().pull() → compileBrandFromTokenSets() → applyBrandPackage()
6
+ */
7
+ import { compileReferoTokens, emitBrandCss, } from "@nebutra/tokens/brand-package";
8
+ function isLeaf(node) {
9
+ return Boolean(node && typeof node === "object" && "$value" in node);
10
+ }
11
+ /** Deep-merge DTCG trees (later sets win on leaf collision). */
12
+ export function mergeTokenTrees(trees) {
13
+ const out = {};
14
+ for (const tree of trees) {
15
+ mergeInto(out, tree);
16
+ }
17
+ return out;
18
+ }
19
+ function mergeInto(target, source) {
20
+ for (const [key, value] of Object.entries(source)) {
21
+ if (key === "__proto__" || key === "constructor" || key === "prototype")
22
+ continue;
23
+ if (isLeaf(value)) {
24
+ Object.defineProperty(target, key, {
25
+ value,
26
+ writable: true,
27
+ enumerable: true,
28
+ configurable: true,
29
+ });
30
+ continue;
31
+ }
32
+ if (value && typeof value === "object") {
33
+ const existing = target[key];
34
+ if (existing && typeof existing === "object" && !isLeaf(existing)) {
35
+ mergeInto(existing, value);
36
+ }
37
+ else {
38
+ Object.defineProperty(target, key, {
39
+ value: structuredClone(value),
40
+ writable: true,
41
+ enumerable: true,
42
+ configurable: true,
43
+ });
44
+ }
45
+ }
46
+ }
47
+ }
48
+ /**
49
+ * Normalize design-sync token sets into the flat Refero-like shape
50
+ * expected by compileReferoTokens (color/font/radius/surface at top level).
51
+ */
52
+ export function tokenSetsToReferoShape(sets) {
53
+ const merged = mergeTokenTrees(sets.map((s) => s.tokens));
54
+ // If pull already looks like Refero (has color.*), use as-is
55
+ if (merged.color || merged.surface || merged.font) {
56
+ return merged;
57
+ }
58
+ // design-tokens monorepo shape often nests under themes — still pass through
59
+ return merged;
60
+ }
61
+ /** Compile Brand Package + CSS from design-sync pull token sets. */
62
+ export function compileBrandFromTokenSets(sets, options = {}) {
63
+ const tokens = tokenSetsToReferoShape(sets);
64
+ return compileReferoTokens({
65
+ tokens,
66
+ ...(options.id ? { id: options.id } : {}),
67
+ ...(options.name ? { name: options.name } : {}),
68
+ ...(options.designMd ? { designMd: options.designMd } : {}),
69
+ });
70
+ }
71
+ /** Convenience: only CSS string */
72
+ export function serializeToBrandCss(sets, options = {}) {
73
+ const result = compileBrandFromTokenSets(sets, options);
74
+ return {
75
+ brand: result.brand,
76
+ css: result.css || emitBrandCss(result.brand),
77
+ warnings: result.warnings,
78
+ };
79
+ }
80
+ /**
81
+ * Full Create Center pipeline step after pull:
82
+ * const { brand, css, warnings } = await pullAndCompileBrand()
83
+ */
84
+ export async function pullAndCompileBrand(pull, options = {}) {
85
+ const { sets } = await pull();
86
+ return compileBrandFromTokenSets(sets, options);
87
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * DTCG → DESIGN.md serializer
3
+ *
4
+ * Converts a set of W3C DTCG token files into a DESIGN.md document:
5
+ * - YAML front matter (machine tokens): colors, typography, rounded, spacing
6
+ * - Markdown prose sections in the spec-mandated order
7
+ *
8
+ * Pure function — no filesystem I/O, no @google/design.md dependency.
9
+ *
10
+ * Known v1 scope limits:
11
+ * - No `components:` group in front matter (avoids contrast-lint risk from
12
+ * lack of structured component token data in this version).
13
+ * - Elevation/shadows are prose-only (DESIGN.md spec has no structured
14
+ * elevation token type; this is a documented spec gap).
15
+ *
16
+ * File layout:
17
+ * to-design-md.ts — public API + YAML front-matter builder (this file)
18
+ * to-design-md.resolve.ts — token index building + alias/hex resolution
19
+ * to-design-md.prose.ts — markdown prose builder + shared types + constants
20
+ */
21
+ import type { DesignTokenSet } from "../types";
22
+ export interface ToDesignMdOptions {
23
+ /**
24
+ * Design system name written into the exported artefact.
25
+ *
26
+ * Defaults to a generic label on purpose: this package is provider-agnostic
27
+ * and has no brand of its own, so naming one here would stamp it onto every
28
+ * downstream design system that did not pass this option.
29
+ */
30
+ name?: string;
31
+ /** One-line brand description for the front matter and prose Overview. */
32
+ description?: string;
33
+ /**
34
+ * Name of a theme token set (e.g. "themes/light") whose
35
+ * `color.background` and `color.foreground` tokens are included in the
36
+ * `colors` front-matter group.
37
+ */
38
+ theme?: string;
39
+ }
40
+ /**
41
+ * Serialize an array of DTCG token sets into a DESIGN.md string.
42
+ *
43
+ * @param sets All token sets to merge (e.g. core + semantic + theme).
44
+ * @param options Optional overrides for name, description, and theme.
45
+ * @returns A deterministic DESIGN.md string (YAML front matter + markdown prose).
46
+ */
47
+ export declare function serializeToDesignMd(sets: DesignTokenSet[], options?: ToDesignMdOptions): string;
48
+ //# sourceMappingURL=to-design-md.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"to-design-md.d.ts","sourceRoot":"","sources":["../../src/serialize/to-design-md.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAqB/C,MAAM,WAAW,iBAAiB;IAChC;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0EAA0E;IAC1E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,cAAc,EAAE,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,MAAM,CA2C/F"}
@@ -0,0 +1,114 @@
1
+ /**
2
+ * DTCG → DESIGN.md serializer
3
+ *
4
+ * Converts a set of W3C DTCG token files into a DESIGN.md document:
5
+ * - YAML front matter (machine tokens): colors, typography, rounded, spacing
6
+ * - Markdown prose sections in the spec-mandated order
7
+ *
8
+ * Pure function — no filesystem I/O, no @google/design.md dependency.
9
+ *
10
+ * Known v1 scope limits:
11
+ * - No `components:` group in front matter (avoids contrast-lint risk from
12
+ * lack of structured component token data in this version).
13
+ * - Elevation/shadows are prose-only (DESIGN.md spec has no structured
14
+ * elevation token type; this is a documented spec gap).
15
+ *
16
+ * File layout:
17
+ * to-design-md.ts — public API + YAML front-matter builder (this file)
18
+ * to-design-md.resolve.ts — token index building + alias/hex resolution
19
+ * to-design-md.prose.ts — markdown prose builder + shared types + constants
20
+ */
21
+ import { buildProse, COLOR_ORDER, } from "./to-design-md.prose";
22
+ import { buildIndex, buildIndexForSet, buildRounded, buildSpacing, buildTypography, readContainers, resolveColorRoles, } from "./to-design-md.resolve";
23
+ /**
24
+ * Serialize an array of DTCG token sets into a DESIGN.md string.
25
+ *
26
+ * @param sets All token sets to merge (e.g. core + semantic + theme).
27
+ * @param options Optional overrides for name, description, and theme.
28
+ * @returns A deterministic DESIGN.md string (YAML front matter + markdown prose).
29
+ */
30
+ export function serializeToDesignMd(sets, options) {
31
+ const name = options?.name ?? "Design System";
32
+ const description = options?.description;
33
+ // 1. Build a flat path→leaf index across ALL sets
34
+ const index = buildIndex(sets);
35
+ // 2. Build a flat path→leaf index for only the theme set (if requested)
36
+ const themeIndex = options?.theme != null
37
+ ? buildIndexForSet(sets.find((s) => s.name === options.theme)?.tokens ?? {})
38
+ : new Map();
39
+ // 3. Resolve roles to hex (skip if source absent; throw if alias is dangling)
40
+ const { roles: colors, descriptions: colorDescriptions } = resolveColorRoles(index, themeIndex);
41
+ const typography = buildTypography(index);
42
+ const rounded = buildRounded(index);
43
+ const spacing = buildSpacing(index);
44
+ const containers = readContainers(index);
45
+ // 4. Emit
46
+ const fmDescription = description ??
47
+ "AI-native SaaS design system. Brand: 云毓蓝 blue (#0033fe) → 云毓青 cyan (#0bf1c3).";
48
+ const frontMatter = buildFrontMatter({
49
+ name,
50
+ description: fmDescription,
51
+ colors,
52
+ typography,
53
+ rounded,
54
+ spacing,
55
+ });
56
+ const prose = buildProse({
57
+ name,
58
+ ...(description !== undefined ? { description } : {}),
59
+ colors,
60
+ colorDescriptions,
61
+ typography,
62
+ rounded,
63
+ containers,
64
+ });
65
+ return `---\n${frontMatter}---\n${prose}`;
66
+ }
67
+ /**
68
+ * Escape a string value for use in YAML double-quoted scalars.
69
+ * Backslash must be escaped first (to avoid double-escaping), then double-quotes.
70
+ */
71
+ function yamlString(value) {
72
+ return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
73
+ }
74
+ function buildFrontMatter(args) {
75
+ const lines = [];
76
+ lines.push(`version: alpha`);
77
+ lines.push(`name: ${yamlString(args.name)}`);
78
+ lines.push(`description: ${yamlString(args.description)}`);
79
+ // colors:
80
+ const colorEntries = Object.entries(args.colors);
81
+ if (colorEntries.length > 0) {
82
+ lines.push(`colors:`);
83
+ const ordered = COLOR_ORDER.filter((k) => args.colors[k] != null);
84
+ for (const key of ordered) {
85
+ lines.push(` ${key}: ${yamlString(args.colors[key])}`);
86
+ }
87
+ }
88
+ // typography:
89
+ lines.push(`typography:`);
90
+ for (const [variant, entry] of Object.entries(args.typography)) {
91
+ lines.push(` ${variant}:`);
92
+ lines.push(` fontFamily: ${yamlString(entry.fontFamily)}`);
93
+ lines.push(` fontSize: ${yamlString(entry.fontSize)}`);
94
+ }
95
+ // rounded:
96
+ const roundedEntries = Object.entries(args.rounded);
97
+ if (roundedEntries.length > 0) {
98
+ lines.push(`rounded:`);
99
+ for (const [key, value] of roundedEntries) {
100
+ lines.push(` ${key}: ${yamlString(value)}`);
101
+ }
102
+ }
103
+ // spacing: (optional)
104
+ if (args.spacing != null) {
105
+ const spacingEntries = Object.entries(args.spacing);
106
+ if (spacingEntries.length > 0) {
107
+ lines.push(`spacing:`);
108
+ for (const [key, value] of spacingEntries) {
109
+ lines.push(` ${key}: ${yamlString(value)}`);
110
+ }
111
+ }
112
+ }
113
+ return lines.join("\n") + "\n";
114
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * DTCG → DESIGN.md prose layer
3
+ *
4
+ * Contains the markdown prose builder (`buildProse`) and all section helpers.
5
+ * Imported by `to-design-md.ts`; do NOT import this file directly in app code.
6
+ */
7
+ export interface ColorRoles {
8
+ primary?: string;
9
+ accent?: string;
10
+ tertiary?: string;
11
+ danger?: string;
12
+ warning?: string;
13
+ success?: string;
14
+ background?: string;
15
+ foreground?: string;
16
+ }
17
+ /** Per-role descriptions sourced from the source semantic token's $description field. */
18
+ export type ColorDescriptions = Partial<Record<keyof ColorRoles, string>>;
19
+ export interface TypographyEntry {
20
+ fontFamily: string;
21
+ fontSize: string;
22
+ }
23
+ export interface TypographyMap {
24
+ h1: TypographyEntry;
25
+ "body-md": TypographyEntry;
26
+ label: TypographyEntry;
27
+ }
28
+ export interface RoundedMap {
29
+ [key: string]: string;
30
+ }
31
+ export interface SpacingMap {
32
+ [key: string]: string;
33
+ }
34
+ export interface ContainerInfo {
35
+ text?: string;
36
+ content?: string;
37
+ wide?: string;
38
+ }
39
+ export interface ProseArgs {
40
+ name: string;
41
+ /** Optional custom description. Replaces the generated Overview paragraph. */
42
+ description?: string;
43
+ colors: ColorRoles;
44
+ colorDescriptions: ColorDescriptions;
45
+ typography: TypographyMap;
46
+ rounded: RoundedMap;
47
+ containers: ContainerInfo;
48
+ }
49
+ export declare const COLOR_ORDER: Array<keyof ColorRoles>;
50
+ export declare const DESIGN_MD_GOVERNANCE: {
51
+ readonly dos: readonly ["Use semantic tokens / CSS variables (`bg-primary`, `text-foreground`, `border-border`) — never raw hex values.", "Use the brand gradient (135° blue→cyan) for primary CTAs and gradient text effects.", "Use `AnimateIn` presets for entrance animations; never use raw `motion.div` with hardcoded transition values.", "Constrain layouts to the container width tiers; use the wide (1400px) container for feature sections.", "Give icon-only buttons an `aria-label`; rely on the global `:focus-visible` ring — do not add component-level focus rings."];
52
+ readonly donts: readonly ["Don't hardcode brand or status hex values in components — use token aliases (`var(--brand-primary)`, etc.).", "Don't use `max-w-5xl` or `max-w-7xl` for feature sections — use the wide container (`max-w-wide`).", "Don't reintroduce hardcoded focus rings (they double-render with the global `:focus-visible` rule).", "Don't use raw form controls (`<input>`, `<select>`, `<textarea>`) in app surfaces — use the primitives from `@nebutra/ui/primitives`."];
53
+ };
54
+ export declare function buildProse(args: ProseArgs): string;
55
+ //# sourceMappingURL=to-design-md.prose.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"to-design-md.prose.d.ts","sourceRoot":"","sources":["../../src/serialize/to-design-md.prose.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,MAAM,WAAW,UAAU;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,yFAAyF;AACzF,MAAM,MAAM,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;AAE1E,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,eAAe,CAAC;IACpB,SAAS,EAAE,eAAe,CAAC;IAC3B,KAAK,EAAE,eAAe,CAAC;CACxB;AAED,MAAM,WAAW,UAAU;IACzB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,UAAU;IACzB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,UAAU,CAAC;IACnB,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,UAAU,EAAE,aAAa,CAAC;IAC1B,OAAO,EAAE,UAAU,CAAC;IACpB,UAAU,EAAE,aAAa,CAAC;CAC3B;AAID,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,MAAM,UAAU,CAS/C,CAAC;AAIF,eAAO,MAAM,oBAAoB;;;CAcvB,CAAC;AAIX,wBAAgB,UAAU,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAalD"}
@@ -0,0 +1,143 @@
1
+ /**
2
+ * DTCG → DESIGN.md prose layer
3
+ *
4
+ * Contains the markdown prose builder (`buildProse`) and all section helpers.
5
+ * Imported by `to-design-md.ts`; do NOT import this file directly in app code.
6
+ */
7
+ // ─── Stable color key order (used by both front-matter and prose) ─────────────
8
+ export const COLOR_ORDER = [
9
+ "primary",
10
+ "accent",
11
+ "tertiary",
12
+ "danger",
13
+ "warning",
14
+ "success",
15
+ "background",
16
+ "foreground",
17
+ ];
18
+ // ─── Governance list (exported for consumers who want to inspect the rules) ────
19
+ export const DESIGN_MD_GOVERNANCE = {
20
+ dos: [
21
+ "Use semantic tokens / CSS variables (`bg-primary`, `text-foreground`, `border-border`) — never raw hex values.",
22
+ "Use the brand gradient (135° blue→cyan) for primary CTAs and gradient text effects.",
23
+ "Use `AnimateIn` presets for entrance animations; never use raw `motion.div` with hardcoded transition values.",
24
+ "Constrain layouts to the container width tiers; use the wide (1400px) container for feature sections.",
25
+ "Give icon-only buttons an `aria-label`; rely on the global `:focus-visible` ring — do not add component-level focus rings.",
26
+ ],
27
+ donts: [
28
+ "Don't hardcode brand or status hex values in components — use token aliases (`var(--brand-primary)`, etc.).",
29
+ "Don't use `max-w-5xl` or `max-w-7xl` for feature sections — use the wide container (`max-w-wide`).",
30
+ "Don't reintroduce hardcoded focus rings (they double-render with the global `:focus-visible` rule).",
31
+ "Don't use raw form controls (`<input>`, `<select>`, `<textarea>`) in app surfaces — use the primitives from `@nebutra/ui/primitives`.",
32
+ ],
33
+ };
34
+ // ─── Prose builder ────────────────────────────────────────────────────────────
35
+ export function buildProse(args) {
36
+ const sections = [];
37
+ sections.push(buildOverview(args.name, args.colors, args.description));
38
+ sections.push(buildColors(args.colors, args.colorDescriptions));
39
+ sections.push(buildTypographySection(args.typography));
40
+ sections.push(buildLayout(args.containers));
41
+ sections.push(buildElevation());
42
+ sections.push(buildShapes(args.rounded));
43
+ sections.push(buildComponents());
44
+ sections.push(buildDosDonts());
45
+ return "\n" + sections.join("\n\n") + "\n";
46
+ }
47
+ // ─── Section helpers ──────────────────────────────────────────────────────────
48
+ /**
49
+ * The fallback paragraph is generated from the caller's own tokens.
50
+ *
51
+ * It used to be Nebutra's: it named 云毓蓝 #0033fe and 云毓青 #0bf1c3 as "the
52
+ * brand palette" and asserted Vercel/Geist parity, so every downstream design
53
+ * system exported a document describing someone else's colours as its own.
54
+ */
55
+ function buildOverview(name, colors, description) {
56
+ if (description)
57
+ return `## Overview\n\n${description}`;
58
+ const palette = [
59
+ colors.primary ? `primary ${colors.primary}` : null,
60
+ colors.accent ? `accent ${colors.accent}` : null,
61
+ ]
62
+ .filter(Boolean)
63
+ .join(" → ");
64
+ const paragraph = `${name} is a token-first design system` +
65
+ (palette ? ` built on a ${palette} palette` : "") +
66
+ ". It favours semantic tokens over raw hex values and follows the DTCG " +
67
+ "(W3C draft) format, which drives CSS variables, Tailwind utilities and " +
68
+ "the Style Dictionary pipeline.";
69
+ return `## Overview\n\n${paragraph}`;
70
+ }
71
+ const COLOR_LABELS = {
72
+ primary: "Primary",
73
+ accent: "Accent",
74
+ tertiary: "Tertiary",
75
+ danger: "Danger",
76
+ warning: "Warning",
77
+ success: "Success",
78
+ background: "Background",
79
+ foreground: "Foreground",
80
+ };
81
+ function buildColors(colors, colorDescriptions) {
82
+ const colorLines = [];
83
+ for (const key of COLOR_ORDER) {
84
+ const hex = colors[key];
85
+ if (!hex)
86
+ continue;
87
+ const label = COLOR_LABELS[key];
88
+ const desc = colorDescriptions[key];
89
+ colorLines.push(`- **${label}** (\`${hex}\`)${desc ? ` — ${desc}` : ""}`);
90
+ }
91
+ return (`## Colors\n\n` + (colorLines.length > 0 ? colorLines.join("\n") : "_No color roles resolved._"));
92
+ }
93
+ function buildTypographySection(typography) {
94
+ const typLines = [];
95
+ for (const [variant, entry] of Object.entries(typography)) {
96
+ typLines.push(`- **${variant}**: \`${entry.fontSize}\` — ${entry.fontFamily}`);
97
+ }
98
+ return `## Typography\n\n` + typLines.join("\n");
99
+ }
100
+ function buildLayout(containers) {
101
+ const containerLines = [];
102
+ if (containers.text) {
103
+ containerLines.push(`- **text** (\`${containers.text}\`) — Reading-focused: hero copy, FAQ, article body`);
104
+ }
105
+ if (containers.content) {
106
+ containerLines.push(`- **content** (\`${containers.content}\`) — Pricing tables, blog index, architecture diagrams`);
107
+ }
108
+ if (containers.wide) {
109
+ containerLines.push(`- **wide** (\`${containers.wide}\`) — Feature bento grids, testimonials, product demo sections, navbar`);
110
+ }
111
+ const layoutBody = containerLines.length > 0
112
+ ? `Three container-width tiers constrain layout density:\n\n` + containerLines.join("\n")
113
+ : `Use the \`--container-text\`, \`--container-content\`, and \`--container-wide\` CSS variables for layout constraints.`;
114
+ return `## Layout\n\n` + layoutBody;
115
+ }
116
+ function buildElevation() {
117
+ return (`## Elevation & Depth\n\n` +
118
+ `Elevation is expressed as layered box-shadows (xs → 2xl) plus brand-tinted glow variants (\`brand\`, \`brand-lg\`). ` +
119
+ `No structured elevation token is emitted in this DESIGN.md because the current DESIGN.md specification ` +
120
+ `has no elevation token type (this is a documented spec gap). ` +
121
+ `Consume shadows via the \`elevation.*\` token set in the DTCG source files or the ` +
122
+ `Tailwind \`shadow-*\` utilities mapped from those tokens.`);
123
+ }
124
+ function buildShapes(rounded) {
125
+ const roundedKeys = Object.keys(rounded);
126
+ const shapeBody = roundedKeys.length > 0
127
+ ? `Border-radius follows a named scale: ${roundedKeys.map((k) => `**${k}** (\`${rounded[k]}\`)`).join(", ")}. ` +
128
+ `Use \`size.radius.*\` tokens — never hardcode \`border-radius\` values.`
129
+ : `Border-radius follows the \`size.radius.*\` token scale. Use named tokens, never hardcode values.`;
130
+ return `## Shapes\n\n` + shapeBody;
131
+ }
132
+ function buildComponents() {
133
+ return (`## Components\n\n` +
134
+ `No structured component tokens are emitted in DESIGN.md v1 (avoids contrast-lint risk without full component token coverage). ` +
135
+ `Component rules live in the Storybook \`@nebutra/ui\` library and the \`@nebutra/tokens\` CSS variable sheet. ` +
136
+ `All interactive components must use \`@nebutra/ui/primitives\` form controls — raw \`<input>\`/\`<select>\`/\`<textarea>\` ` +
137
+ `are banned in \`apps/**\` (lint-enforced via \`scripts/lint-no-raw-inputs.mjs\`).`);
138
+ }
139
+ function buildDosDonts() {
140
+ const doSection = DESIGN_MD_GOVERNANCE.dos.map((d) => `- ✓ ${d}`).join("\n");
141
+ const dontSection = DESIGN_MD_GOVERNANCE.donts.map((d) => `- ✗ ${d}`).join("\n");
142
+ return `## Do's and Don'ts\n\n### Do\n\n${doSection}\n\n### Don't\n\n${dontSection}`;
143
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * DTCG → DESIGN.md resolver layer
3
+ *
4
+ * Builds flat token indexes from token sets, resolves aliases, extracts
5
+ * typed values (colors, typography, rounded, spacing, containers).
6
+ *
7
+ * Imported by `to-design-md.ts`; do NOT import this file directly in app code.
8
+ */
9
+ import type { DesignTokenLeaf, DesignTokenSet, DesignTokenTree } from "../types";
10
+ import type { ColorDescriptions, ColorRoles, ContainerInfo, RoundedMap, SpacingMap, TypographyMap } from "./to-design-md.prose";
11
+ export type { ResolvedColors };
12
+ export { buildIndex, buildIndexForSet, buildRounded, buildSpacing, buildTypography, readContainers, resolveColorRoles, };
13
+ type FlatIndex = Map<string, DesignTokenLeaf>;
14
+ interface ResolvedColors {
15
+ roles: ColorRoles;
16
+ descriptions: ColorDescriptions;
17
+ }
18
+ declare function buildIndex(sets: DesignTokenSet[]): FlatIndex;
19
+ declare function buildIndexForSet(tree: DesignTokenTree): FlatIndex;
20
+ /**
21
+ * Resolve a token value to its literal (non-alias) string.
22
+ * Throws if the alias is dangling (referenced path not in index).
23
+ * Returns null only if the path itself is missing from the index (graceful skip).
24
+ *
25
+ * @param path The current token path being resolved.
26
+ * @param index The flat token index to look up.
27
+ * @param visited Set of paths already visited (cycle detection).
28
+ * @param originPath The originating token path (for cycle error messages).
29
+ */
30
+ export declare function resolveValue(path: string, index: FlatIndex, visited?: Set<string>, originPath?: string): string | null;
31
+ declare function resolveColorRoles(index: FlatIndex, themeIndex: FlatIndex): ResolvedColors;
32
+ declare function buildTypography(index: FlatIndex): TypographyMap;
33
+ declare function buildRounded(index: FlatIndex): RoundedMap;
34
+ declare function buildSpacing(index: FlatIndex): SpacingMap | null;
35
+ declare function readContainers(index: FlatIndex): ContainerInfo;
36
+ //# sourceMappingURL=to-design-md.resolve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"to-design-md.resolve.d.ts","sourceRoot":"","sources":["../../src/serialize/to-design-md.resolve.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AACjF,OAAO,KAAK,EACV,iBAAiB,EACjB,UAAU,EACV,aAAa,EACb,UAAU,EACV,UAAU,EACV,aAAa,EACd,MAAM,sBAAsB,CAAC;AAI9B,YAAY,EAAE,cAAc,EAAE,CAAC;AAC/B,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,cAAc,EACd,iBAAiB,GAClB,CAAC;AAIF,KAAK,SAAS,GAAG,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;AAE9C,UAAU,cAAc;IACtB,KAAK,EAAE,UAAU,CAAC;IAClB,YAAY,EAAE,iBAAiB,CAAC;CACjC;AAID,iBAAS,UAAU,CAAC,IAAI,EAAE,cAAc,EAAE,GAAG,SAAS,CAMrD;AAED,iBAAS,gBAAgB,CAAC,IAAI,EAAE,eAAe,GAAG,SAAS,CAI1D;AAyBD;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,SAAS,EAChB,OAAO,GAAE,GAAG,CAAC,MAAM,CAAa,EAChC,UAAU,CAAC,EAAE,MAAM,GAClB,MAAM,GAAG,IAAI,CA0Bf;AAiCD,iBAAS,iBAAiB,CAAC,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,GAAG,cAAc,CAqClF;AAID,iBAAS,eAAe,CAAC,KAAK,EAAE,SAAS,GAAG,aAAa,CAYxD;AAmBD,iBAAS,YAAY,CAAC,KAAK,EAAE,SAAS,GAAG,UAAU,CA+BlD;AAOD,iBAAS,YAAY,CAAC,KAAK,EAAE,SAAS,GAAG,UAAU,GAAG,IAAI,CA+BzD;AAID,iBAAS,cAAc,CAAC,KAAK,EAAE,SAAS,GAAG,aAAa,CAavD"}