@digdir/designsystemet 1.7.1 → 1.7.3
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 +9 -1
- package/configs/test-tokens.config.json +4 -0
- package/dist/bin/config.js +9 -4
- package/dist/bin/designsystemet.d.ts.map +1 -1
- package/dist/bin/designsystemet.js +272 -43
- package/dist/config.schema.json +16 -3
- package/dist/src/config.d.ts +12 -0
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/config.js +9 -4
- package/dist/src/index.js +17 -10
- package/dist/src/scripts/createJsonSchema.js +9 -4
- package/dist/src/scripts/update-preview-tokens.js +2 -1
- package/dist/src/tokens/build.js +6 -5
- package/dist/src/tokens/create/generators/$designsystemet.js +6 -5
- package/dist/src/tokens/create/generators/color.d.ts.map +1 -1
- package/dist/src/tokens/create/generators/color.js +2 -1
- package/dist/src/tokens/create/write.js +6 -5
- package/dist/src/tokens/create.js +2 -1
- package/dist/src/tokens/format.js +8 -6
- package/dist/src/tokens/generate-config.d.ts +11 -0
- package/dist/src/tokens/generate-config.d.ts.map +1 -0
- package/dist/src/tokens/generate-config.js +202 -0
- package/dist/src/tokens/index.js +8 -6
- package/dist/src/tokens/process/output/declarations.js +6 -5
- package/dist/src/tokens/process/output/theme.js +6 -5
- package/package.json +6 -5
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ If a [config file](#using-a-config-file) you can also re-run with `"clean": true
|
|
|
26
26
|
> ⚠️ **WARNING** ⚠️
|
|
27
27
|
> The design tokens created by this tool are considered an implementation detail, and is subject
|
|
28
28
|
> to change at any time without being considered a breaking change. We **only** support customisations
|
|
29
|
-
> done through the CLI options. Direct editing of the design tokens are **not** supported.
|
|
29
|
+
> done through the CLI options and config. Direct editing of the design tokens are **not** supported.
|
|
30
30
|
>
|
|
31
31
|
> Since tokens may be added or removed at any time, it is necessary to routinely re-run this
|
|
32
32
|
> command when upgrading the libraries. This will remove any direct edits to the design tokens.
|
|
@@ -105,3 +105,11 @@ npx @digdir/designsystemet tokens build
|
|
|
105
105
|
#### Complex config example
|
|
106
106
|
|
|
107
107
|
Have a look at the `*.config.json` files under the `packages/cli` in the Github repo for more complex examples.
|
|
108
|
+
|
|
109
|
+
#### Create config from existing tokens
|
|
110
|
+
|
|
111
|
+
You can get a minimal config file, meaning without overrides, generated from existing design tokens using the following command:
|
|
112
|
+
|
|
113
|
+
```sh
|
|
114
|
+
npx @digdir/designsystemet generate-config-from-tokens --dir <path to design tokens>
|
|
115
|
+
```
|
package/dist/bin/config.js
CHANGED
|
@@ -387,21 +387,26 @@ var colorCategorySchema = z.record(
|
|
|
387
387
|
error: `Color names cannot include reserved names: ${RESERVED_COLORS.join(", ")}`
|
|
388
388
|
}).describe("An object with one or more color definitions. The property name is used as the color name.");
|
|
389
389
|
var colorModeOverrideSchema = z.object({
|
|
390
|
-
light: colorSchema.optional(),
|
|
391
|
-
dark: colorSchema.optional()
|
|
390
|
+
light: colorSchema.optional().describe("A hex color that overrides light mode"),
|
|
391
|
+
dark: colorSchema.optional().describe("A hex color that overrides dark mode")
|
|
392
392
|
}).describe('Override values for semantic color tokens like "background-subtle", "border-default", etc.');
|
|
393
393
|
var colorWeightOverrideSchema = z.partialRecord(z.enum([...colorNames]), colorModeOverrideSchema).describe('The name of the color to add overrides for, e.g. "accent"');
|
|
394
394
|
var semanticColorOverrideSchema = z.record(z.string(), colorWeightOverrideSchema).describe("An object with color names as keys");
|
|
395
395
|
var severityColorOverrideSchema = z.partialRecord(z.enum(baseColorNames), colorSchema.describe("A hex color, which is used for creating a color scale")).optional().describe("An object with severity color names as keys");
|
|
396
|
+
var linkVisitedOverrideSchema = z.object({
|
|
397
|
+
light: colorSchema.optional().describe("A hex color that overrides light mode"),
|
|
398
|
+
dark: colorSchema.optional().describe("A hex color that overrides dark mode")
|
|
399
|
+
}).describe('Overrides for the "link-visited" color');
|
|
396
400
|
var overridesSchema = z.object({
|
|
397
401
|
colors: semanticColorOverrideSchema.optional(),
|
|
398
|
-
severity: severityColorOverrideSchema.optional()
|
|
402
|
+
severity: severityColorOverrideSchema.optional(),
|
|
403
|
+
linkVisited: linkVisitedOverrideSchema.optional()
|
|
399
404
|
}).describe("Overrides for generated design tokens. Currently only supports colors defined in your theme").optional();
|
|
400
405
|
var themeSchema = z.object({
|
|
401
406
|
colors: z.object({
|
|
402
407
|
main: colorCategorySchema,
|
|
403
408
|
support: colorCategorySchema.optional().default({}),
|
|
404
|
-
neutral: colorSchema
|
|
409
|
+
neutral: colorSchema.describe("A hex color, which is used for creating a color scale.")
|
|
405
410
|
}).meta({ description: "Defines the colors for this theme" }),
|
|
406
411
|
typography: z.object({
|
|
407
412
|
fontFamily: z.string().meta({ description: "Sets the font-family for this theme" })
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"designsystemet.d.ts","sourceRoot":"","sources":["../../bin/designsystemet.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"designsystemet.d.ts","sourceRoot":"","sources":["../../bin/designsystemet.ts"],"names":[],"mappings":";AAeA,eAAO,MAAM,WAAW,4iBASvB,CAAC"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// bin/designsystemet.ts
|
|
4
4
|
import { Argument, createCommand, program } from "@commander-js/extra-typings";
|
|
5
|
-
import
|
|
5
|
+
import pc12 from "picocolors";
|
|
6
6
|
import * as R29 from "ramda";
|
|
7
7
|
|
|
8
8
|
// src/colors/colorMetadata.ts
|
|
@@ -410,13 +410,13 @@ var mkdir = async (dir, dry) => {
|
|
|
410
410
|
}
|
|
411
411
|
return fs.mkdir(dir, { recursive: true });
|
|
412
412
|
};
|
|
413
|
-
var writeFile = async (
|
|
413
|
+
var writeFile = async (path6, data, dry) => {
|
|
414
414
|
if (dry) {
|
|
415
|
-
console.log(`${pc2.blue("writeFile")} ${
|
|
415
|
+
console.log(`${pc2.blue("writeFile")} ${path6}`);
|
|
416
416
|
return Promise.resolve();
|
|
417
417
|
}
|
|
418
|
-
return fs.writeFile(
|
|
419
|
-
console.error(pc2.red(`Error writing file: ${
|
|
418
|
+
return fs.writeFile(path6, data, { encoding: "utf-8" }).catch((error) => {
|
|
419
|
+
console.error(pc2.red(`Error writing file: ${path6}`));
|
|
420
420
|
console.error(pc2.red(error));
|
|
421
421
|
throw error;
|
|
422
422
|
});
|
|
@@ -430,13 +430,13 @@ var cleanDir = async (dir, dry) => {
|
|
|
430
430
|
\u{1F525} Cleaning dir ${pc2.red(`${dir.trim()}`)} `);
|
|
431
431
|
return fs.rm(dir, { recursive: true, force: true });
|
|
432
432
|
};
|
|
433
|
-
var readFile = async (
|
|
433
|
+
var readFile = async (path6, dry, allowFileNotFound) => {
|
|
434
434
|
if (dry) {
|
|
435
|
-
console.log(`${pc2.blue("readFile")} ${
|
|
435
|
+
console.log(`${pc2.blue("readFile")} ${path6}`);
|
|
436
436
|
return Promise.resolve("");
|
|
437
437
|
}
|
|
438
438
|
try {
|
|
439
|
-
return await fs.readFile(
|
|
439
|
+
return await fs.readFile(path6, "utf-8");
|
|
440
440
|
} catch (error) {
|
|
441
441
|
if (allowFileNotFound && error.code === "ENOENT") {
|
|
442
442
|
return "";
|
|
@@ -861,7 +861,7 @@ import pc5 from "picocolors";
|
|
|
861
861
|
// package.json
|
|
862
862
|
var package_default = {
|
|
863
863
|
name: "@digdir/designsystemet",
|
|
864
|
-
version: "1.7.
|
|
864
|
+
version: "1.7.3",
|
|
865
865
|
description: "CLI for Designsystemet",
|
|
866
866
|
author: "Designsystemet team",
|
|
867
867
|
engines: {
|
|
@@ -913,7 +913,8 @@ var package_default = {
|
|
|
913
913
|
"test:tokens-build-config-tailwind": "pnpm run designsystemet tokens build -t ./temp/config/design-tokens -o ./temp/config/build --clean --experimental-tailwind",
|
|
914
914
|
"test:tokens-create-and-build-options": "pnpm test:tokens-create-options && pnpm test:tokens-build",
|
|
915
915
|
"test:tokens-create-and-build-config": "pnpm test:tokens-create-config && pnpm test:tokens-build-config",
|
|
916
|
-
test: "
|
|
916
|
+
"test:generate-config-from-tokens": "pnpm run designsystemet generate-config-from-tokens -d ../../internal/design-tokens --dry",
|
|
917
|
+
test: "node -v && pnpm test:tokens-create-and-build-options && pnpm test:generate-config-from-tokens && pnpm test:tokens-create-and-build-config",
|
|
917
918
|
"digdir:tokens-build": "pnpm run designsystemet tokens build -t ../../internal/design-tokens -o ../../packages/theme/brand --clean --experimental-tailwind",
|
|
918
919
|
"digdir:tokens-create": "pnpm run designsystemet tokens create --config ./configs/digdir.config.json",
|
|
919
920
|
"update:template": "tsx ./src/scripts/update-template.ts",
|
|
@@ -928,7 +929,7 @@ var package_default = {
|
|
|
928
929
|
"change-case": "^5.4.4",
|
|
929
930
|
"chroma-js": "^3.1.2",
|
|
930
931
|
"colorjs.io": "^0.6.0-alpha.1",
|
|
931
|
-
commander: "^14.0.
|
|
932
|
+
commander: "^14.0.2",
|
|
932
933
|
"fast-glob": "^3.3.3",
|
|
933
934
|
hsluv: "^1.0.1",
|
|
934
935
|
"object-hash": "^3.0.0",
|
|
@@ -942,9 +943,9 @@ var package_default = {
|
|
|
942
943
|
devDependencies: {
|
|
943
944
|
"@tokens-studio/types": "0.5.2",
|
|
944
945
|
"@types/apca-w3": "^0.1.3",
|
|
945
|
-
"@types/chroma-js": "^3.1.
|
|
946
|
+
"@types/chroma-js": "^3.1.2",
|
|
946
947
|
"@types/fs-extra": "^11.0.4",
|
|
947
|
-
"@types/node": "^22.
|
|
948
|
+
"@types/node": "^22.19.0",
|
|
948
949
|
"@types/object-hash": "^3.0.6",
|
|
949
950
|
"@types/ramda": "^0.31.1",
|
|
950
951
|
"fs-extra": "^11.3.2",
|
|
@@ -992,8 +993,8 @@ var pathStartsWithOneOf = R5.curry(
|
|
|
992
993
|
}
|
|
993
994
|
const tokenPath = mapToLowerCase(token.path);
|
|
994
995
|
const matchPathsStartingWith = R5.map((pathOrString) => {
|
|
995
|
-
const
|
|
996
|
-
return R5.startsWith(mapToLowerCase(
|
|
996
|
+
const path6 = typeof pathOrString === "string" ? [pathOrString] : pathOrString;
|
|
997
|
+
return R5.startsWith(mapToLowerCase(path6), tokenPath);
|
|
997
998
|
}, paths);
|
|
998
999
|
return hasAnyTruth(matchPathsStartingWith);
|
|
999
1000
|
}
|
|
@@ -2428,8 +2429,8 @@ ${dynamicColors}`;
|
|
|
2428
2429
|
import pc6 from "picocolors";
|
|
2429
2430
|
import * as R20 from "ramda";
|
|
2430
2431
|
var defaultFileHeader2 = `build: v${package_default.version}`;
|
|
2431
|
-
var getFileNameWithoutExtension = (
|
|
2432
|
-
const pathSegments =
|
|
2432
|
+
var getFileNameWithoutExtension = (path6) => {
|
|
2433
|
+
const pathSegments = path6.split("/");
|
|
2433
2434
|
return pathSegments[pathSegments.length - 1].split(".").slice(0, -1).join(".");
|
|
2434
2435
|
};
|
|
2435
2436
|
var createThemeCSSFiles = ({
|
|
@@ -3864,6 +3865,7 @@ var generateColorScheme = (themeName, colorScheme2, colors2, overrides) => {
|
|
|
3864
3865
|
baseColorsWithOverrides
|
|
3865
3866
|
);
|
|
3866
3867
|
const linkColor = generateColor(generateColorScale(dsLinkColor, colorScheme2));
|
|
3868
|
+
const linkOverride = overrides?.linkVisited?.[colorScheme2] ? { $type: "color", $value: overrides.linkVisited[colorScheme2] } : void 0;
|
|
3867
3869
|
return {
|
|
3868
3870
|
[themeName]: {
|
|
3869
3871
|
...main,
|
|
@@ -3871,7 +3873,7 @@ var generateColorScheme = (themeName, colorScheme2, colors2, overrides) => {
|
|
|
3871
3873
|
neutral,
|
|
3872
3874
|
...globalColors,
|
|
3873
3875
|
link: {
|
|
3874
|
-
visited: linkColor[12]
|
|
3876
|
+
visited: linkOverride || linkColor[12]
|
|
3875
3877
|
}
|
|
3876
3878
|
}
|
|
3877
3879
|
};
|
|
@@ -4346,18 +4348,218 @@ var createTokens = async (opts) => {
|
|
|
4346
4348
|
return { tokenSets };
|
|
4347
4349
|
};
|
|
4348
4350
|
|
|
4351
|
+
// src/tokens/generate-config.ts
|
|
4352
|
+
import fs3 from "fs/promises";
|
|
4353
|
+
import path3 from "path";
|
|
4354
|
+
import pc9 from "picocolors";
|
|
4355
|
+
async function readJsonFile(filePath) {
|
|
4356
|
+
try {
|
|
4357
|
+
const content = await fs3.readFile(filePath, "utf-8");
|
|
4358
|
+
return JSON.parse(content);
|
|
4359
|
+
} catch (err) {
|
|
4360
|
+
throw new Error(`Failed to read token file at ${filePath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
4361
|
+
}
|
|
4362
|
+
}
|
|
4363
|
+
function extractBaseColor(colorScale) {
|
|
4364
|
+
if ("12" in colorScale && typeof colorScale["12"] === "object" && "$value" in colorScale["12"]) {
|
|
4365
|
+
const token = colorScale["12"];
|
|
4366
|
+
if (token.$type === "color") {
|
|
4367
|
+
return token.$value;
|
|
4368
|
+
}
|
|
4369
|
+
}
|
|
4370
|
+
return null;
|
|
4371
|
+
}
|
|
4372
|
+
async function discoverThemes(tokensDir) {
|
|
4373
|
+
const lightModePath = path3.join(tokensDir, "themes");
|
|
4374
|
+
try {
|
|
4375
|
+
const files = await fs3.readdir(lightModePath);
|
|
4376
|
+
const themes = files.filter((file) => file.endsWith(".json")).map((file) => file.replace(".json", ""));
|
|
4377
|
+
return themes;
|
|
4378
|
+
} catch {
|
|
4379
|
+
throw new Error(`Could not find themes. Make sure ${pc9.blue(lightModePath)} exists and contains theme JSON files.`);
|
|
4380
|
+
}
|
|
4381
|
+
}
|
|
4382
|
+
async function readThemeTokens(tokensDir, themeName) {
|
|
4383
|
+
const themePath = path3.join(tokensDir, "primitives", "modes", "color-scheme", "light", `${themeName}.json`);
|
|
4384
|
+
return readJsonFile(themePath);
|
|
4385
|
+
}
|
|
4386
|
+
async function readThemeConfig(tokensDir, themeName) {
|
|
4387
|
+
const themeConfigPath = path3.join(tokensDir, "themes", `${themeName}.json`);
|
|
4388
|
+
try {
|
|
4389
|
+
return await readJsonFile(themeConfigPath);
|
|
4390
|
+
} catch {
|
|
4391
|
+
return null;
|
|
4392
|
+
}
|
|
4393
|
+
}
|
|
4394
|
+
function extractBorderRadius(themeConfig) {
|
|
4395
|
+
if (!themeConfig || !("border-radius" in themeConfig)) {
|
|
4396
|
+
return void 0;
|
|
4397
|
+
}
|
|
4398
|
+
const borderRadius = themeConfig["border-radius"];
|
|
4399
|
+
if ("base" in borderRadius && typeof borderRadius.base === "object" && "$value" in borderRadius.base) {
|
|
4400
|
+
const token = borderRadius.base;
|
|
4401
|
+
return Number(token.$value);
|
|
4402
|
+
}
|
|
4403
|
+
return void 0;
|
|
4404
|
+
}
|
|
4405
|
+
function extractFontFamily(themeConfig) {
|
|
4406
|
+
if (!themeConfig || !("font-family" in themeConfig)) {
|
|
4407
|
+
return void 0;
|
|
4408
|
+
}
|
|
4409
|
+
const fontFamily = themeConfig["font-family"];
|
|
4410
|
+
if (typeof fontFamily === "object" && "$value" in fontFamily) {
|
|
4411
|
+
const token = fontFamily;
|
|
4412
|
+
const value = token.$value;
|
|
4413
|
+
if (value.startsWith("{") && value.endsWith("}")) {
|
|
4414
|
+
return void 0;
|
|
4415
|
+
}
|
|
4416
|
+
return value;
|
|
4417
|
+
}
|
|
4418
|
+
return void 0;
|
|
4419
|
+
}
|
|
4420
|
+
async function readTypographyConfig(tokensDir, themeName) {
|
|
4421
|
+
const typographyConfigPath = path3.join(
|
|
4422
|
+
tokensDir,
|
|
4423
|
+
"primitives",
|
|
4424
|
+
"modes",
|
|
4425
|
+
"typography",
|
|
4426
|
+
"primary",
|
|
4427
|
+
`${themeName}.json`
|
|
4428
|
+
);
|
|
4429
|
+
try {
|
|
4430
|
+
return await readJsonFile(typographyConfigPath);
|
|
4431
|
+
} catch {
|
|
4432
|
+
return null;
|
|
4433
|
+
}
|
|
4434
|
+
}
|
|
4435
|
+
function extractFontFamilyFromPrimitives(typographyConfig, themeName) {
|
|
4436
|
+
if (!typographyConfig) {
|
|
4437
|
+
return void 0;
|
|
4438
|
+
}
|
|
4439
|
+
const themeTypography = typographyConfig[themeName];
|
|
4440
|
+
if (!themeTypography || !("font-family" in themeTypography)) {
|
|
4441
|
+
return void 0;
|
|
4442
|
+
}
|
|
4443
|
+
const fontFamily = themeTypography["font-family"];
|
|
4444
|
+
if (typeof fontFamily === "object" && "$value" in fontFamily) {
|
|
4445
|
+
const token = fontFamily;
|
|
4446
|
+
return token.$value;
|
|
4447
|
+
}
|
|
4448
|
+
return void 0;
|
|
4449
|
+
}
|
|
4450
|
+
function categorizeColors(themeTokens, themeName) {
|
|
4451
|
+
const main = {};
|
|
4452
|
+
const support = {};
|
|
4453
|
+
let neutral = null;
|
|
4454
|
+
const builtInColors = ["neutral", "info", "success", "warning", "danger"];
|
|
4455
|
+
const specialKeys = ["link"];
|
|
4456
|
+
const themeColors = themeTokens[themeName];
|
|
4457
|
+
if (!themeColors) {
|
|
4458
|
+
return { main, support, neutral };
|
|
4459
|
+
}
|
|
4460
|
+
for (const [colorName, colorValue] of Object.entries(themeColors)) {
|
|
4461
|
+
if (specialKeys.includes(colorName)) {
|
|
4462
|
+
continue;
|
|
4463
|
+
}
|
|
4464
|
+
if (typeof colorValue === "object" && !("$value" in colorValue)) {
|
|
4465
|
+
const baseColor = extractBaseColor(colorValue);
|
|
4466
|
+
if (baseColor) {
|
|
4467
|
+
if (colorName === "neutral") {
|
|
4468
|
+
neutral = baseColor;
|
|
4469
|
+
} else if (builtInColors.includes(colorName)) {
|
|
4470
|
+
} else if (colorName === "accent") {
|
|
4471
|
+
main[colorName] = baseColor;
|
|
4472
|
+
} else {
|
|
4473
|
+
support[colorName] = baseColor;
|
|
4474
|
+
}
|
|
4475
|
+
}
|
|
4476
|
+
}
|
|
4477
|
+
}
|
|
4478
|
+
return { main, support, neutral };
|
|
4479
|
+
}
|
|
4480
|
+
async function generateConfigFromTokens(options) {
|
|
4481
|
+
const { tokensDir, dry = false } = options;
|
|
4482
|
+
console.log(`
|
|
4483
|
+
Reading tokens from ${pc9.blue(tokensDir)}`);
|
|
4484
|
+
const themes = await discoverThemes(tokensDir);
|
|
4485
|
+
if (themes.length === 0) {
|
|
4486
|
+
throw new Error(`
|
|
4487
|
+
No themes found in ${pc9.blue(tokensDir)}`);
|
|
4488
|
+
}
|
|
4489
|
+
console.log(`
|
|
4490
|
+
Found ${pc9.green(String(themes.length))} theme(s): ${themes.map((t) => pc9.cyan(t)).join(", ")}`);
|
|
4491
|
+
const config = {
|
|
4492
|
+
outDir: tokensDir,
|
|
4493
|
+
themes: {}
|
|
4494
|
+
};
|
|
4495
|
+
for (const themeName of themes) {
|
|
4496
|
+
console.log(`
|
|
4497
|
+
Processing theme ${pc9.cyan(themeName)}...`);
|
|
4498
|
+
const themeTokens = await readThemeTokens(tokensDir, themeName);
|
|
4499
|
+
const themeConfig = await readThemeConfig(tokensDir, themeName);
|
|
4500
|
+
const typographyConfig = await readTypographyConfig(tokensDir, themeName);
|
|
4501
|
+
const { main, support, neutral } = categorizeColors(themeTokens, themeName);
|
|
4502
|
+
if (Object.keys(main).length === 0) {
|
|
4503
|
+
console.warn(pc9.yellow(`
|
|
4504
|
+
Warning: No main colors found for theme ${themeName}`));
|
|
4505
|
+
}
|
|
4506
|
+
if (!neutral) {
|
|
4507
|
+
console.warn(pc9.yellow(`
|
|
4508
|
+
Warning: No neutral color found for theme ${themeName}`));
|
|
4509
|
+
continue;
|
|
4510
|
+
}
|
|
4511
|
+
const borderRadius = extractBorderRadius(themeConfig);
|
|
4512
|
+
const fontFamily = extractFontFamily(themeConfig) ?? extractFontFamilyFromPrimitives(typographyConfig, themeName);
|
|
4513
|
+
config.themes[themeName] = {
|
|
4514
|
+
colors: {
|
|
4515
|
+
main,
|
|
4516
|
+
support,
|
|
4517
|
+
neutral
|
|
4518
|
+
},
|
|
4519
|
+
borderRadius,
|
|
4520
|
+
typography: fontFamily ? { fontFamily } : void 0
|
|
4521
|
+
};
|
|
4522
|
+
console.log(
|
|
4523
|
+
`
|
|
4524
|
+
\u2705 Main colors: ${Object.keys(main).map((c) => pc9.cyan(c)).join(", ") || pc9.dim("none")}`
|
|
4525
|
+
);
|
|
4526
|
+
console.log(
|
|
4527
|
+
`
|
|
4528
|
+
\u2705 Support colors: ${Object.keys(support).map((c) => pc9.cyan(c)).join(", ") || pc9.dim("none")}`
|
|
4529
|
+
);
|
|
4530
|
+
console.log(`
|
|
4531
|
+
\u2705 Neutral: ${pc9.cyan(neutral)}`);
|
|
4532
|
+
if (borderRadius !== void 0) {
|
|
4533
|
+
console.log(`
|
|
4534
|
+
\u2705 Border radius: ${pc9.cyan(String(borderRadius))}`);
|
|
4535
|
+
}
|
|
4536
|
+
if (fontFamily) {
|
|
4537
|
+
console.log(`
|
|
4538
|
+
\u2705 Font family: ${pc9.cyan(fontFamily)}`);
|
|
4539
|
+
}
|
|
4540
|
+
}
|
|
4541
|
+
if (!dry && options.outFile) {
|
|
4542
|
+
const configJson = JSON.stringify(config, null, 2);
|
|
4543
|
+
await fs3.writeFile(options.outFile, configJson, "utf-8");
|
|
4544
|
+
console.log();
|
|
4545
|
+
console.log(`
|
|
4546
|
+
\u2705 Config file written to ${pc9.blue(options.outFile)}`);
|
|
4547
|
+
}
|
|
4548
|
+
return config;
|
|
4549
|
+
}
|
|
4550
|
+
|
|
4349
4551
|
// bin/config.ts
|
|
4350
|
-
import
|
|
4351
|
-
import
|
|
4552
|
+
import path5 from "path";
|
|
4553
|
+
import pc11 from "picocolors";
|
|
4352
4554
|
import * as R28 from "ramda";
|
|
4353
4555
|
|
|
4354
4556
|
// src/config.ts
|
|
4355
|
-
import
|
|
4557
|
+
import pc10 from "picocolors";
|
|
4356
4558
|
import * as R27 from "ramda";
|
|
4357
4559
|
import { z } from "zod";
|
|
4358
4560
|
import { fromError } from "zod-validation-error";
|
|
4359
|
-
function mapPathToOptionName(
|
|
4360
|
-
const normalisedPath =
|
|
4561
|
+
function mapPathToOptionName(path6) {
|
|
4562
|
+
const normalisedPath = path6[0] === "themes" ? ["theme", ...R27.drop(2, path6)] : path6;
|
|
4361
4563
|
const option = R27.path(normalisedPath, cliOptions);
|
|
4362
4564
|
if (typeof option !== "string") {
|
|
4363
4565
|
return;
|
|
@@ -4372,12 +4574,12 @@ function makeFriendlyError(err) {
|
|
|
4372
4574
|
const optionName = mapPathToOptionName(issue.path);
|
|
4373
4575
|
const errorCode = `(error code: ${issue.code})`;
|
|
4374
4576
|
const optionMessage = optionName ? ` or CLI option --${optionName}` : "";
|
|
4375
|
-
return ` - Error in JSON value ${
|
|
4376
|
-
${issue.message} ${
|
|
4577
|
+
return ` - Error in JSON value ${pc10.red(issuePath)}${optionMessage}:
|
|
4578
|
+
${issue.message} ${pc10.dim(errorCode)}`;
|
|
4377
4579
|
}).join("\n")
|
|
4378
4580
|
});
|
|
4379
4581
|
} catch (_err2) {
|
|
4380
|
-
console.error(
|
|
4582
|
+
console.error(pc10.red(err instanceof Error ? err.message : "Unknown error occurred while parsing config file"));
|
|
4381
4583
|
console.error(err instanceof Error ? err.stack : "No stack trace available");
|
|
4382
4584
|
}
|
|
4383
4585
|
}
|
|
@@ -4385,7 +4587,7 @@ function validateConfig(schema, unvalidatedConfig, configPath) {
|
|
|
4385
4587
|
try {
|
|
4386
4588
|
return schema.parse(unvalidatedConfig);
|
|
4387
4589
|
} catch (err) {
|
|
4388
|
-
console.error(
|
|
4590
|
+
console.error(pc10.redBright(`Invalid config file at ${pc10.red(configPath)}`));
|
|
4389
4591
|
const validationError = makeFriendlyError(err);
|
|
4390
4592
|
console.error(validationError?.toString());
|
|
4391
4593
|
process.exit(1);
|
|
@@ -4398,7 +4600,7 @@ function parseConfig(configFile, configPath) {
|
|
|
4398
4600
|
try {
|
|
4399
4601
|
return JSON.parse(configFile);
|
|
4400
4602
|
} catch (err) {
|
|
4401
|
-
console.error(
|
|
4603
|
+
console.error(pc10.redBright(`Failed parsing config file at ${pc10.red(configPath)}`));
|
|
4402
4604
|
const validationError = makeFriendlyError(err);
|
|
4403
4605
|
console.error(validationError?.toString());
|
|
4404
4606
|
process.exit(1);
|
|
@@ -4428,21 +4630,26 @@ var colorCategorySchema = z.record(
|
|
|
4428
4630
|
error: `Color names cannot include reserved names: ${RESERVED_COLORS.join(", ")}`
|
|
4429
4631
|
}).describe("An object with one or more color definitions. The property name is used as the color name.");
|
|
4430
4632
|
var colorModeOverrideSchema = z.object({
|
|
4431
|
-
light: colorSchema.optional(),
|
|
4432
|
-
dark: colorSchema.optional()
|
|
4633
|
+
light: colorSchema.optional().describe("A hex color that overrides light mode"),
|
|
4634
|
+
dark: colorSchema.optional().describe("A hex color that overrides dark mode")
|
|
4433
4635
|
}).describe('Override values for semantic color tokens like "background-subtle", "border-default", etc.');
|
|
4434
4636
|
var colorWeightOverrideSchema = z.partialRecord(z.enum([...colorNames]), colorModeOverrideSchema).describe('The name of the color to add overrides for, e.g. "accent"');
|
|
4435
4637
|
var semanticColorOverrideSchema = z.record(z.string(), colorWeightOverrideSchema).describe("An object with color names as keys");
|
|
4436
4638
|
var severityColorOverrideSchema = z.partialRecord(z.enum(baseColorNames), colorSchema.describe("A hex color, which is used for creating a color scale")).optional().describe("An object with severity color names as keys");
|
|
4639
|
+
var linkVisitedOverrideSchema = z.object({
|
|
4640
|
+
light: colorSchema.optional().describe("A hex color that overrides light mode"),
|
|
4641
|
+
dark: colorSchema.optional().describe("A hex color that overrides dark mode")
|
|
4642
|
+
}).describe('Overrides for the "link-visited" color');
|
|
4437
4643
|
var overridesSchema = z.object({
|
|
4438
4644
|
colors: semanticColorOverrideSchema.optional(),
|
|
4439
|
-
severity: severityColorOverrideSchema.optional()
|
|
4645
|
+
severity: severityColorOverrideSchema.optional(),
|
|
4646
|
+
linkVisited: linkVisitedOverrideSchema.optional()
|
|
4440
4647
|
}).describe("Overrides for generated design tokens. Currently only supports colors defined in your theme").optional();
|
|
4441
4648
|
var themeSchema = z.object({
|
|
4442
4649
|
colors: z.object({
|
|
4443
4650
|
main: colorCategorySchema,
|
|
4444
4651
|
support: colorCategorySchema.optional().default({}),
|
|
4445
|
-
neutral: colorSchema
|
|
4652
|
+
neutral: colorSchema.describe("A hex color, which is used for creating a color scale.")
|
|
4446
4653
|
}).meta({ description: "Defines the colors for this theme" }),
|
|
4447
4654
|
typography: z.object({
|
|
4448
4655
|
fontFamily: z.string().meta({ description: "Sets the font-family for this theme" })
|
|
@@ -4474,7 +4681,7 @@ var getCliOption = getOptionIfMatchingSource("cli", "default");
|
|
|
4474
4681
|
|
|
4475
4682
|
// bin/config.ts
|
|
4476
4683
|
async function readConfigFile(configPath, allowFileNotFound = true) {
|
|
4477
|
-
const resolvedPath =
|
|
4684
|
+
const resolvedPath = path5.resolve(process.cwd(), configPath);
|
|
4478
4685
|
let configFile;
|
|
4479
4686
|
try {
|
|
4480
4687
|
configFile = await readFile(resolvedPath, false, allowFileNotFound);
|
|
@@ -4482,11 +4689,11 @@ async function readConfigFile(configPath, allowFileNotFound = true) {
|
|
|
4482
4689
|
if (allowFileNotFound) {
|
|
4483
4690
|
return "";
|
|
4484
4691
|
}
|
|
4485
|
-
console.error(
|
|
4692
|
+
console.error(pc11.redBright(`Could not read config file at ${pc11.blue(resolvedPath)}`));
|
|
4486
4693
|
throw err;
|
|
4487
4694
|
}
|
|
4488
4695
|
if (configFile) {
|
|
4489
|
-
console.log(`Found config file: ${
|
|
4696
|
+
console.log(`Found config file: ${pc11.green(resolvedPath)}`);
|
|
4490
4697
|
}
|
|
4491
4698
|
return configFile;
|
|
4492
4699
|
}
|
|
@@ -4497,7 +4704,7 @@ async function parseCreateConfig(configFile, options) {
|
|
|
4497
4704
|
(x) => /* @__PURE__ */ new Set([...R28.keys(x.colors.main), ...R28.keys(x.colors.support)])
|
|
4498
4705
|
);
|
|
4499
4706
|
if (!R28.all(R28.equals(R28.__, themeColors[0]), themeColors)) {
|
|
4500
|
-
console.error(
|
|
4707
|
+
console.error(pc11.redBright(`In config, all themes must have the same custom color names, but we found:`));
|
|
4501
4708
|
const themeNames = R28.keys(configParsed.themes ?? {});
|
|
4502
4709
|
themeColors.forEach((colors2, index) => {
|
|
4503
4710
|
const colorNames2 = Array.from(colors2);
|
|
@@ -4561,11 +4768,11 @@ var DEFAULT_THEME_NAME = "theme";
|
|
|
4561
4768
|
var DEFAULT_CONFIG_FILE = "designsystemet.config.json";
|
|
4562
4769
|
function makeTokenCommands() {
|
|
4563
4770
|
const tokenCmd = createCommand("tokens");
|
|
4564
|
-
tokenCmd.command("build").description("Build Designsystemet tokens").option("-t, --tokens <string>", `Path to ${
|
|
4771
|
+
tokenCmd.command("build").description("Build Designsystemet tokens").option("-t, --tokens <string>", `Path to ${pc12.blue("design-tokens")}`, DEFAULT_TOKENS_CREATE_DIR).option(
|
|
4565
4772
|
"-o, --out-dir <string>",
|
|
4566
|
-
`Output directory for built ${
|
|
4773
|
+
`Output directory for built ${pc12.blue("design-tokens")}`,
|
|
4567
4774
|
DEFAULT_TOKENS_BUILD_DIR
|
|
4568
|
-
).option(`--${cliOptions.clean} [boolean]`, "Clean output directory before building tokens", parseBoolean, false).option("--dry [boolean]", `Dry run for built ${
|
|
4775
|
+
).option(`--${cliOptions.clean} [boolean]`, "Clean output directory before building tokens", parseBoolean, false).option("--dry [boolean]", `Dry run for built ${pc12.blue("design-tokens")}`, parseBoolean, false).option("--verbose", "Enable verbose output", false).option("--config <string>", `Path to config file (default: "${DEFAULT_CONFIG_FILE}")`).option("--experimental-tailwind", "Generate Tailwind CSS classes for tokens", false).action(async (opts) => {
|
|
4569
4776
|
console.log(figletAscii);
|
|
4570
4777
|
const { verbose, clean, dry, experimentalTailwind } = opts;
|
|
4571
4778
|
const tokensDir = typeof opts.tokens === "string" ? opts.tokens : DEFAULT_TOKENS_CREATE_DIR;
|
|
@@ -4583,9 +4790,9 @@ function makeTokenCommands() {
|
|
|
4583
4790
|
});
|
|
4584
4791
|
tokenCmd.command("create").description("Create Designsystemet tokens").option(`-m, --${cliOptions.theme.colors.main} <name:hex...>`, `Main colors`, parseColorValues).option(`-s, --${cliOptions.theme.colors.support} <name:hex...>`, `Support colors`, parseColorValues).option(`-n, --${cliOptions.theme.colors.neutral} <hex>`, `Neutral hex color`, convertToHex).option(
|
|
4585
4792
|
`-o, --${cliOptions.outDir} <string>`,
|
|
4586
|
-
`Output directory for created ${
|
|
4793
|
+
`Output directory for created ${pc12.blue("design-tokens")}`,
|
|
4587
4794
|
DEFAULT_TOKENS_CREATE_DIR
|
|
4588
|
-
).option(`--${cliOptions.clean} [boolean]`, "Clean output directory before creating tokens", parseBoolean, false).option("--dry [boolean]", `Dry run for created ${
|
|
4795
|
+
).option(`--${cliOptions.clean} [boolean]`, "Clean output directory before creating tokens", parseBoolean, false).option("--dry [boolean]", `Dry run for created ${pc12.blue("design-tokens")}`, parseBoolean, false).option(`-f, --${cliOptions.theme.typography.fontFamily} <string>`, `Font family (experimental)`, DEFAULT_FONT).option(
|
|
4589
4796
|
`-b, --${cliOptions.theme.borderRadius} <number>`,
|
|
4590
4797
|
`Unitless base border-radius in px`,
|
|
4591
4798
|
(radiusAsString) => Number(radiusAsString),
|
|
@@ -4615,6 +4822,28 @@ function makeTokenCommands() {
|
|
|
4615
4822
|
return tokenCmd;
|
|
4616
4823
|
}
|
|
4617
4824
|
program.addCommand(makeTokenCommands());
|
|
4825
|
+
program.command("generate-config-from-tokens").description("Generate a config file from existing design tokens. Will not include overrides.").option("-d, --dir <string>", "Path to design tokens directory", DEFAULT_TOKENS_CREATE_DIR).option("-o, --out <string>", "Output path for config file", DEFAULT_CONFIG_FILE).option("--dry [boolean]", "Dry run - show config without writing file", parseBoolean, false).action(async (opts) => {
|
|
4826
|
+
console.log(figletAscii);
|
|
4827
|
+
const { dry } = opts;
|
|
4828
|
+
const tokensDir = typeof opts.dir === "string" ? opts.dir : DEFAULT_TOKENS_CREATE_DIR;
|
|
4829
|
+
const outFile = typeof opts.out === "string" ? opts.out : DEFAULT_CONFIG_FILE;
|
|
4830
|
+
try {
|
|
4831
|
+
const config = await generateConfigFromTokens({
|
|
4832
|
+
tokensDir,
|
|
4833
|
+
outFile: dry ? void 0 : outFile,
|
|
4834
|
+
dry
|
|
4835
|
+
});
|
|
4836
|
+
if (dry) {
|
|
4837
|
+
console.log();
|
|
4838
|
+
console.log("Generated config (dry run):");
|
|
4839
|
+
console.log(JSON.stringify(config, null, 2));
|
|
4840
|
+
}
|
|
4841
|
+
} catch (error) {
|
|
4842
|
+
console.error(pc12.redBright("Error generating config:"));
|
|
4843
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
4844
|
+
process.exit(1);
|
|
4845
|
+
}
|
|
4846
|
+
});
|
|
4618
4847
|
program.command("migrate").description("run a Designsystemet migration").addArgument(new Argument("[migration]", "Available migrations").choices(Object.keys(migrations_default))).option("-l --list", "List available migrations").option("-g --glob <glob>", "Glob for files upon which to apply the migration", "./**/*.(tsx|css)").action((migrationKey, opts) => {
|
|
4619
4848
|
console.log(figletAscii);
|
|
4620
4849
|
const { glob: glob2, list } = opts;
|
|
@@ -4628,8 +4857,8 @@ program.command("migrate").description("run a Designsystemet migration").addArgu
|
|
|
4628
4857
|
console.error("Migration not found!");
|
|
4629
4858
|
throw "Aborting";
|
|
4630
4859
|
}
|
|
4631
|
-
console.log(`Applying migration ${
|
|
4632
|
-
migration?.(glob2).then(() => console.log(`Migration ${
|
|
4860
|
+
console.log(`Applying migration ${pc12.blue(migrationKey)} with glob: ${pc12.green(glob2)}`);
|
|
4861
|
+
migration?.(glob2).then(() => console.log(`Migration ${pc12.blue(migrationKey)} finished`)).catch((error) => console.log(error));
|
|
4633
4862
|
} else {
|
|
4634
4863
|
console.log("Migrate: please specify a migration name or --list");
|
|
4635
4864
|
}
|
package/dist/config.schema.json
CHANGED
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
}
|
|
48
48
|
},
|
|
49
49
|
"neutral": {
|
|
50
|
-
"description": "A hex color, which is used for creating a color scale.
|
|
50
|
+
"description": "A hex color, which is used for creating a color scale."
|
|
51
51
|
}
|
|
52
52
|
},
|
|
53
53
|
"required": [
|
|
@@ -114,10 +114,10 @@
|
|
|
114
114
|
"type": "object",
|
|
115
115
|
"properties": {
|
|
116
116
|
"light": {
|
|
117
|
-
"description": "A hex color
|
|
117
|
+
"description": "A hex color that overrides light mode"
|
|
118
118
|
},
|
|
119
119
|
"dark": {
|
|
120
|
-
"description": "A hex color
|
|
120
|
+
"description": "A hex color that overrides dark mode"
|
|
121
121
|
}
|
|
122
122
|
},
|
|
123
123
|
"additionalProperties": false
|
|
@@ -139,6 +139,19 @@
|
|
|
139
139
|
"additionalProperties": {
|
|
140
140
|
"description": "A hex color, which is used for creating a color scale"
|
|
141
141
|
}
|
|
142
|
+
},
|
|
143
|
+
"linkVisited": {
|
|
144
|
+
"description": "Overrides for the \"link-visited\" color",
|
|
145
|
+
"type": "object",
|
|
146
|
+
"properties": {
|
|
147
|
+
"light": {
|
|
148
|
+
"description": "A hex color that overrides light mode"
|
|
149
|
+
},
|
|
150
|
+
"dark": {
|
|
151
|
+
"description": "A hex color that overrides dark mode"
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
"additionalProperties": false
|
|
142
155
|
}
|
|
143
156
|
},
|
|
144
157
|
"additionalProperties": false
|
package/dist/src/config.d.ts
CHANGED
|
@@ -39,6 +39,10 @@ declare const overridesSchema: z.ZodOptional<z.ZodObject<{
|
|
|
39
39
|
success: "success";
|
|
40
40
|
warning: "warning";
|
|
41
41
|
}> & z.core.$partial, z.ZodPipe<z.ZodString, z.ZodTransform<`#${string}`, string>>>>>;
|
|
42
|
+
linkVisited: z.ZodOptional<z.ZodObject<{
|
|
43
|
+
light: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<`#${string}`, string>>>;
|
|
44
|
+
dark: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<`#${string}`, string>>>;
|
|
45
|
+
}, z.core.$strip>>;
|
|
42
46
|
}, z.core.$strip>>;
|
|
43
47
|
declare const themeSchema: z.ZodObject<{
|
|
44
48
|
colors: z.ZodObject<{
|
|
@@ -78,6 +82,10 @@ declare const themeSchema: z.ZodObject<{
|
|
|
78
82
|
success: "success";
|
|
79
83
|
warning: "warning";
|
|
80
84
|
}> & z.core.$partial, z.ZodPipe<z.ZodString, z.ZodTransform<`#${string}`, string>>>>>;
|
|
85
|
+
linkVisited: z.ZodOptional<z.ZodObject<{
|
|
86
|
+
light: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<`#${string}`, string>>>;
|
|
87
|
+
dark: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<`#${string}`, string>>>;
|
|
88
|
+
}, z.core.$strip>>;
|
|
81
89
|
}, z.core.$strip>>;
|
|
82
90
|
}, z.core.$strip>;
|
|
83
91
|
export declare const commonConfig: z.ZodObject<{
|
|
@@ -126,6 +134,10 @@ export declare const configFileCreateSchema: z.ZodObject<{
|
|
|
126
134
|
success: "success";
|
|
127
135
|
warning: "warning";
|
|
128
136
|
}> & z.core.$partial, z.ZodPipe<z.ZodString, z.ZodTransform<`#${string}`, string>>>>>;
|
|
137
|
+
linkVisited: z.ZodOptional<z.ZodObject<{
|
|
138
|
+
light: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<`#${string}`, string>>>;
|
|
139
|
+
dark: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<`#${string}`, string>>>;
|
|
140
|
+
}, z.core.$strip>>;
|
|
129
141
|
}, z.core.$strip>>;
|
|
130
142
|
}, z.core.$strip>>>;
|
|
131
143
|
clean: z.ZodOptional<z.ZodBoolean>;
|
package/dist/src/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAuCxB;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAC9B,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EACpB,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC1C,UAAU,EAAE,MAAM,GACjB,CAAC,CAUH;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,CAAC,CAexE;AAWD,eAAO,MAAM,UAAU,QAA2C,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAuCxB;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAC9B,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EACpB,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC1C,UAAU,EAAE,MAAM,GACjB,CAAC,CAUH;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,CAAC,CAexE;AAWD,eAAO,MAAM,UAAU,QAA2C,CAAC;AAoDnE,QAAA,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAOR,CAAC;AAEd,QAAA,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAkBmG,CAAC;AAErH,eAAO,MAAM,YAAY;;iBAEvB,CAAC;AAYH;;GAEG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAqD,CAAC;AACzF,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAC9D,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAC7D,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AACxE,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAC5D,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC"}
|