igniteui-theming 25.1.0 → 25.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +75 -0
- package/dist/index.js +12 -0
- package/dist/json/components/bootstrap.json +1 -0
- package/dist/json/components/fluent.json +1 -0
- package/dist/json/components/indigo.json +1 -0
- package/dist/json/components/material.json +1 -0
- package/{json → dist/json}/components/themes.json +31 -1
- package/dist/mcp/generators/css.d.ts +7 -4
- package/dist/mcp/generators/css.js +129 -104
- package/dist/mcp/generators/sass.js +227 -254
- package/dist/mcp/index.js +259 -323
- package/dist/mcp/knowledge/color-usage.js +524 -502
- package/dist/mcp/knowledge/colors.js +61 -50
- package/dist/mcp/knowledge/component-metadata.js +697 -598
- package/dist/mcp/knowledge/component-themes.js +70 -57
- package/dist/mcp/knowledge/custom-palettes.js +4 -9
- package/dist/mcp/knowledge/docs/colors/guidance.js +4 -0
- package/dist/mcp/knowledge/docs/colors/usage.js +4 -0
- package/dist/mcp/knowledge/docs/layout/functions/border-radius.js +4 -0
- package/dist/mcp/knowledge/docs/layout/functions/pad.js +4 -0
- package/dist/mcp/knowledge/docs/layout/functions/sizable.js +4 -0
- package/dist/mcp/knowledge/docs/layout/mixins/sizable.js +4 -0
- package/dist/mcp/knowledge/docs/layout/mixins/sizing.js +4 -0
- package/dist/mcp/knowledge/docs/layout/mixins/spacing.js +4 -0
- package/dist/mcp/knowledge/docs/layout/overview.js +4 -0
- package/dist/mcp/knowledge/docs/setup/platform.js +4 -0
- package/dist/mcp/knowledge/elevations.d.ts +1 -1
- package/dist/mcp/knowledge/elevations.js +26 -12
- package/dist/mcp/knowledge/index.js +23 -87
- package/dist/mcp/knowledge/layout-docs.d.ts +1 -1
- package/dist/mcp/knowledge/multipliers.js +5 -0
- package/dist/mcp/knowledge/palettes.js +29 -17
- package/dist/mcp/knowledge/platforms/angular.js +98 -120
- package/dist/mcp/knowledge/platforms/blazor.js +39 -34
- package/dist/mcp/knowledge/platforms/common.js +83 -68
- package/dist/mcp/knowledge/platforms/index.js +265 -242
- package/dist/mcp/knowledge/platforms/react.js +43 -35
- package/dist/mcp/knowledge/platforms/webcomponents.js +266 -292
- package/dist/mcp/knowledge/sass-api.js +1 -0
- package/dist/mcp/knowledge/typography.js +13 -5
- package/dist/mcp/resources/index.js +1 -0
- package/dist/mcp/resources/presets.js +409 -508
- package/dist/mcp/theming/dist/json/colors/meta/multipliers.js +50 -0
- package/dist/mcp/theming/dist/json/colors/presets/palettes.js +85 -0
- package/dist/mcp/theming/dist/json/components/themes.js +5792 -0
- package/dist/mcp/theming/dist/json/elevations/indigo.js +29 -0
- package/dist/mcp/theming/dist/json/elevations/material.js +3 -0
- package/dist/mcp/theming/dist/json/typography/presets/typescales.js +621 -0
- package/dist/mcp/tools/descriptions.js +98 -154
- package/dist/mcp/tools/handlers/color.js +58 -56
- package/dist/mcp/tools/handlers/component-theme.js +163 -225
- package/dist/mcp/tools/handlers/component-tokens.js +159 -219
- package/dist/mcp/tools/handlers/custom-palette.js +138 -179
- package/dist/mcp/tools/handlers/elevations.js +27 -28
- package/dist/mcp/tools/handlers/index.js +11 -0
- package/dist/mcp/tools/handlers/layout.js +125 -176
- package/dist/mcp/tools/handlers/palette.js +105 -120
- package/dist/mcp/tools/handlers/platform.js +289 -311
- package/dist/mcp/tools/handlers/resource.js +22 -31
- package/dist/mcp/tools/handlers/theme.js +86 -103
- package/dist/mcp/tools/handlers/typography.js +29 -30
- package/dist/mcp/tools/index.js +13 -0
- package/dist/mcp/tools/schemas.js +239 -218
- package/dist/mcp/utils/color.js +277 -239
- package/dist/mcp/utils/preprocessing.js +57 -30
- package/dist/mcp/utils/result.js +43 -45
- package/dist/mcp/utils/sass.js +271 -191
- package/dist/mcp/utils/theming-resolve.d.ts +19 -0
- package/dist/mcp/utils/theming-resolve.js +57 -0
- package/dist/mcp/utils/types.js +96 -53
- package/dist/mcp/validators/custom-palette.js +218 -243
- package/dist/mcp/validators/index.js +3 -0
- package/dist/mcp/validators/palette.js +231 -229
- package/dist/tailwind/utilities/bootstrap.css +1 -0
- package/dist/tailwind/utilities/fluent.css +1 -0
- package/dist/tailwind/utilities/indigo.css +1 -0
- package/dist/tailwind/utilities/material.css +1 -0
- package/package.json +45 -64
- package/sass/json/README.md +12 -7
- package/sass/themes/_mixins.scss +1 -0
- package/sass/themes/components/button-group/_button-group-theme.scss +42 -0
- package/sass/themes/components/grid/_grid-theme.scss +1 -1
- package/sass/themes/schemas/components/dark/_button-group.scss +173 -50
- package/sass/themes/schemas/components/dark/_grid.scss +0 -16
- package/sass/themes/schemas/components/light/_button-group.scss +221 -99
- package/sass/themes/schemas/components/light/_grid.scss +14 -20
- package/LICENSE +0 -21
- package/README.md +0 -391
- package/dist/mcp/json/colors/presets/palettes.json.js +0 -13
- package/dist/mcp/json/components/themes.json.js +0 -143
- package/dist/mcp/json/elevations/indigo.json.js +0 -8
- package/dist/mcp/json/elevations/material.json.js +0 -8
- package/dist/mcp/json/typography/presets/typescales.json.js +0 -17
- package/dist/mcp/knowledge/docs/colors/guidance.md.js +0 -4
- package/dist/mcp/knowledge/docs/colors/usage.md.js +0 -4
- package/dist/mcp/knowledge/docs/layout/functions/border-radius.md.js +0 -4
- package/dist/mcp/knowledge/docs/layout/functions/pad.md.js +0 -4
- package/dist/mcp/knowledge/docs/layout/functions/sizable.md.js +0 -4
- package/dist/mcp/knowledge/docs/layout/mixins/sizable.md.js +0 -4
- package/dist/mcp/knowledge/docs/layout/mixins/sizing.md.js +0 -4
- package/dist/mcp/knowledge/docs/layout/mixins/spacing.md.js +0 -4
- package/dist/mcp/knowledge/docs/layout/overview.md.js +0 -4
- package/dist/mcp/knowledge/docs/setup/platform.md.js +0 -4
- package/dist/mcp/vite-env.d.ts +0 -18
- package/index.js +0 -5
- package/json/components/bootstrap.json +0 -1
- package/json/components/fluent.json +0 -1
- package/json/components/indigo.json +0 -1
- package/json/components/material.json +0 -1
- package/tailwind/utilities/bootstrap.css +0 -1
- package/tailwind/utilities/fluent.css +0 -1
- package/tailwind/utilities/indigo.css +0 -1
- package/tailwind/utilities/material.css +0 -1
- /package/{json → dist/json}/colors/meta/multipliers.json +0 -0
- /package/{json → dist/json}/colors/meta/palette.json +0 -0
- /package/{json → dist/json}/colors/presets/palettes.json +0 -0
- /package/{json → dist/json}/elevations/indigo.json +0 -0
- /package/{json → dist/json}/elevations/material.json +0 -0
- /package/{json → dist/json}/typography/presets/typescales.json +0 -0
- /package/{tailwind → dist/tailwind}/themes/base.css +0 -0
- /package/{tailwind → dist/tailwind}/themes/dark/bootstrap.css +0 -0
- /package/{tailwind → dist/tailwind}/themes/dark/fluent.css +0 -0
- /package/{tailwind → dist/tailwind}/themes/dark/indigo.css +0 -0
- /package/{tailwind → dist/tailwind}/themes/dark/material.css +0 -0
- /package/{tailwind → dist/tailwind}/themes/light/bootstrap.css +0 -0
- /package/{tailwind → dist/tailwind}/themes/light/fluent.css +0 -0
- /package/{tailwind → dist/tailwind}/themes/light/indigo.css +0 -0
- /package/{tailwind → dist/tailwind}/themes/light/material.css +0 -0
package/dist/mcp/utils/types.js
CHANGED
|
@@ -1,58 +1,101 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
//#region src/utils/types.ts
|
|
2
|
+
/**
|
|
3
|
+
* Shared TypeScript types for the MCP server.
|
|
4
|
+
*
|
|
5
|
+
* This module is the SINGLE SOURCE OF TRUTH for shared types and constants.
|
|
6
|
+
* Other modules should import from here rather than redefining types.
|
|
7
|
+
*
|
|
8
|
+
* IMPORTANT: This module should have NO imports from knowledge/ to avoid
|
|
9
|
+
* circular dependencies. Knowledge modules may import from this module.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Supported target platforms for code generation.
|
|
13
|
+
*
|
|
14
|
+
* - angular: Ignite UI for Angular (uses igniteui-angular/theming)
|
|
15
|
+
* - webcomponents: Ignite UI for Web Components (uses igniteui-theming directly)
|
|
16
|
+
* - react: Ignite UI for React (uses igniteui-theming directly)
|
|
17
|
+
* - blazor: Ignite UI for Blazor (uses igniteui-theming for Sass compilation)
|
|
18
|
+
* - generic: Platform-agnostic output using igniteui-theming directly (no Ignite UI product)
|
|
19
|
+
*/
|
|
20
|
+
var PLATFORMS = [
|
|
21
|
+
"angular",
|
|
22
|
+
"webcomponents",
|
|
23
|
+
"react",
|
|
24
|
+
"blazor",
|
|
25
|
+
"generic"
|
|
7
26
|
];
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
27
|
+
/**
|
|
28
|
+
* Supported design systems.
|
|
29
|
+
*/
|
|
30
|
+
var DESIGN_SYSTEMS = [
|
|
31
|
+
"material",
|
|
32
|
+
"bootstrap",
|
|
33
|
+
"fluent",
|
|
34
|
+
"indigo"
|
|
13
35
|
];
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Supported theme variants.
|
|
38
|
+
*/
|
|
39
|
+
var VARIANTS = ["light", "dark"];
|
|
40
|
+
/**
|
|
41
|
+
* Supported elevation presets.
|
|
42
|
+
*/
|
|
43
|
+
var ELEVATION_PRESETS = ["material", "indigo"];
|
|
44
|
+
/**
|
|
45
|
+
* Supported output formats for code generation.
|
|
46
|
+
*
|
|
47
|
+
* - sass: Generates Sass code using the igniteui-theming library functions
|
|
48
|
+
* - css: Generates CSS custom properties (variables) directly
|
|
49
|
+
*/
|
|
50
|
+
var OUTPUT_FORMATS = ["sass", "css"];
|
|
51
|
+
/**
|
|
52
|
+
* Standard shade levels used in the theming system.
|
|
53
|
+
*/
|
|
54
|
+
var SHADE_LEVELS = [
|
|
55
|
+
"50",
|
|
56
|
+
"100",
|
|
57
|
+
"200",
|
|
58
|
+
"300",
|
|
59
|
+
"400",
|
|
60
|
+
"500",
|
|
61
|
+
"600",
|
|
62
|
+
"700",
|
|
63
|
+
"800",
|
|
64
|
+
"900"
|
|
28
65
|
];
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
66
|
+
/**
|
|
67
|
+
* Accent shade levels (Material Design style).
|
|
68
|
+
*/
|
|
69
|
+
var ACCENT_SHADE_LEVELS = [
|
|
70
|
+
"A100",
|
|
71
|
+
"A200",
|
|
72
|
+
"A400",
|
|
73
|
+
"A700"
|
|
33
74
|
];
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
75
|
+
/**
|
|
76
|
+
* All chromatic shade levels (standard + accent).
|
|
77
|
+
* Derived from SHADE_LEVELS and ACCENT_SHADE_LEVELS to maintain single source of truth.
|
|
78
|
+
*/
|
|
79
|
+
var ALL_COLOR_SHADES = [...SHADE_LEVELS, ...ACCENT_SHADE_LEVELS];
|
|
80
|
+
/**
|
|
81
|
+
* All palette color groups.
|
|
82
|
+
* These are the color families that make up a complete palette.
|
|
83
|
+
*/
|
|
84
|
+
var PALETTE_COLOR_GROUPS = [
|
|
85
|
+
"primary",
|
|
86
|
+
"secondary",
|
|
87
|
+
"gray",
|
|
88
|
+
"surface",
|
|
89
|
+
"info",
|
|
90
|
+
"success",
|
|
91
|
+
"warn",
|
|
92
|
+
"error"
|
|
43
93
|
];
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
ELEVATION_PRESETS,
|
|
53
|
-
OUTPUT_FORMATS,
|
|
54
|
-
PALETTE_COLOR_GROUPS,
|
|
55
|
-
PLATFORMS,
|
|
56
|
-
SHADE_LEVELS,
|
|
57
|
-
VARIANTS
|
|
58
|
-
};
|
|
94
|
+
/**
|
|
95
|
+
* Chromatic color groups (excludes gray which has different shade handling).
|
|
96
|
+
* These groups use 14 shades (50-900, A100-A700).
|
|
97
|
+
* Derived from PALETTE_COLOR_GROUPS to maintain single source of truth.
|
|
98
|
+
*/
|
|
99
|
+
var CHROMATIC_COLOR_GROUPS = PALETTE_COLOR_GROUPS.filter((g) => g !== "gray");
|
|
100
|
+
//#endregion
|
|
101
|
+
export { ACCENT_SHADE_LEVELS, ALL_COLOR_SHADES, CHROMATIC_COLOR_GROUPS, DESIGN_SYSTEMS, ELEVATION_PRESETS, OUTPUT_FORMATS, PALETTE_COLOR_GROUPS, PLATFORMS, SHADE_LEVELS, VARIANTS };
|
|
@@ -1,257 +1,232 @@
|
|
|
1
|
-
import "../
|
|
2
|
-
import "../
|
|
3
|
-
import "../knowledge/
|
|
4
|
-
import { CHROMATIC_COLOR_GROUPS, ALL_COLOR_SHADES, SHADE_LEVELS } from "../utils/types.js";
|
|
5
|
-
import "../knowledge/palettes.js";
|
|
6
|
-
import "node:fs";
|
|
7
|
-
import "node:path";
|
|
8
|
-
import { validateColorsInBatch, DEFAULT_HUE_TOLERANCE, analyzeColorsWithHue, huesAreClose } from "../utils/color.js";
|
|
1
|
+
import { analyzeColorsWithHue, huesAreClose, validateColorsInBatch } from "../utils/color.js";
|
|
2
|
+
import { ALL_COLOR_SHADES, CHROMATIC_COLOR_GROUPS, SHADE_LEVELS } from "../utils/types.js";
|
|
3
|
+
import "../knowledge/index.js";
|
|
9
4
|
import { formatValidationMessages } from "../utils/result.js";
|
|
5
|
+
//#region src/validators/custom-palette.ts
|
|
6
|
+
/**
|
|
7
|
+
* Validation for custom palette structures.
|
|
8
|
+
*
|
|
9
|
+
* Uses the unified ValidationResult type from result.ts for consistent
|
|
10
|
+
* error/warning handling across the codebase.
|
|
11
|
+
*
|
|
12
|
+
* Performance optimization: Uses batch color validation to minimize Sass
|
|
13
|
+
* compilations. Instead of validating each color individually (which would
|
|
14
|
+
* spawn ~100+ Sass processes), we collect all colors and validate them in
|
|
15
|
+
* a single Sass compilation.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Helper to create a field path from color group and optional shade.
|
|
19
|
+
*/
|
|
10
20
|
function makeFieldPath(colorGroup, shade) {
|
|
11
|
-
|
|
21
|
+
return shade ? `${colorGroup}.${shade}` : colorGroup;
|
|
12
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Collects all colors from a palette input for batch validation.
|
|
25
|
+
*/
|
|
13
26
|
function collectAllColors(input) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
if (input.gray) {
|
|
29
|
-
collectFromDefinition(
|
|
30
|
-
"gray",
|
|
31
|
-
input.gray,
|
|
32
|
-
[...SHADE_LEVELS],
|
|
33
|
-
colors,
|
|
34
|
-
missingShades
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
return { colors, missingShades };
|
|
27
|
+
const colors = [];
|
|
28
|
+
const missingShades = [];
|
|
29
|
+
for (const group of CHROMATIC_COLOR_GROUPS) {
|
|
30
|
+
const definition = input[group];
|
|
31
|
+
if (definition) collectFromDefinition(group, definition, ALL_COLOR_SHADES, colors, missingShades);
|
|
32
|
+
}
|
|
33
|
+
if (input.gray) collectFromDefinition("gray", input.gray, [...SHADE_LEVELS], colors, missingShades);
|
|
34
|
+
return {
|
|
35
|
+
colors,
|
|
36
|
+
missingShades
|
|
37
|
+
};
|
|
38
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Collects colors from a single color definition.
|
|
41
|
+
*/
|
|
39
42
|
function collectFromDefinition(groupName, definition, expectedShades, colors, missingShades) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
key: `${groupName}.contrast.${shade}`,
|
|
70
|
-
color,
|
|
71
|
-
groupName,
|
|
72
|
-
shade,
|
|
73
|
-
isContrast: true
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
43
|
+
if (definition.mode === "shades") colors.push({
|
|
44
|
+
key: `${groupName}.baseColor`,
|
|
45
|
+
color: definition.baseColor,
|
|
46
|
+
groupName
|
|
47
|
+
});
|
|
48
|
+
else {
|
|
49
|
+
for (const shade of expectedShades) {
|
|
50
|
+
const color = definition.shades[shade];
|
|
51
|
+
if (!color) missingShades.push({
|
|
52
|
+
field: makeFieldPath(groupName, shade),
|
|
53
|
+
message: `Missing required shade: ${shade}`
|
|
54
|
+
});
|
|
55
|
+
else colors.push({
|
|
56
|
+
key: `${groupName}.${shade}`,
|
|
57
|
+
color,
|
|
58
|
+
groupName,
|
|
59
|
+
shade
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
if (definition.contrastOverrides) {
|
|
63
|
+
for (const [shade, color] of Object.entries(definition.contrastOverrides)) if (expectedShades.includes(shade)) colors.push({
|
|
64
|
+
key: `${groupName}.contrast.${shade}`,
|
|
65
|
+
color,
|
|
66
|
+
groupName,
|
|
67
|
+
shade,
|
|
68
|
+
isContrast: true
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
79
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* Validates a custom palette input structure.
|
|
75
|
+
*
|
|
76
|
+
* @param input - The custom palette input to validate
|
|
77
|
+
* @param variant - Theme variant for gray shade progression validation (defaults to 'light')
|
|
78
|
+
*/
|
|
80
79
|
async function validateCustomPalette(input, variant = "light") {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
for (const group of CHROMATIC_COLOR_GROUPS) {
|
|
141
|
-
const definition = input[group];
|
|
142
|
-
if (definition?.mode === "explicit") {
|
|
143
|
-
await validateShadeProgression(
|
|
144
|
-
group,
|
|
145
|
-
definition.shades,
|
|
146
|
-
"chromatic",
|
|
147
|
-
variant,
|
|
148
|
-
warnings
|
|
149
|
-
);
|
|
150
|
-
await validateMonochromaticHue(group, definition.shades, warnings);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
if (input.gray?.mode === "explicit") {
|
|
154
|
-
await validateShadeProgression(
|
|
155
|
-
"gray",
|
|
156
|
-
input.gray.shades,
|
|
157
|
-
"gray",
|
|
158
|
-
variant,
|
|
159
|
-
warnings
|
|
160
|
-
);
|
|
161
|
-
}
|
|
162
|
-
return {
|
|
163
|
-
isValid: errors.length === 0,
|
|
164
|
-
errors,
|
|
165
|
-
warnings
|
|
166
|
-
};
|
|
80
|
+
const errors = [];
|
|
81
|
+
const warnings = [];
|
|
82
|
+
const { colors, missingShades } = collectAllColors(input);
|
|
83
|
+
errors.push(...missingShades);
|
|
84
|
+
const chromaticShadeSet = ALL_COLOR_SHADES;
|
|
85
|
+
const grayShadeSet = SHADE_LEVELS;
|
|
86
|
+
for (const group of CHROMATIC_COLOR_GROUPS) {
|
|
87
|
+
const definition = input[group];
|
|
88
|
+
if (definition?.mode === "explicit" && definition.contrastOverrides) {
|
|
89
|
+
for (const shade of Object.keys(definition.contrastOverrides)) if (!chromaticShadeSet.includes(shade)) errors.push({
|
|
90
|
+
field: makeFieldPath(group, shade),
|
|
91
|
+
message: `Invalid contrast override key: ${shade}. Valid keys are: ${ALL_COLOR_SHADES.join(", ")}`
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (input.gray?.mode === "explicit" && input.gray.contrastOverrides) {
|
|
96
|
+
for (const shade of Object.keys(input.gray.contrastOverrides)) if (!grayShadeSet.includes(shade)) errors.push({
|
|
97
|
+
field: makeFieldPath("gray", shade),
|
|
98
|
+
message: `Invalid contrast override key: ${shade}. Valid keys are: ${SHADE_LEVELS.join(", ")}`
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
if (colors.length > 0) {
|
|
102
|
+
const colorMap = {};
|
|
103
|
+
for (const c of colors) colorMap[c.key] = c.color;
|
|
104
|
+
const validationResults = await validateColorsInBatch(colorMap);
|
|
105
|
+
for (const c of colors) if (!validationResults[c.key]) if (c.isContrast) errors.push({
|
|
106
|
+
field: makeFieldPath(c.groupName, `contrast.${c.shade}`),
|
|
107
|
+
message: `Invalid contrast color for shade ${c.shade}: ${c.color}`,
|
|
108
|
+
currentValue: c.color
|
|
109
|
+
});
|
|
110
|
+
else if (c.shade) errors.push({
|
|
111
|
+
field: makeFieldPath(c.groupName, c.shade),
|
|
112
|
+
message: `Invalid color value for shade ${c.shade}: ${c.color}`,
|
|
113
|
+
currentValue: c.color
|
|
114
|
+
});
|
|
115
|
+
else errors.push({
|
|
116
|
+
field: c.groupName,
|
|
117
|
+
message: `Invalid base color: ${c.color}`,
|
|
118
|
+
currentValue: c.color
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
for (const group of CHROMATIC_COLOR_GROUPS) {
|
|
122
|
+
const definition = input[group];
|
|
123
|
+
if (definition?.mode === "explicit") {
|
|
124
|
+
await validateShadeProgression(group, definition.shades, "chromatic", variant, warnings);
|
|
125
|
+
await validateMonochromaticHue(group, definition.shades, warnings);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (input.gray?.mode === "explicit") await validateShadeProgression("gray", input.gray.shades, "gray", variant, warnings);
|
|
129
|
+
return {
|
|
130
|
+
isValid: errors.length === 0,
|
|
131
|
+
errors,
|
|
132
|
+
warnings
|
|
133
|
+
};
|
|
167
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* Format validation result as markdown.
|
|
137
|
+
*
|
|
138
|
+
* This is a thin wrapper around formatValidationMessages for backward compatibility.
|
|
139
|
+
* New code should use formatValidationMessages directly.
|
|
140
|
+
*/
|
|
168
141
|
function formatCustomPaletteValidation(result) {
|
|
169
|
-
|
|
170
|
-
|
|
142
|
+
if (result.isValid && result.warnings.length === 0) return "";
|
|
143
|
+
return formatValidationMessages(result);
|
|
171
144
|
}
|
|
145
|
+
/**
|
|
146
|
+
* Validates that shade progression follows expected luminance direction.
|
|
147
|
+
*
|
|
148
|
+
* - Chromatic colors: shade 50 should be lighter than shade 900 (always)
|
|
149
|
+
* - Gray (light themes): shade 50 should be lighter than shade 900
|
|
150
|
+
* - Gray (dark themes): shade 50 should be darker than shade 900 (inverted)
|
|
151
|
+
*
|
|
152
|
+
* Only checks endpoints (50 vs 900), not full progression.
|
|
153
|
+
* Issues warnings, not errors.
|
|
154
|
+
*/
|
|
172
155
|
async function validateShadeProgression(groupName, shades, colorType, variant, warnings) {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
message: `${context} shade 50 should be lighter than shade 900. Found: 50 (luminance: ${lum50.toFixed(3)}) vs 900 (luminance: ${lum900.toFixed(3)}).`,
|
|
202
|
-
severity: "warning"
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
} catch (_error) {
|
|
206
|
-
if (process.env.DEBUG) ;
|
|
207
|
-
}
|
|
156
|
+
const shade50 = shades["50"];
|
|
157
|
+
const shade900 = shades["900"];
|
|
158
|
+
if (!shade50 || !shade900) return;
|
|
159
|
+
try {
|
|
160
|
+
const analysis = await analyzeColorsWithHue({
|
|
161
|
+
shade50,
|
|
162
|
+
shade900
|
|
163
|
+
});
|
|
164
|
+
const lum50 = analysis.shade50?.luminance;
|
|
165
|
+
const lum900 = analysis.shade900?.luminance;
|
|
166
|
+
if (lum50 === void 0 || lum900 === void 0) return;
|
|
167
|
+
if (colorType === "gray" && variant === "dark") {
|
|
168
|
+
if (lum50 >= lum900) warnings.push({
|
|
169
|
+
field: groupName,
|
|
170
|
+
message: `For dark themes, gray shade 50 should be darker than shade 900 (inverted progression). Found: 50 (luminance: ${lum50.toFixed(3)}) vs 900 (luminance: ${lum900.toFixed(3)}).`,
|
|
171
|
+
severity: "warning"
|
|
172
|
+
});
|
|
173
|
+
} else if (lum50 <= lum900) {
|
|
174
|
+
const context = colorType === "gray" ? "For light themes, gray" : "Chromatic";
|
|
175
|
+
warnings.push({
|
|
176
|
+
field: groupName,
|
|
177
|
+
message: `${context} shade 50 should be lighter than shade 900. Found: 50 (luminance: ${lum50.toFixed(3)}) vs 900 (luminance: ${lum900.toFixed(3)}).`,
|
|
178
|
+
severity: "warning"
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
} catch (_error) {
|
|
182
|
+
if (process.env.DEBUG) {}
|
|
183
|
+
}
|
|
208
184
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
185
|
+
/**
|
|
186
|
+
* Validates that a chromatic color family is monochromatic (same hue family).
|
|
187
|
+
*
|
|
188
|
+
* Checks hues at shades 50, 500, and 900. Warns if hue variation exceeds tolerance.
|
|
189
|
+
* Only applies to chromatic colors, not gray.
|
|
190
|
+
*/
|
|
191
|
+
async function validateMonochromaticHue(groupName, shades, warnings, tolerance = 30) {
|
|
192
|
+
const shade50 = shades["50"];
|
|
193
|
+
const shade500 = shades["500"];
|
|
194
|
+
const shade900 = shades["900"];
|
|
195
|
+
if (!shade50 || !shade500 || !shade900) return;
|
|
196
|
+
try {
|
|
197
|
+
const analysis = await analyzeColorsWithHue({
|
|
198
|
+
shade50,
|
|
199
|
+
shade500,
|
|
200
|
+
shade900
|
|
201
|
+
});
|
|
202
|
+
const hue50 = analysis.shade50?.hue;
|
|
203
|
+
const hue500 = analysis.shade500?.hue;
|
|
204
|
+
const hue900 = analysis.shade900?.hue;
|
|
205
|
+
if (hue50 === void 0 || hue500 === void 0 || hue900 === void 0) return;
|
|
206
|
+
const hues = [
|
|
207
|
+
{
|
|
208
|
+
shade: "50",
|
|
209
|
+
hue: hue50
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
shade: "500",
|
|
213
|
+
hue: hue500
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
shade: "900",
|
|
217
|
+
hue: hue900
|
|
218
|
+
}
|
|
219
|
+
];
|
|
220
|
+
const outliers = [];
|
|
221
|
+
for (let i = 0; i < hues.length; i++) for (let j = i + 1; j < hues.length; j++) if (!huesAreClose(hues[i].hue, hues[j].hue, tolerance)) outliers.push(`${hues[i].shade} (${Math.round(hues[i].hue)}°) vs ${hues[j].shade} (${Math.round(hues[j].hue)}°)`);
|
|
222
|
+
if (outliers.length > 0) warnings.push({
|
|
223
|
+
field: groupName,
|
|
224
|
+
message: `Color shades may not be monochromatic (hue varies by more than ±${tolerance}°). Differences found: ${outliers.join(", ")}. Consider using colors from the same hue family for visual consistency.`,
|
|
225
|
+
severity: "warning"
|
|
226
|
+
});
|
|
227
|
+
} catch (_error) {
|
|
228
|
+
if (process.env.DEBUG) {}
|
|
229
|
+
}
|
|
253
230
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
validateCustomPalette
|
|
257
|
-
};
|
|
231
|
+
//#endregion
|
|
232
|
+
export { formatCustomPaletteValidation, validateCustomPalette };
|