learning_ai_common_plat/packages/design-tokens/scripts/generate.ts
saravanakumardb1 b80d249c78 feat(design-tokens): add @bytelyst/design-tokens package
- Canonical bytelyst.tokens.json with colors, typography, spacing, radius, elevation, motion, breakpoints
- loadTokens() for programmatic access
- generate.ts script outputs 4 platform formats:
  - tokens.css (CSS custom properties with --ml-* prefix)
  - tokens.ts (TypeScript constants)
  - MindLystTokens.kt (Kotlin object for KMP shared module)
  - MindLystTheme.swift (Swift structs for SwiftUI)
- Shared across LysnrAI dashboards and MindLyst native apps
2026-02-12 11:22:52 -08:00

157 lines
5.8 KiB
TypeScript

/**
* Token generator — reads bytelyst.tokens.json, outputs 4 platform formats:
* 1. CSS custom properties (tokens.css)
* 2. TypeScript constants (tokens.ts)
* 3. Kotlin object (MindLystTokens.kt) — for KMP shared module
* 4. Swift structs (MindLystTheme.swift) — for iOS SwiftUI
*
* Usage: tsx scripts/generate.ts
*/
import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
import { resolve, dirname } from "node:path";
import { fileURLToPath } from "node:url";
const __dirname = dirname(fileURLToPath(import.meta.url));
const tokensPath = resolve(__dirname, "../tokens/bytelyst.tokens.json");
const outDir = resolve(__dirname, "../generated");
mkdirSync(outDir, { recursive: true });
const tokens = JSON.parse(readFileSync(tokensPath, "utf-8"));
// ── 1. CSS ───────────────────────────────────────────────────────────
function generateCSS(): string {
const lines: string[] = [
"/* Auto-generated from bytelyst.tokens.json — do not edit manually */",
"",
":root {",
];
// Semantic colors (dark theme as default)
for (const [key, value] of Object.entries(tokens.color.semantic.dark)) {
lines.push(` --ml-${camelToKebab(key)}: ${value};`);
}
// Typography
for (const [key, value] of Object.entries(tokens.typography.fontFamily)) {
lines.push(` --ml-font-${key}: ${value};`);
}
for (const [key, value] of Object.entries(tokens.typography.fontSize)) {
lines.push(` --ml-text-${key}: ${value}px;`);
}
// Spacing
for (const [key, value] of Object.entries(tokens.spacing)) {
lines.push(` --ml-space-${key}: ${value}px;`);
}
// Radius
for (const [key, value] of Object.entries(tokens.radius)) {
lines.push(` --ml-radius-${key}: ${value}px;`);
}
// Elevation
for (const [key, value] of Object.entries(tokens.elevation)) {
lines.push(` --ml-shadow-${key}: ${value};`);
}
lines.push("}", "");
return lines.join("\n");
}
// ── 2. TypeScript ────────────────────────────────────────────────────
function generateTS(): string {
return [
"// Auto-generated from bytelyst.tokens.json — do not edit manually",
"",
`export const tokens = ${JSON.stringify(tokens, null, 2)} as const;`,
"",
"export type Tokens = typeof tokens;",
"",
].join("\n");
}
// ── 3. Kotlin ────────────────────────────────────────────────────────
function generateKotlin(): string {
const lines: string[] = [
"// Auto-generated from bytelyst.tokens.json — do not edit manually",
"package com.mindlyst.shared.theme",
"",
"object MindLystTokens {",
"",
" // Semantic colors (dark)",
" object Colors {",
];
for (const [key, value] of Object.entries(tokens.color.semantic.dark)) {
if (typeof value === "string" && value.startsWith("#")) {
const hex = value.replace("#", "");
lines.push(` const val ${key} = 0xFF${hex.toUpperCase()}`);
}
}
lines.push(" }", "", " // Brain gradients", " object Brains {");
for (const [name, grad] of Object.entries(tokens.color.brain) as [string, { from: string; to: string }][]) {
const fromHex = grad.from.replace("#", "");
const toHex = grad.to.replace("#", "");
lines.push(` val ${name}From = 0xFF${fromHex.toUpperCase()}`);
lines.push(` val ${name}To = 0xFF${toHex.toUpperCase()}`);
}
lines.push(" }", "", " // Spacing (8pt grid)", " object Spacing {");
for (const [key, value] of Object.entries(tokens.spacing)) {
lines.push(` const val x${key} = ${value}`);
}
lines.push(" }", "", " // Radius", " object Radius {");
for (const [key, value] of Object.entries(tokens.radius)) {
lines.push(` const val ${key} = ${value}`);
}
lines.push(" }", "}");
return lines.join("\n");
}
// ── 4. Swift ─────────────────────────────────────────────────────────
function generateSwift(): string {
const lines: string[] = [
"// Auto-generated from bytelyst.tokens.json — do not edit manually",
"import SwiftUI",
"",
"struct MindLystColors {",
];
for (const [key, value] of Object.entries(tokens.color.semantic.dark)) {
if (typeof value === "string" && value.startsWith("#")) {
lines.push(` static let ${key} = Color(hex: "${value}")`);
}
}
lines.push("}", "", "struct MindLystSpacing {");
for (const [key, value] of Object.entries(tokens.spacing)) {
lines.push(` static let x${key}: CGFloat = ${value}`);
}
lines.push("}", "", "struct MindLystRadius {");
for (const [key, value] of Object.entries(tokens.radius)) {
lines.push(` static let ${key}: CGFloat = ${value}`);
}
lines.push("}");
return lines.join("\n");
}
// ── Helpers ──────────────────────────────────────────────────────────
function camelToKebab(str: string): string {
return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
}
// ── Write all ────────────────────────────────────────────────────────
writeFileSync(resolve(outDir, "tokens.css"), generateCSS());
writeFileSync(resolve(outDir, "tokens.ts"), generateTS());
writeFileSync(resolve(outDir, "MindLystTokens.kt"), generateKotlin());
writeFileSync(resolve(outDir, "MindLystTheme.swift"), generateSwift());
console.log("Generated 4 token files in generated/");