🚀 Socket Launch Week Day 4:Socket MCP Adds Org Alerts, Threat Feed Review, and Package Inspection.Learn more
Sign In

typeui.sh

Package Overview
Dependencies
Maintainers
1
Versions
13
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

typeui.sh - npm Package Compare versions

Comparing version
0.6.0
to
0.7.0
+312
dist/generation/randomDesignSystem.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateRandomDesignSystem = generateRandomDesignSystem;
const designSystemSchema_1 = require("../domain/designSystemSchema");
const PRODUCT_PREFIXES = [
"Atlas",
"Nova",
"Lumen",
"Vertex",
"Nimbus",
"Harbor",
"Prism",
"Cobalt",
"Echo",
"Forge",
"Pulse",
"Aster"
];
const PRODUCT_SUFFIXES = [
"UI",
"Cloud",
"Flow",
"Studio",
"Works",
"Console",
"Labs",
"Desk",
"Board",
"Portal",
"Suite",
"System"
];
const VISUAL_STYLE_OPTIONS = [
"modern",
"minimal",
"clean",
"high-contrast",
"bold",
"playful",
"editorial",
"data-dense",
"enterprise",
"premium"
];
const TYPOGRAPHY_SCALE_OPTIONS = [
"12/14/16/20/24/32",
"12/14/16/18/24/30/36",
"13/15/17/21/27/35",
"14/16/18/24/32/40",
"mobile-first compact scale",
"desktop-first expressive scale"
];
const FONT_WEIGHT_OPTIONS = [
"100",
"200",
"300",
"400",
"500",
"600",
"700",
"800",
"900"
];
const GOOGLE_FONT_SANS_OPTIONS = [
"Inter",
"Roboto",
"Open Sans",
"Lato",
"Montserrat",
"Poppins",
"Nunito",
"Work Sans",
"Source Sans 3",
"Plus Jakarta Sans",
"Archivo",
"Barlow",
"Kanit",
"M PLUS 1p",
"Raleway",
"PT Sans",
"Ubuntu",
"Cabin",
"Hind",
"Public Sans",
"Mulish",
"Quicksand",
"Lexend",
"Manrope",
"Noto Sans",
"DM Sans",
"Rubik",
"Urbanist"
];
const GOOGLE_FONT_DISPLAY_OPTIONS = [
"Inter",
"Montserrat",
"Poppins",
"Space Grotesk",
"Plus Jakarta Sans",
"Outfit",
"Playfair Display",
"Merriweather",
"Bebas Neue",
"Raleway",
"Oswald",
"Archivo",
"Anton",
"Bricolage Grotesque",
"Sora",
"Figtree",
"Josefin Sans",
"Lora",
"Archivo Black",
"Abril Fatface",
"Cormorant Garamond",
"DM Serif Display"
];
const GOOGLE_FONT_MONO_OPTIONS = [
"JetBrains Mono",
"Fira Code",
"Source Code Pro",
"IBM Plex Mono",
"Inconsolata",
"Space Mono",
"Roboto Mono",
"Ubuntu Mono",
"Fira Mono",
"Cousine",
"PT Mono",
"Anonymous Pro",
"Overpass Mono"
];
const COLOR_PALETTE_OPTIONS = [
"primary",
"secondary",
"neutral",
"success",
"warning",
"danger",
"info",
"surface/subtle layers",
"dark mode parity"
];
const SPACING_SCALE_OPTIONS = [
"4/8/12/16/24/32",
"2/4/8/12/16/24/32/48",
"8pt baseline grid",
"compact density mode",
"comfortable density mode"
];
const ACCESSIBILITY_OPTIONS = [
"WCAG 2.2 AA",
"keyboard-first interactions",
"visible focus states",
"semantic HTML before ARIA",
"screen-reader tested labels",
"reduced-motion support",
"44px+ touch targets",
"high-contrast support"
];
const WRITING_TONE_OPTIONS = [
"concise",
"confident",
"helpful",
"clear",
"friendly",
"professional",
"action-oriented",
"low-jargon"
];
const DO_RULE_OPTIONS = [
"prefer semantic tokens over raw values",
"preserve visual hierarchy",
"keep interaction states explicit",
"design for empty/loading/error states",
"ensure responsive behavior by default",
"document accessibility rationale"
];
const DONT_RULE_OPTIONS = [
"avoid low contrast text",
"avoid inconsistent spacing rhythm",
"avoid decorative motion without purpose",
"avoid ambiguous labels",
"avoid mixing multiple visual metaphors",
"avoid inaccessible hit areas"
];
const BRAND_AUDIENCES = [
"product",
"engineering",
"design systems",
"growth",
"operations",
"support"
];
const BRAND_DOMAINS = [
"dashboard",
"admin",
"commerce",
"analytics",
"collaboration",
"publishing",
"developer tooling",
"ops automation"
];
function randomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function sampleOne(values) {
return values[randomInt(0, values.length - 1)];
}
function sampleMany(values, minCount, maxCount) {
const count = randomInt(minCount, Math.min(maxCount, values.length));
const shuffled = [...values];
for (let i = shuffled.length - 1; i > 0; i -= 1) {
const j = randomInt(0, i);
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
}
return shuffled.slice(0, count);
}
function hslToHex(hue, saturation, lightness) {
const s = saturation / 100;
const l = lightness / 100;
const c = (1 - Math.abs(2 * l - 1)) * s;
const x = c * (1 - Math.abs(((hue / 60) % 2) - 1));
const m = l - c / 2;
let r = 0;
let g = 0;
let b = 0;
if (hue < 60) {
r = c;
g = x;
}
else if (hue < 120) {
r = x;
g = c;
}
else if (hue < 180) {
g = c;
b = x;
}
else if (hue < 240) {
g = x;
b = c;
}
else if (hue < 300) {
r = x;
b = c;
}
else {
r = c;
b = x;
}
const toHex = (value) => Math.round((value + m) * 255)
.toString(16)
.padStart(2, "0")
.toUpperCase();
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
}
function generateColorTokens() {
const baseHue = randomInt(0, 359);
const primary = hslToHex(baseHue, randomInt(58, 84), randomInt(32, 46));
const secondary = hslToHex((baseHue + randomInt(32, 92)) % 360, randomInt(60, 88), randomInt(38, 55));
const success = hslToHex(randomInt(122, 145), randomInt(55, 75), randomInt(34, 46));
const warning = hslToHex(randomInt(30, 44), randomInt(72, 90), randomInt(42, 56));
const danger = hslToHex(randomInt(2, 15), randomInt(65, 88), randomInt(42, 55));
const surface = hslToHex(baseHue, randomInt(10, 24), randomInt(95, 99));
const text = hslToHex(baseHue, randomInt(18, 34), randomInt(11, 18));
return { primary, secondary, success, warning, danger, surface, text };
}
function generateProductName() {
return `${sampleOne(PRODUCT_PREFIXES)} ${sampleOne(PRODUCT_SUFFIXES)}`;
}
function generateBrandSummary(productName, visualStyle) {
const audience = sampleOne(BRAND_AUDIENCES);
const domain = sampleOne(BRAND_DOMAINS);
const direction = visualStyle.split(",")[0]?.trim() || visualStyle;
return `${productName} delivers ${direction} interface guidance for ${domain} workflows, helping ${audience} teams ship consistently and accessibly.`;
}
function generateTypographyScale() {
const scale = sampleOne(TYPOGRAPHY_SCALE_OPTIONS);
const primary = sampleOne(GOOGLE_FONT_SANS_OPTIONS);
const display = sampleOne(GOOGLE_FONT_DISPLAY_OPTIONS);
const mono = sampleOne(GOOGLE_FONT_MONO_OPTIONS);
const weights = sampleMany(FONT_WEIGHT_OPTIONS, 4, 7).sort((a, b) => Number(a) - Number(b));
return `${scale} | Fonts: primary=${primary}, display=${display}, mono=${mono} | weights=${weights.join(", ")}`;
}
function generateColorPalette() {
const required = ["primary", "neutral"];
const optional = COLOR_PALETTE_OPTIONS.filter((value) => !required.includes(value));
const selected = [...required, ...sampleMany(optional, 2, 5)];
const tokens = generateColorTokens();
return (`${selected.join(", ")} | Tokens: primary=${tokens.primary}, secondary=${tokens.secondary}, ` +
`success=${tokens.success}, warning=${tokens.warning}, danger=${tokens.danger}, ` +
`surface=${tokens.surface}, text=${tokens.text}`);
}
function generateRandomDesignSystem() {
const visualStyle = sampleMany(VISUAL_STYLE_OPTIONS, 3, 5).join(", ");
const design = {
productName: generateProductName(),
brandSummary: "",
visualStyle,
typographyScale: generateTypographyScale(),
colorPalette: generateColorPalette(),
spacingScale: sampleOne(SPACING_SCALE_OPTIONS),
accessibilityRequirements: sampleMany(ACCESSIBILITY_OPTIONS, 3, 5).join(", "),
writingTone: sampleMany(WRITING_TONE_OPTIONS, 2, 4).join(", "),
doRules: sampleMany(DO_RULE_OPTIONS, 3, 5),
dontRules: sampleMany(DONT_RULE_OPTIONS, 3, 5)
};
design.brandSummary = generateBrandSummary(design.productName, visualStyle);
return designSystemSchema_1.DesignSystemSchema.parse(design);
}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.runDesignGeneration = runDesignGeneration;
const updateSkillFile_1 = require("../io/updateSkillFile");
const shared_1 = require("../renderers/shared");
async function runDesignGeneration(options) {
const content = (0, shared_1.createDesignMarkdownFile)(options.designSystem);
const result = await (0, updateSkillFile_1.writeMarkdownFile)(options.projectRoot, "DESIGN.md", content, options.dryRun ?? false);
return [
{
filePath: result.absPath,
changed: result.changed
}
];
}
+97
-15

@@ -13,2 +13,4 @@ #!/usr/bin/env node

const existingDesignSystem_1 = require("./generation/existingDesignSystem");
const randomDesignSystem_1 = require("./generation/randomDesignSystem");
const runDesignGeneration_1 = require("./generation/runDesignGeneration");
const runGeneration_1 = require("./generation/runGeneration");

@@ -37,2 +39,15 @@ const runPull_1 = require("./generation/runPull");

}
function parsePullFormatOption(raw) {
const parsed = designSystemSchema_1.PullFormatSchema.safeParse(raw ?? "skill");
if (!parsed.success) {
throw new Error("Unsupported format. Supported: skill, design.");
}
return parsed.data;
}
async function resolvePullFormatOption(raw) {
if (raw) {
return parsePullFormatOption(raw);
}
return (0, registry_1.promptPullFormatSelection)();
}
function printResults(mode, results) {

@@ -45,6 +60,51 @@ console.log("");

}
async function randomizeLike(options) {
const format = await resolvePullFormatOption(options.format);
const designSystem = (0, randomDesignSystem_1.generateRandomDesignSystem)();
if (format === "design") {
const results = await (0, runDesignGeneration_1.runDesignGeneration)({
projectRoot: process.cwd(),
designSystem,
dryRun: Boolean(options.dryRun)
});
printResults(options.dryRun ? "preview" : "randomized", results);
return;
}
const selectedProviders = parseProviderOption(options.providers) ?? (await (0, designSystem_1.promptProviders)());
const providers = [...new Set([...types_1.ALWAYS_INCLUDED_PROVIDERS, ...selectedProviders])];
const results = await (0, runGeneration_1.runGeneration)({
projectRoot: process.cwd(),
providers,
designSystem,
metadata: (0, skillMetadata_1.buildDefaultSkillMetadata)(designSystem.productName),
dryRun: Boolean(options.dryRun)
});
printResults(options.dryRun ? "preview" : "randomized", results);
}
async function generateLike(action, mode, options) {
const format = await resolvePullFormatOption(options.format);
let designSystem;
if (format === "design") {
if (action === "update") {
const existing = await (0, existingDesignSystem_1.loadExistingDesignMarkdown)(process.cwd());
if (!existing) {
throw new Error("No existing DESIGN.md found in the project root. Run `typeui.sh generate` first.");
}
const fields = await (0, designSystem_1.promptDesignSystemFields)();
const updates = await (0, designSystem_1.promptDesignSystemUpdates)(existing, fields);
designSystem = { ...existing, ...updates };
}
else {
designSystem = await (0, designSystem_1.promptDesignSystem)("typeui.sh");
}
const results = await (0, runDesignGeneration_1.runDesignGeneration)({
projectRoot: process.cwd(),
designSystem,
dryRun: Boolean(options.dryRun)
});
printResults(mode, results);
return;
}
const selectedProviders = parseProviderOption(options.providers) ?? (await (0, designSystem_1.promptProviders)());
const providers = [...new Set([...types_1.ALWAYS_INCLUDED_PROVIDERS, ...selectedProviders])];
let designSystem;
let metadata = (0, skillMetadata_1.buildDefaultSkillMetadata)("typeui.sh");

@@ -79,5 +139,12 @@ if (action === "update") {

}
const selectedProviders = parseProviderOption(options.providers) ?? (await (0, designSystem_1.promptProviders)());
const providers = [...new Set([...types_1.ALWAYS_INCLUDED_PROVIDERS, ...selectedProviders])];
const pullResult = await (0, registryClient_1.pullSkillMarkdown)(parsedSlug.data);
const format = await resolvePullFormatOption(options.format);
const providers = format === "skill"
? [
...new Set([
...types_1.ALWAYS_INCLUDED_PROVIDERS,
...(parseProviderOption(options.providers) ?? (await (0, designSystem_1.promptProviders)()))
])
]
: [];
const pullResult = await (0, registryClient_1.pullRegistryMarkdown)(parsedSlug.data, format);
if (!pullResult.ok) {

@@ -90,2 +157,3 @@ throw new Error(`Registry pull failed: ${pullResult.reason}`);

markdown: pullResult.markdown,
format,
dryRun: Boolean(options.dryRun)

@@ -96,2 +164,3 @@ });

async function listLike(options) {
const format = await resolvePullFormatOption(options.format);
const specsResult = await (0, registryClient_1.listRegistrySpecs)();

@@ -105,8 +174,8 @@ if (!specsResult.ok) {

}
const selectableSpecs = specsResult.specs.filter((spec) => spec.hasSkillMd);
const selectableSpecs = specsResult.specs.filter((spec) => (format === "skill" ? spec.hasSkillMd : spec.hasDesignMd));
if (selectableSpecs.length === 0) {
throw new Error("No pullable registry specs available.");
throw new Error(`No pullable registry specs available for format '${format}'.`);
}
const selected = await (0, registry_1.promptRegistrySpecSelection)(specsResult.specs);
await pullLike(selected.slug, options);
const selected = await (0, registry_1.promptRegistrySpecSelection)(specsResult.specs, format);
await pullLike(selected.slug, { ...options, format });
}

@@ -128,4 +197,5 @@ const program = new commander_1.Command();

.command("generate")
.description("Generate provider skill files in the current project.")
.option("-p, --providers <providers>", "Comma-separated providers")
.description("Generate SKILL.md provider files or DESIGN.md in the current project.")
.option("-p, --providers <providers>", "Comma-separated providers (skill format only)")
.option("-f, --format <format>", "Output format (skill|design)")
.option("--dry-run", "Preview file changes without writing")

@@ -137,4 +207,5 @@ .action(async (options) => {

.command("update")
.description("Update existing provider skill files in the current project.")
.option("-p, --providers <providers>", "Comma-separated providers")
.description("Update existing SKILL.md provider files or root DESIGN.md in the current project.")
.option("-p, --providers <providers>", "Comma-separated providers (skill format only)")
.option("-f, --format <format>", "Output format (skill|design)")
.option("--dry-run", "Preview file changes without writing")

@@ -146,4 +217,5 @@ .action(async (options) => {

.command("pull <slug>")
.description("Pull a registry skill by slug and write selected provider files.")
.option("-p, --providers <providers>", "Comma-separated providers")
.description("Pull a registry markdown file by slug and write SKILL.md or DESIGN.md outputs.")
.option("-p, --providers <providers>", "Comma-separated providers (skill format only)")
.option("-f, --format <format>", "Registry file format (skill|design)")
.option("--dry-run", "Preview file changes without writing")

@@ -156,3 +228,4 @@ .action(async (slug, options) => {

.description("List available registry design system specs.")
.option("-p, --providers <providers>", "Comma-separated providers")
.option("-p, --providers <providers>", "Comma-separated providers (skill format only)")
.option("-f, --format <format>", "Registry file format (skill|design)")
.option("--dry-run", "Preview file changes without writing")

@@ -162,2 +235,11 @@ .action(async (options) => {

});
program
.command("randomize")
.description("Generate a randomized local design system and write SKILL.md or DESIGN.md outputs.")
.option("-p, --providers <providers>", "Comma-separated providers (skill format only)")
.option("-f, --format <format>", "Output format (skill|design)")
.option("--dry-run", "Preview file changes without writing")
.action(async (options) => {
await randomizeLike(options);
});
program.parseAsync().catch((error) => {

@@ -164,0 +246,0 @@ const message = error instanceof Error ? error.message : String(error);

+3
-3

@@ -14,4 +14,4 @@ "use strict";

exports.REGISTRY_SPECS_URL = `${exports.GITHUB_REGISTRY_RAW_BASE_URL}/skills/index.json`;
function getRegistryPullUrl(skillPath) {
const encodedPath = skillPath
function getRegistryPullUrl(markdownPath) {
const encodedPath = markdownPath
.split("/")

@@ -30,3 +30,3 @@ .filter(Boolean)

function getDesignSystemPreviewUrl(slug) {
return `${exports.API_DOMAIN}/design-systems/${encodeURIComponent(slug)}`;
return `${exports.API_DOMAIN}/design-skills/${encodeURIComponent(slug)}`;
}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RegistrySlugSchema = exports.FlatDesignSystemPromptSchema = exports.SkillMetadataSchema = exports.DesignSystemSchema = exports.ProviderSelectionSchema = void 0;
exports.PullFormatSchema = exports.RegistrySlugSchema = exports.FlatDesignSystemPromptSchema = exports.SkillMetadataSchema = exports.DesignSystemSchema = exports.ProviderSelectionSchema = void 0;
const zod_1 = require("zod");

@@ -64,1 +64,2 @@ const types_1 = require("../types");

.regex(/^[a-z0-9](?:[a-z0-9-_]*[a-z0-9])?$/, "Slug must contain only lowercase letters, numbers, dashes, or underscores.");
exports.PullFormatSchema = zod_1.z.enum(["skill", "design"]);

@@ -7,4 +7,6 @@ "use strict";

exports.parseManagedDesignSystem = parseManagedDesignSystem;
exports.parseDesignMarkdown = parseDesignMarkdown;
exports.parseSkillMetadata = parseSkillMetadata;
exports.loadExistingDesignSystem = loadExistingDesignSystem;
exports.loadExistingDesignMarkdown = loadExistingDesignMarkdown;
const promises_1 = __importDefault(require("node:fs/promises"));

@@ -52,2 +54,7 @@ const node_path_1 = __importDefault(require("node:path"));

}
function extractSectionToEnd(body, title) {
const pattern = new RegExp(`${escapeRegExp(title)}\\n([\\s\\S]*)`);
const match = body.match(pattern);
return match?.[1]?.trim() ?? null;
}
function extractListSection(body, title, nextTitle) {

@@ -67,2 +74,6 @@ const text = extractSection(body, title, nextTitle);

}
function extractDesignStyleValue(body, label) {
const match = body.match(new RegExp(`^- \\*\\*${escapeRegExp(label)}:\\*\\* (.+)$`, "m"));
return match?.[1]?.trim() ?? null;
}
function parseManagedDesignSystem(content) {

@@ -108,2 +119,38 @@ const managed = extractManagedBlock(content);

}
function parseDesignMarkdown(content) {
const frontmatter = extractFrontmatter(content);
if (!frontmatter) {
return null;
}
const productNameRaw = parseFrontmatterField(frontmatter, "name");
const productName = productNameRaw ? parseQuotedYamlValue(productNameRaw) : "";
const brandSummary = extractSection(content, "## Overview", "## Style Foundations");
const visualStyle = extractDesignStyleValue(content, "Visual style");
const typographyScale = extractDesignStyleValue(content, "Typography scale");
const colorPalette = extractDesignStyleValue(content, "Color palette");
const spacingScale = extractDesignStyleValue(content, "Spacing scale");
const accessibilityRequirements = extractSection(content, "## Accessibility", "## Writing Tone");
const writingTone = extractSection(content, "## Writing Tone", "## Rules: Do");
const doRules = extractListSection(content, "## Rules: Do", "## Rules: Don't");
const dontText = extractSectionToEnd(content, "## Rules: Don't");
const dontRules = dontText
? dontText
.split("\n")
.map((line) => line.replace(/^- /, "").trim())
.filter(Boolean)
: null;
const parsed = designSystemSchema_1.DesignSystemSchema.safeParse({
productName,
brandSummary: brandSummary ?? "",
visualStyle: visualStyle ?? "",
typographyScale: typographyScale ?? "",
colorPalette: colorPalette ?? "",
spacingScale: spacingScale ?? "",
accessibilityRequirements: accessibilityRequirements ?? "",
writingTone: writingTone ?? "",
doRules: doRules ?? [],
dontRules: dontRules ?? []
});
return parsed.success ? parsed.data : null;
}
function parseSkillMetadata(content) {

@@ -148,1 +195,15 @@ const frontmatter = extractFrontmatter(content);

}
async function loadExistingDesignMarkdown(projectRoot) {
const absPath = node_path_1.default.resolve(projectRoot, "DESIGN.md");
try {
const content = await promises_1.default.readFile(absPath, "utf8");
return parseDesignMarkdown(content);
}
catch (error) {
const e = error;
if (e.code !== "ENOENT") {
throw error;
}
}
return null;
}

@@ -7,6 +7,20 @@ "use strict";

async function runPull(options) {
const format = options.format ?? "skill";
if (format === "design") {
const result = await (0, updateSkillFile_1.writeMarkdownFile)(options.projectRoot, "DESIGN.md", options.markdown, options.dryRun ?? false);
return [
{
filePath: result.absPath,
changed: result.changed
}
];
}
const providers = options.providers ?? [];
if (providers.length === 0) {
throw new Error("No providers selected for skill format.");
}
const results = [];
const seenPaths = new Set();
for (const provider of options.providers) {
const relativePath = types_1.PROVIDER_DETAILS[provider].relativePath;
for (const provider of providers) {
const relativePath = (0, types_1.getProviderOutputPath)(provider, format);
if (seenPaths.has(relativePath)) {

@@ -13,0 +27,0 @@ continue;

@@ -7,2 +7,3 @@ "use strict";

exports.upsertManagedSkillFile = upsertManagedSkillFile;
exports.writeMarkdownFile = writeMarkdownFile;
const promises_1 = __importDefault(require("node:fs/promises"));

@@ -78,1 +79,21 @@ const node_path_1 = __importDefault(require("node:path"));

}
async function writeMarkdownFile(projectRoot, relativePath, content, dryRun = false) {
const absPath = node_path_1.default.resolve(projectRoot, relativePath);
await promises_1.default.mkdir(node_path_1.default.dirname(absPath), { recursive: true });
let existing = "";
try {
existing = await promises_1.default.readFile(absPath, "utf8");
}
catch (error) {
const e = error;
if (e.code !== "ENOENT") {
throw error;
}
}
const nextContent = content.endsWith("\n") ? content : `${content}\n`;
const changed = existing !== nextContent;
if (!dryRun && changed) {
await promises_1.default.writeFile(absPath, nextContent, "utf8");
}
return { absPath, changed };
}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.promptPullFormatSelection = promptPullFormatSelection;
exports.promptRegistrySpecSelection = promptRegistrySpecSelection;

@@ -14,7 +15,33 @@ const config_1 = require("../config");

}
async function promptRegistrySpecSelection(specs) {
async function promptPullFormatSelection() {
const answer = await prompt([
{
type: "list",
name: "format",
message: "Select output format:",
choices: [
{
name: "SKILL.md (provider-specific paths)",
value: "skill"
},
{
name: "DESIGN.md (project root)",
value: "design"
}
]
}
]);
return answer.format;
}
async function promptRegistrySpecSelection(specs, format) {
const choices = specs.map((spec) => ({
name: `${spec.name} (${(0, config_1.getDesignSystemPreviewUrl)(spec.slug).replace(/^https?:\/\//, "")})${spec.hasSkillMd ? "" : " - no skill markdown"}`,
name: `${spec.name} (${(0, config_1.getDesignSystemPreviewUrl)(spec.slug).replace(/^https?:\/\//, "")})${(format === "skill" ? spec.hasSkillMd : spec.hasDesignMd) ? "" : ` - no ${format} markdown`}`,
value: spec.slug,
disabled: spec.hasSkillMd ? false : "No skill markdown available for pull."
disabled: format === "skill"
? spec.hasSkillMd
? false
: "No skill markdown available for pull."
: spec.hasDesignMd
? false
: "No design markdown available for pull."
}));

@@ -25,3 +52,3 @@ const answer = await prompt([

name: "selected",
message: "Select one registry spec to pull:",
message: `Select one registry spec to pull (${format}):`,
choices,

@@ -28,0 +55,0 @@ validate: (value) => value.length === 1 || "Select exactly one spec."

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.pullRegistryMarkdown = pullRegistryMarkdown;
exports.pullSkillMarkdown = pullSkillMarkdown;

@@ -26,9 +27,16 @@ exports.listRegistrySpecs = listRegistrySpecs;

const candidate = rawValue;
if (typeof candidate.slug !== "string" || typeof candidate.name !== "string" || typeof candidate.skillPath !== "string") {
if (typeof candidate.slug !== "string" || typeof candidate.name !== "string") {
return null;
}
if (candidate.skillPath !== undefined && typeof candidate.skillPath !== "string") {
return null;
}
if (candidate.designPath !== undefined && typeof candidate.designPath !== "string") {
return null;
}
index[key] = {
slug: candidate.slug,
name: candidate.name,
skillPath: candidate.skillPath
skillPath: candidate.skillPath ?? "",
designPath: candidate.designPath ?? ""
};

@@ -84,3 +92,20 @@ }

}
async function pullSkillMarkdown(slug) {
function inferDesignPathFromSkillPath(skillPath) {
const normalizedSkillPath = skillPath.trim();
if (!normalizedSkillPath.endsWith("SKILL.md")) {
return "";
}
return normalizedSkillPath.replace(/SKILL\.md$/, "DESIGN.md");
}
function resolveRegistryMarkdownPath(entry, format) {
if (format === "skill") {
return entry.skillPath.trim();
}
const explicitDesignPath = entry.designPath.trim();
if (explicitDesignPath) {
return explicitDesignPath;
}
return inferDesignPathFromSkillPath(entry.skillPath);
}
async function pullRegistryMarkdown(slug, format) {
const parsedSlug = designSystemSchema_1.RegistrySlugSchema.safeParse(slug);

@@ -104,9 +129,10 @@ if (!parsedSlug.success) {

}
if (!entry.skillPath.trim()) {
const markdownPath = resolveRegistryMarkdownPath(entry, format);
if (!markdownPath) {
return {
ok: false,
reason: `No skill markdown path found for slug '${parsedSlug.data}'.`
reason: `No ${format} markdown path found for slug '${parsedSlug.data}'.`
};
}
const endpoint = (0, config_1.getRegistryPullUrl)(entry.skillPath);
const endpoint = (0, config_1.getRegistryPullUrl)(markdownPath);
let response;

@@ -131,3 +157,3 @@ try {

ok: false,
reason: mapRegistryHttpFailure(response.status, `fetching markdown for '${parsedSlug.data}'`)
reason: mapRegistryHttpFailure(response.status, `fetching ${format} markdown for '${parsedSlug.data}'`)
};

@@ -152,2 +178,5 @@ }

}
async function pullSkillMarkdown(slug) {
return pullRegistryMarkdown(slug, "skill");
}
async function listRegistrySpecs() {

@@ -163,3 +192,4 @@ const indexResult = await fetchRegistryIndex();

previewUrl: (0, config_1.getDesignSystemUrl)(entry.slug),
hasSkillMd: Boolean(entry.skillPath.trim())
hasSkillMd: Boolean(entry.skillPath.trim()),
hasDesignMd: Boolean(resolveRegistryMarkdownPath(entry, "design"))
}));

@@ -166,0 +196,0 @@ return {

@@ -6,2 +6,3 @@ "use strict";

exports.createManagedSkillFile = createManagedSkillFile;
exports.createDesignMarkdownFile = createDesignMarkdownFile;
const config_1 = require("../config");

@@ -89,2 +90,65 @@ const skillMetadata_1 = require("../skillMetadata");

}
function parseKeyValuePairs(value) {
return value
.split(",")
.map((part) => part.trim())
.filter(Boolean)
.reduce((acc, part) => {
const [rawKey, ...rawValueParts] = part.split("=");
const key = rawKey?.trim().toLowerCase();
const rawValue = rawValueParts.join("=").trim();
if (key && rawValue) {
acc[key] = rawValue;
}
return acc;
}, {});
}
function parseTypographyMetadata(typographyScale) {
const sourceScale = typographyScale.split("|")[0]?.trim() || "12/14/16/20/24/32";
const fontsMatch = typographyScale.match(/\|\s*Fonts:\s*([^|]+)/i);
const fontPairs = parseKeyValuePairs(fontsMatch?.[1] ?? "");
const weightsMatch = typographyScale.match(/weights\s*=\s*([^|]+)/i);
return {
sourceScale,
primary: fontPairs.primary ?? "Public Sans",
display: fontPairs.display ?? fontPairs.primary ?? "Public Sans",
mono: fontPairs.mono ?? "Space Grotesk",
weights: weightsMatch?.[1]?.trim() ?? "400, 500, 600, 700"
};
}
function parseColorTokens(colorPalette) {
const tokensMatch = colorPalette.match(/\|\s*Tokens:\s*([^|]+)/i);
const tokenPairs = parseKeyValuePairs(tokensMatch?.[1] ?? "");
const primary = tokenPairs.primary ?? "#1A1C1E";
const secondary = tokenPairs.secondary ?? "#6C7278";
const surface = tokenPairs.surface ?? "#F7F5F2";
const text = tokenPairs.text ?? "#1A1C1E";
return {
primary,
secondary,
tertiary: tokenPairs.tertiary ?? secondary,
neutral: tokenPairs.neutral ?? surface,
success: tokenPairs.success ?? "#16A34A",
warning: tokenPairs.warning ?? "#D97706",
danger: tokenPairs.danger ?? "#DC2626",
surface,
text
};
}
function deriveSpacingTokens(spacingScale) {
const numericValues = spacingScale.match(/\d+/g)?.map((value) => Number(value)) ?? [];
if (numericValues.length >= 2) {
return {
sm: `${numericValues[0]}px`,
md: `${numericValues[1]}px`
};
}
if (/compact/i.test(spacingScale)) {
return { sm: "4px", md: "8px" };
}
if (/comfortable/i.test(spacingScale)) {
return { sm: "8px", md: "16px" };
}
return { sm: "8px", md: "16px" };
}
function createSkillFrontmatter(metadata) {

@@ -103,1 +167,61 @@ return [

}
function createDesignMarkdownFile(design) {
const typography = parseTypographyMetadata(design.typographyScale);
const colors = parseColorTokens(design.colorPalette);
const spacing = deriveSpacingTokens(design.spacingScale);
return [
"---",
`name: "${escapeYamlString(design.productName)}"`,
"colors:",
` primary: "${colors.primary}"`,
` secondary: "${colors.secondary}"`,
` tertiary: "${colors.tertiary}"`,
` neutral: "${colors.neutral}"`,
` success: "${colors.success}"`,
` warning: "${colors.warning}"`,
` danger: "${colors.danger}"`,
` surface: "${colors.surface}"`,
` text: "${colors.text}"`,
"typography:",
" h1:",
` fontFamily: "${escapeYamlString(typography.display)}"`,
" fontSize: 3rem",
" body-md:",
` fontFamily: "${escapeYamlString(typography.primary)}"`,
" fontSize: 1rem",
" label-caps:",
` fontFamily: "${escapeYamlString(typography.mono)}"`,
" fontSize: 0.75rem",
` sourceScale: "${escapeYamlString(typography.sourceScale)}"`,
` weights: "${escapeYamlString(typography.weights)}"`,
"rounded:",
" sm: 4px",
" md: 8px",
"spacing:",
` sm: ${spacing.sm}`,
` md: ${spacing.md}`,
` sourceScale: "${escapeYamlString(design.spacingScale)}"`,
"---",
"",
"## Overview",
design.brandSummary,
"",
"## Style Foundations",
`- **Visual style:** ${design.visualStyle}`,
`- **Typography scale:** ${design.typographyScale}`,
`- **Color palette:** ${design.colorPalette}`,
`- **Spacing scale:** ${design.spacingScale}`,
"",
"## Accessibility",
design.accessibilityRequirements,
"",
"## Writing Tone",
design.writingTone,
"",
"## Rules: Do",
list(design.doRules),
"",
"## Rules: Don't",
list(design.dontRules)
].join("\n");
}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DESIGN_SYSTEM_FIELDS = exports.OPTIONAL_PROVIDERS = exports.ALWAYS_INCLUDED_PROVIDERS = exports.SUPPORTED_PROVIDERS = exports.PROVIDER_DETAILS = void 0;
exports.getProviderOutputPath = getProviderOutputPath;
exports.PROVIDER_DETAILS = {

@@ -150,2 +151,9 @@ universal: {

exports.OPTIONAL_PROVIDERS = exports.SUPPORTED_PROVIDERS.filter((provider) => !Boolean(exports.PROVIDER_DETAILS[provider].alwaysIncluded));
function getProviderOutputPath(provider, format) {
const skillPath = exports.PROVIDER_DETAILS[provider].relativePath;
if (format === "skill") {
return skillPath;
}
return skillPath.replace(/\/SKILL\.md$/, "/DESIGN.md");
}
exports.DESIGN_SYSTEM_FIELDS = [

@@ -152,0 +160,0 @@ "productName",

{
"name": "typeui.sh",
"version": "0.6.0",
"version": "0.7.0",
"description": "Generate design system specifications and style guides as skill files for AI coding providers",

@@ -5,0 +5,0 @@ "main": "dist/cli.js",

+26
-10

@@ -12,3 +12,3 @@ ```

[typeui.sh](https://www.typeui.sh) is an open-source command line interface (CLI) that generates, updates, and can download skill.md files with design system specifications to instruct agentic tools and LLM's to use a certain design when building interfaces.
[TypeUI](https://www.typeui.sh) is an open-source command line interface (CLI) that generates, updates, and can download `SKILL.md` or `DESIGN.md` files with design system specifications to instruct agentic tools and LLM's to use a certain design when building interfaces.

@@ -23,5 +23,5 @@ ## Getting started

## Design systems
## Design skills
Check out all [design systems](https://typeui.sh/design-skills) that can be pulled into your project.
Check out all [design skills](https://wwww.typeui.sh/design-skills) that can be pulled into your project. Available in both `DESIGN.md` and `SKILL.md` formats.

@@ -32,6 +32,7 @@ ## Available commands

| --- | --- |
| `generate` | Run the interactive design system prompts and generate skill files. |
| `update` | Update existing managed skill content in generated files. |
| `pull <slug>` | Pull a registry skill from `bergside/awesome-design-skills` and write it to selected provider paths. |
| `generate` | Run interactive prompts, choose `SKILL.md` or `DESIGN.md`, then generate output files. |
| `update` | Run interactive prompts, choose `SKILL.md` or `DESIGN.md`, then update existing output files. |
| `pull <slug>` | Pull a registry markdown file from `bergside/awesome-design-skills` (`SKILL.md` -> provider paths, `DESIGN.md` -> project root `DESIGN.md`). |
| `list` | Show available registry specs from `bergside/awesome-design-skills` (with typeui.sh preview links), then pull one automatically. |
| `randomize` | Generate a fully randomized local design system and write `SKILL.md` or `DESIGN.md` outputs. |

@@ -58,8 +59,23 @@ ## Design Skill File Structure

For local development:
## Local development
If you want to use this locally these are the commands you need to run:
```bash
npm install
npm run build
```
Then use the commands in your terminal:
```
node dist/cli.js --help
node dist/cli.js generate
node dist/cli.js generate --format design
node dist/cli.js randomize
node dist/cli.js randomize --format design
node dist/cli.js list
node dist/cli.js pull [slug]
node dist/cli.js pull [slug] --format design
node dist/cli.js list --format design
```

@@ -69,10 +85,10 @@

The CLI is open-source under the MIT License.
The CLI and public registry is open-source under the MIT License.
## Pro version
Get access to curated design system files by getting the [pro version](https://www.typeui.sh/#pricing) and supporting our work.
Get access to enhanched design skill files and a private Discord community by getting the [pro version](https://www.typeui.sh/#pricing) and thus supporting our open-source work.
## Sponsors
If you'd like to become a sponsor of the project, please [contact us](https://www.bergside.com/contact) on our company website.
If you'd like to become a sponsor of the project, please check out the [sponsorship page](https://www.typeui.sh/sponsor) on our website.