@atomixstudio/mcp 1.0.26 → 1.0.27
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.
- package/README.md +1 -1
- package/dist/index.js +55 -36
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -100,7 +100,7 @@ Run these prompts from your AI tool (e.g. **/--hello**, **/--get-started**):
|
|
|
100
100
|
|
|
101
101
|
| Prompt | Description |
|
|
102
102
|
|--------|-------------|
|
|
103
|
-
| **/--hello** | Get started — overview, tokens,
|
|
103
|
+
| **/--hello** | Get started — overview, tokens, essential commands (including **/--sync-to-figma**). Run this first. |
|
|
104
104
|
| **/--get-started** | Get started with design system in project. Three phases; creates files only after you approve. |
|
|
105
105
|
| **/--rules** | Governance rules for your AI tool (Cursor, Copilot, Windsurf, etc.). |
|
|
106
106
|
| **/--sync** | Sync tokens, AI rules, skills files, and dependencies manifest (icons, fonts). Use **/--refactor** to migrate deprecated tokens. |
|
package/dist/index.js
CHANGED
|
@@ -1607,7 +1607,8 @@ function buildFigmaPayloadsFromDS(data) {
|
|
|
1607
1607
|
}
|
|
1608
1608
|
if (variables.length === 0 && modes.length === 0) modes.push("Light");
|
|
1609
1609
|
const dsName = data.meta?.name;
|
|
1610
|
-
const
|
|
1610
|
+
const collectionPrefix = dsName ? `${dsName} ` : "";
|
|
1611
|
+
const colorCollectionName = `${collectionPrefix}Colors`;
|
|
1611
1612
|
const textStyles = [];
|
|
1612
1613
|
const sizeToPx = (val, basePx = 16) => {
|
|
1613
1614
|
if (typeof val === "number") return Math.round(val);
|
|
@@ -1642,7 +1643,15 @@ function buildFigmaPayloadsFromDS(data) {
|
|
|
1642
1643
|
}
|
|
1643
1644
|
return "Inter";
|
|
1644
1645
|
};
|
|
1645
|
-
const
|
|
1646
|
+
const toFontFamilyString = (val) => {
|
|
1647
|
+
if (typeof val === "string") {
|
|
1648
|
+
const s = val.trim().replace(/^["']|["']$/g, "");
|
|
1649
|
+
return s || "Inter";
|
|
1650
|
+
}
|
|
1651
|
+
return firstFont(val);
|
|
1652
|
+
};
|
|
1653
|
+
const fontFamilyMap = typography?.fontFamily ?? {};
|
|
1654
|
+
const defaultFontFamily = typography ? firstFont(typography.fontFamily ?? "Inter") : "Inter";
|
|
1646
1655
|
const fontSizeMap = typography?.fontSize;
|
|
1647
1656
|
const fontWeightMap = typography?.fontWeight;
|
|
1648
1657
|
const lineHeightMap = typography?.lineHeight;
|
|
@@ -1653,6 +1662,10 @@ function buildFigmaPayloadsFromDS(data) {
|
|
|
1653
1662
|
for (const [key, sizeVal] of Object.entries(fontSizeMap)) {
|
|
1654
1663
|
const fontSize = sizeToPx(sizeVal);
|
|
1655
1664
|
if (fontSize <= 0) continue;
|
|
1665
|
+
const role = typesetKeyToFontFamilyRole(key);
|
|
1666
|
+
const fontFamily = toFontFamilyString(
|
|
1667
|
+
fontFamilyMap[role] ?? fontFamilyMap.body ?? fontFamilyMap.heading ?? fontFamilyMap.display ?? defaultFontFamily
|
|
1668
|
+
);
|
|
1656
1669
|
const lh = lineHeightMap && typeof lineHeightMap === "object" ? lineHeightMap[key] : void 0;
|
|
1657
1670
|
const weight = fontWeightMap && typeof fontWeightMap === "object" ? fontWeightMap[key] : void 0;
|
|
1658
1671
|
const fontWeight = weight != null ? String(weight) : "400";
|
|
@@ -1695,7 +1708,7 @@ function buildFigmaPayloadsFromDS(data) {
|
|
|
1695
1708
|
const lineHeightUnitless = lhStr != null ? lhStr.endsWith("%") ? parseFloat(lhStr) / 100 : sizeToPx(lhStr) / fontSize : 1.5;
|
|
1696
1709
|
const payload = {
|
|
1697
1710
|
name: styleName.startsWith("Typography") ? styleName : `Typography / ${styleName.replace(/\//g, " / ")}`,
|
|
1698
|
-
fontFamily,
|
|
1711
|
+
fontFamily: defaultFontFamily,
|
|
1699
1712
|
fontWeight: String(style.fontWeight ?? "400"),
|
|
1700
1713
|
fontSize,
|
|
1701
1714
|
lineHeightUnit: "PERCENT",
|
|
@@ -1717,59 +1730,58 @@ function buildFigmaPayloadsFromDS(data) {
|
|
|
1717
1730
|
const variables2 = [];
|
|
1718
1731
|
for (const [key, val] of Object.entries(spacing.scale)) {
|
|
1719
1732
|
const n = tokenValueToNumber(val);
|
|
1720
|
-
if (n >= 0) variables2.push({ name: key
|
|
1733
|
+
if (n >= 0) variables2.push({ name: `Spacing / ${key}`, value: n });
|
|
1721
1734
|
}
|
|
1722
1735
|
variables2.sort((a, b) => a.value - b.value);
|
|
1723
|
-
if (variables2.length > 0) numberVariableCollections.push({ collectionName
|
|
1736
|
+
if (variables2.length > 0) numberVariableCollections.push({ collectionName: `${collectionPrefix}Spacing`, categoryKey: "Spacing", variables: variables2, scopes: ["GAP"] });
|
|
1724
1737
|
}
|
|
1725
1738
|
const radius = tokens?.radius;
|
|
1726
1739
|
if (radius?.scale && typeof radius.scale === "object") {
|
|
1727
1740
|
const variables2 = [];
|
|
1728
1741
|
for (const [key, val] of Object.entries(radius.scale)) {
|
|
1729
1742
|
const n = tokenValueToNumber(val);
|
|
1730
|
-
if (n >= 0) variables2.push({ name: key
|
|
1743
|
+
if (n >= 0) variables2.push({ name: `Radius / ${key}`, value: n });
|
|
1731
1744
|
}
|
|
1732
1745
|
variables2.sort((a, b) => a.value - b.value);
|
|
1733
|
-
if (variables2.length > 0) numberVariableCollections.push({ collectionName
|
|
1746
|
+
if (variables2.length > 0) numberVariableCollections.push({ collectionName: `${collectionPrefix}Radius`, categoryKey: "Radius", variables: variables2, scopes: ["CORNER_RADIUS"] });
|
|
1734
1747
|
}
|
|
1735
1748
|
const borders = tokens?.borders;
|
|
1736
1749
|
if (borders?.width && typeof borders.width === "object") {
|
|
1737
1750
|
const variables2 = [];
|
|
1738
1751
|
for (const [key, val] of Object.entries(borders.width)) {
|
|
1739
1752
|
const n = tokenValueToNumber(val);
|
|
1740
|
-
if (n >= 0) variables2.push({ name: key
|
|
1753
|
+
if (n >= 0) variables2.push({ name: `Borders / ${key}`, value: n });
|
|
1741
1754
|
}
|
|
1742
1755
|
variables2.sort((a, b) => a.value - b.value);
|
|
1743
|
-
if (variables2.length > 0) numberVariableCollections.push({ collectionName
|
|
1756
|
+
if (variables2.length > 0) numberVariableCollections.push({ collectionName: `${collectionPrefix}Borders`, categoryKey: "Borders", variables: variables2, scopes: ["STROKE_FLOAT"] });
|
|
1744
1757
|
}
|
|
1745
1758
|
const sizing = tokens?.sizing;
|
|
1759
|
+
const sizingVariables = [];
|
|
1746
1760
|
if (sizing?.height && typeof sizing.height === "object") {
|
|
1747
|
-
const variables2 = [];
|
|
1748
1761
|
for (const [key, val] of Object.entries(sizing.height)) {
|
|
1749
1762
|
const n = tokenValueToNumber(val);
|
|
1750
|
-
if (n >= 0)
|
|
1763
|
+
if (n >= 0) sizingVariables.push({ name: `Height / ${key}`, value: n });
|
|
1751
1764
|
}
|
|
1752
|
-
variables2.sort((a, b) => a.value - b.value);
|
|
1753
|
-
if (variables2.length > 0) numberVariableCollections.push({ collectionName, categoryKey: "Height", variables: variables2, scopes: ["WIDTH_HEIGHT"] });
|
|
1754
1765
|
}
|
|
1755
1766
|
if (sizing?.icon && typeof sizing.icon === "object") {
|
|
1756
|
-
const variables2 = [];
|
|
1757
1767
|
for (const [key, val] of Object.entries(sizing.icon)) {
|
|
1758
1768
|
const n = tokenValueToNumber(val);
|
|
1759
|
-
if (n >= 0)
|
|
1769
|
+
if (n >= 0) sizingVariables.push({ name: `Icon / ${key}`, value: n });
|
|
1760
1770
|
}
|
|
1761
|
-
|
|
1762
|
-
|
|
1771
|
+
}
|
|
1772
|
+
sizingVariables.sort((a, b) => a.value - b.value);
|
|
1773
|
+
if (sizingVariables.length > 0) {
|
|
1774
|
+
numberVariableCollections.push({ collectionName: `${collectionPrefix}Sizing`, categoryKey: "Sizing", variables: sizingVariables, scopes: ["WIDTH_HEIGHT"] });
|
|
1763
1775
|
}
|
|
1764
1776
|
const layout = tokens?.layout;
|
|
1765
1777
|
if (layout?.breakpoint && typeof layout.breakpoint === "object") {
|
|
1766
1778
|
const variables2 = [];
|
|
1767
1779
|
for (const [key, val] of Object.entries(layout.breakpoint)) {
|
|
1768
1780
|
const n = tokenValueToNumber(val);
|
|
1769
|
-
if (n >= 0) variables2.push({ name: key
|
|
1781
|
+
if (n >= 0) variables2.push({ name: `Breakpoint / ${key}`, value: n });
|
|
1770
1782
|
}
|
|
1771
1783
|
variables2.sort((a, b) => a.value - b.value);
|
|
1772
|
-
if (variables2.length > 0) numberVariableCollections.push({ collectionName
|
|
1784
|
+
if (variables2.length > 0) numberVariableCollections.push({ collectionName: `${collectionPrefix}Layout`, categoryKey: "Layout", variables: variables2, scopes: ["WIDTH_HEIGHT"] });
|
|
1773
1785
|
}
|
|
1774
1786
|
const effectStyles = [];
|
|
1775
1787
|
const shadows = tokens?.shadows;
|
|
@@ -1800,7 +1812,7 @@ function buildFigmaPayloadsFromDS(data) {
|
|
|
1800
1812
|
return nameA.localeCompare(nameB);
|
|
1801
1813
|
});
|
|
1802
1814
|
return {
|
|
1803
|
-
colorVariables: { collectionName, modes, variables },
|
|
1815
|
+
colorVariables: { collectionName: colorCollectionName, modes, variables },
|
|
1804
1816
|
paintStyles,
|
|
1805
1817
|
textStyles,
|
|
1806
1818
|
numberVariableCollections,
|
|
@@ -1810,7 +1822,7 @@ function buildFigmaPayloadsFromDS(data) {
|
|
|
1810
1822
|
function getExpectedFigmaNamesFromDS(data) {
|
|
1811
1823
|
const payloads = buildFigmaPayloadsFromDS(data);
|
|
1812
1824
|
const numberVariableNames = payloads.numberVariableCollections.flatMap(
|
|
1813
|
-
(c) => c.variables.map((v) =>
|
|
1825
|
+
(c) => c.variables.map((v) => v.name)
|
|
1814
1826
|
);
|
|
1815
1827
|
return {
|
|
1816
1828
|
colorVariableNames: payloads.colorVariables.variables.map((v) => v.name),
|
|
@@ -1941,10 +1953,10 @@ async function fetchDesignSystemForMCP(forceRefresh = false) {
|
|
|
1941
1953
|
}
|
|
1942
1954
|
var TOKEN_CATEGORIES = ["colors", "typography", "spacing", "sizing", "shadows", "radius", "borders", "motion", "zIndex"];
|
|
1943
1955
|
function typesetKeyToFontFamilyRole(key) {
|
|
1944
|
-
const prefix = key.split("-")[0] ??
|
|
1945
|
-
if (prefix === "display") return "display";
|
|
1946
|
-
if (prefix === "heading") return "heading";
|
|
1947
|
-
if (prefix === "mono") return "mono";
|
|
1956
|
+
const prefix = (key.split("-")[0] ?? key).toLowerCase();
|
|
1957
|
+
if (prefix === "display" || prefix.startsWith("display")) return "display";
|
|
1958
|
+
if (prefix === "heading" || prefix.startsWith("heading")) return "heading";
|
|
1959
|
+
if (prefix === "mono" || prefix.startsWith("mono")) return "mono";
|
|
1948
1960
|
if (prefix.startsWith("body")) return "body";
|
|
1949
1961
|
return "body";
|
|
1950
1962
|
}
|
|
@@ -1962,12 +1974,15 @@ function buildTypesetsList(typography, cssPrefix = "atmx") {
|
|
|
1962
1974
|
for (const key of Object.keys(fontSize)) {
|
|
1963
1975
|
const role = typesetKeyToFontFamilyRole(key);
|
|
1964
1976
|
const familyName = fontFamily[role] ?? fontFamily.body;
|
|
1965
|
-
const
|
|
1977
|
+
const fontFamilyVarName = familyName ? `--${p}typography-font-family-${role}` : void 0;
|
|
1978
|
+
const fontFamilyVar = familyName ? `var(${fontFamilyVarName})` : "";
|
|
1966
1979
|
const keyKebab = key.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
|
1967
1980
|
typesets.push({
|
|
1968
1981
|
key,
|
|
1969
1982
|
cssClass: `.typeset-${keyKebab}`,
|
|
1970
1983
|
fontFamilyVar: fontFamilyVar || "inherit",
|
|
1984
|
+
fontFamilyVarName,
|
|
1985
|
+
fontFamilyValue: familyName,
|
|
1971
1986
|
fontSizeVar: `var(--${p}typography-${key}-size)`,
|
|
1972
1987
|
fontWeightVar: `var(--${p}typography-${key}-weight)`,
|
|
1973
1988
|
lineHeightVar: `var(--${p}typography-${key}-line-height)`,
|
|
@@ -1983,7 +1998,7 @@ function buildTypesetsList(typography, cssPrefix = "atmx") {
|
|
|
1983
1998
|
var server = new Server(
|
|
1984
1999
|
{
|
|
1985
2000
|
name: "atomix-mcp-user",
|
|
1986
|
-
version: "1.0.
|
|
2001
|
+
version: "1.0.26"
|
|
1987
2002
|
},
|
|
1988
2003
|
{
|
|
1989
2004
|
capabilities: {
|
|
@@ -2652,7 +2667,7 @@ Version: ${designSystemData.meta.version}`,
|
|
|
2652
2667
|
text: JSON.stringify({
|
|
2653
2668
|
count: typesets.length,
|
|
2654
2669
|
typesets,
|
|
2655
|
-
instruction: "Emit one CSS rule per typeset using the cssClass and the listed var() properties. Include font-family, font-size, font-weight, line-height; add letter-spacing when present; add text-transform and text-decoration when hasTextTransform/hasTextDecoration are true so the result is 1:1 with the design system.",
|
|
2670
|
+
instruction: "Emit one CSS rule per typeset using the cssClass and the listed var() properties. Include font-family, font-size, font-weight, line-height; add letter-spacing when present; add text-transform and text-decoration when hasTextTransform/hasTextDecoration are true so the result is 1:1 with the design system. The synced token file quotes font names that contain spaces; no override is needed in the typeset file. For native (iOS/Android) projects, font names with spaces must be quoted or use the platform's canonical font name in theme or resource files.",
|
|
2656
2671
|
...underlineThickness != null && underlineOffset != null && {
|
|
2657
2672
|
underlineVars: {
|
|
2658
2673
|
thickness: `var(--${cssPrefix}-typography-underline-thickness)`,
|
|
@@ -3083,7 +3098,7 @@ ${tokenResponseText}${validationBlock}` : `${summary}${validationBlock}`);
|
|
|
3083
3098
|
showcase: platform2 === "web" || !platform2 ? {
|
|
3084
3099
|
path: "atomix-setup-showcase.html",
|
|
3085
3100
|
template: SHOWCASE_HTML_TEMPLATE,
|
|
3086
|
-
substitutionInstructions:
|
|
3101
|
+
substitutionInstructions: 'Replace placeholders with values from the synced token file. MCP/sync/export use the --atmx- prefix. {{TOKENS_CSS_PATH}} = path to the synced token file (e.g. ./tokens.css, same as syncAll output). {{TYPESETS_LINK}} = if a typeset CSS file was created (e.g. typesets.css), the full tag e.g. <link rel="stylesheet" href="typesets.css">, otherwise empty string. {{DS_NAME}} = design system name. {{BRAND_PRIMARY_VAR}} = var(--atmx-color-brand-primary). {{BRAND_PRIMARY_FOREGROUND_VAR}} = var(--atmx-color-brand-primary-foreground). {{HEADING_FONT_VAR}} = var(--atmx-typography-font-family-heading) or var(--atmx-typography-font-family-display). {{FONT_FAMILY_VAR}} = var(--atmx-typography-font-family-body). {{LARGEST_DISPLAY_TYPESET_CLASS}} = largest display typeset class from listTypesets (display role, largest font size; e.g. typeset-display-2xl or typeset-display-bold), or empty string if no typeset file. {{BODY_TYPESET_CLASS}} = typeset class for body text from listTypesets (e.g. typeset-body-md), or empty string if no typeset file. {{FONT_LINK_TAG}} = Google Fonts <link> for the font, or empty string. Do not invent CSS variable names; use only vars that exist in the export.'
|
|
3087
3102
|
} : void 0,
|
|
3088
3103
|
meta: {
|
|
3089
3104
|
dsName: data.meta.name,
|
|
@@ -3142,11 +3157,10 @@ ${tokenResponseText}${validationBlock}` : `${summary}${validationBlock}`);
|
|
|
3142
3157
|
if (payloads.numberVariableCollections.length > 0) {
|
|
3143
3158
|
const numberResults = [];
|
|
3144
3159
|
try {
|
|
3145
|
-
const foundationsName = payloads.colorVariables.collectionName;
|
|
3146
3160
|
for (const coll of payloads.numberVariableCollections) {
|
|
3147
3161
|
const result = await sendBridgeRequest("create_number_variables", {
|
|
3148
|
-
collectionName:
|
|
3149
|
-
variables: coll.variables.map((v) => ({ name:
|
|
3162
|
+
collectionName: coll.collectionName,
|
|
3163
|
+
variables: coll.variables.map((v) => ({ name: v.name, value: v.value })),
|
|
3150
3164
|
scopes: coll.scopes
|
|
3151
3165
|
});
|
|
3152
3166
|
numberResults.push({ categoryKey: coll.categoryKey, result });
|
|
@@ -3625,6 +3639,7 @@ var SHOWCASE_HTML_TEMPLATE = `<!DOCTYPE html>
|
|
|
3625
3639
|
<title>Setup complete \u2014 {{DS_NAME}}</title>
|
|
3626
3640
|
{{FONT_LINK_TAG}}
|
|
3627
3641
|
<link rel="stylesheet" href="{{TOKENS_CSS_PATH}}">
|
|
3642
|
+
{{TYPESETS_LINK}}
|
|
3628
3643
|
<style>
|
|
3629
3644
|
* { box-sizing: border-box; }
|
|
3630
3645
|
body {
|
|
@@ -3635,9 +3650,11 @@ var SHOWCASE_HTML_TEMPLATE = `<!DOCTYPE html>
|
|
|
3635
3650
|
min-height: 100vh;
|
|
3636
3651
|
padding: 2rem;
|
|
3637
3652
|
display: flex;
|
|
3653
|
+
justify-content: center;
|
|
3654
|
+
align-items: center;
|
|
3638
3655
|
}
|
|
3639
3656
|
.wrap { width: 375px; max-width: 100%; }
|
|
3640
|
-
.icon { width: 2rem; height: 2rem; margin: 0
|
|
3657
|
+
.icon { width: 2rem; height: 2rem; margin: 0 0 1rem; }
|
|
3641
3658
|
h1 {
|
|
3642
3659
|
font-family: {{HEADING_FONT_VAR}}, {{FONT_FAMILY_VAR}}, system-ui, sans-serif;
|
|
3643
3660
|
font-size: clamp(3rem, 4vw, 5rem);
|
|
@@ -3653,12 +3670,12 @@ var SHOWCASE_HTML_TEMPLATE = `<!DOCTYPE html>
|
|
|
3653
3670
|
.tips a { color: inherit; text-decoration: underline; }
|
|
3654
3671
|
</style>
|
|
3655
3672
|
</head>
|
|
3656
|
-
<body>
|
|
3673
|
+
<body class="{{BODY_TYPESET_CLASS}}">
|
|
3657
3674
|
<div class="wrap">
|
|
3658
3675
|
<div class="icon" aria-hidden="true">
|
|
3659
3676
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" width="44" height="44"><path d="M20 6L9 17l-5-5"/></svg>
|
|
3660
3677
|
</div>
|
|
3661
|
-
<h1>You're all set with {{DS_NAME}}</h1>
|
|
3678
|
+
<h1 class="{{LARGEST_DISPLAY_TYPESET_CLASS}}">You're all set with {{DS_NAME}}</h1>
|
|
3662
3679
|
<p class="lead">This page uses your design system: brand primary as background, headline and body typesets, and an icon.</p>
|
|
3663
3680
|
<div class="now">
|
|
3664
3681
|
<strong>What you can do now:</strong>
|
|
@@ -4311,7 +4328,7 @@ Use \`/color\`, \`/spacing\`, \`/radius\`, \`/typography\`, \`/shadow\`, \`/bord
|
|
|
4311
4328
|
- **Token file:** Call **syncAll** with \`output\` set to the path (e.g. "./src/tokens.css" or "./tokens.css"). syncAll also writes skills and atomix-dependencies.json. You must call syncAll; do not only suggest the user run it later.
|
|
4312
4329
|
- **Icon package:** Install per getDependencies. When rendering icons, apply the design system's icon tokens: use getToken(\`sizing.icon.*\`) or listTokens(\`sizing\`) for size, and getToken(\`icons.strokeWidth\`) for stroke width when the DS defines it; do not use hardcoded sizes or stroke widths.
|
|
4313
4330
|
- **Fonts and typeset:** Add font links (e.g. \`<link>\` or \`@import\` from Google Fonts). Then build a **complete typeset CSS**: call **listTypesets** to get every typeset from the owner's design system (do not skip any). Emit **one CSS rule per typeset** using the \`cssClass\` and the \`fontFamilyVar\`, \`fontSizeVar\`, \`fontWeightVar\`, \`lineHeightVar\` (and \`letterSpacingVar\`, \`textTransformVar\`, \`textDecorationVar\` when present) returned by listTypesets. Include text-transform and text-decoration when the typeset has them so the result is **1:1** with the design system. The typeset file must define the full type scale\u2014not only a font import. Do not create a CSS file that contains only a font import.
|
|
4314
|
-
- **Showcase page (web only):** If platform is web and getDependencies returned a \`showcase\` object, create the file at \`showcase.path\` using \`showcase.template\`. Replace every placeholder per \`showcase.substitutionInstructions\`: TOKENS_CSS_PATH, DS_NAME, BRAND_PRIMARY_VAR (page background), BRAND_PRIMARY_FOREGROUND_VAR (text on brand), HEADING_FONT_VAR (h1), FONT_FAMILY_VAR (body), FONT_LINK_TAG. Use only CSS variable names that exist in the synced token file. Do not change the HTML structure. After creating the file, launch it in the default browser (e.g. \`open atomix-setup-showcase.html\` on macOS, \`xdg-open atomix-setup-showcase.html\` on Linux, or the equivalent on Windows).
|
|
4331
|
+
- **Showcase page (web only):** If platform is web and getDependencies returned a \`showcase\` object, create the file at \`showcase.path\` using \`showcase.template\`. Replace every placeholder per \`showcase.substitutionInstructions\`: TOKENS_CSS_PATH, TYPESETS_LINK (if you created a typeset CSS file, use the full \`<link rel="stylesheet" href="\u2026">\` tag; otherwise empty string), DS_NAME, BRAND_PRIMARY_VAR (page background), BRAND_PRIMARY_FOREGROUND_VAR (text on brand), HEADING_FONT_VAR (h1 fallback), FONT_FAMILY_VAR (body fallback), LARGEST_DISPLAY_TYPESET_CLASS (largest display typeset from listTypesets) and BODY_TYPESET_CLASS (e.g. typeset-body-md; leave empty if no typeset file), FONT_LINK_TAG. Use only CSS variable names that exist in the synced token file. Do not change the HTML structure. After creating the file, launch it in the default browser (e.g. \`open atomix-setup-showcase.html\` on macOS, \`xdg-open atomix-setup-showcase.html\` on Linux, or the equivalent on Windows).
|
|
4315
4332
|
- Report only what you actually created or updated. Do not claim the token file was added if you did not call syncAll.
|
|
4316
4333
|
- **After reporting \u2013 styles/theme:**
|
|
4317
4334
|
- **Web:** If the project already has at least one CSS file: recommend how to integrate Atomix (e.g. import the synced tokens file, use \`var(--atmx-*)\`). Do not suggest a new global CSS. Only if there is **no** CSS file at all, ask once: "There are no CSS files yet. Do you want me to build a global typeset from the design system?" If yes, create a CSS file that includes: (1) font \`@import\` or document that a font link is needed, and (2) **typeset rules**\u2014call **listTypesets** and emit **one CSS class per typeset** (do not skip any). For each class set font-family, font-size, font-weight, line-height, letter-spacing; when the typeset has text-transform or text-decoration, set those too for a 1:1 match. Use the CSS variable names returned by listTypesets. The output must not be only a font import; it must define every typeset with every style detail from the design system.
|
|
@@ -4431,6 +4448,8 @@ function onShutdown() {
|
|
|
4431
4448
|
}
|
|
4432
4449
|
process.on("SIGINT", onShutdown);
|
|
4433
4450
|
process.on("SIGTERM", onShutdown);
|
|
4451
|
+
process.stdin.on("end", onShutdown);
|
|
4452
|
+
process.stdin.on("close", onShutdown);
|
|
4434
4453
|
async function main() {
|
|
4435
4454
|
await startServer();
|
|
4436
4455
|
}
|