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.
Files changed (128) hide show
  1. package/dist/index.d.ts +75 -0
  2. package/dist/index.js +12 -0
  3. package/dist/json/components/bootstrap.json +1 -0
  4. package/dist/json/components/fluent.json +1 -0
  5. package/dist/json/components/indigo.json +1 -0
  6. package/dist/json/components/material.json +1 -0
  7. package/{json → dist/json}/components/themes.json +31 -1
  8. package/dist/mcp/generators/css.d.ts +7 -4
  9. package/dist/mcp/generators/css.js +129 -104
  10. package/dist/mcp/generators/sass.js +227 -254
  11. package/dist/mcp/index.js +259 -323
  12. package/dist/mcp/knowledge/color-usage.js +524 -502
  13. package/dist/mcp/knowledge/colors.js +61 -50
  14. package/dist/mcp/knowledge/component-metadata.js +697 -598
  15. package/dist/mcp/knowledge/component-themes.js +70 -57
  16. package/dist/mcp/knowledge/custom-palettes.js +4 -9
  17. package/dist/mcp/knowledge/docs/colors/guidance.js +4 -0
  18. package/dist/mcp/knowledge/docs/colors/usage.js +4 -0
  19. package/dist/mcp/knowledge/docs/layout/functions/border-radius.js +4 -0
  20. package/dist/mcp/knowledge/docs/layout/functions/pad.js +4 -0
  21. package/dist/mcp/knowledge/docs/layout/functions/sizable.js +4 -0
  22. package/dist/mcp/knowledge/docs/layout/mixins/sizable.js +4 -0
  23. package/dist/mcp/knowledge/docs/layout/mixins/sizing.js +4 -0
  24. package/dist/mcp/knowledge/docs/layout/mixins/spacing.js +4 -0
  25. package/dist/mcp/knowledge/docs/layout/overview.js +4 -0
  26. package/dist/mcp/knowledge/docs/setup/platform.js +4 -0
  27. package/dist/mcp/knowledge/elevations.d.ts +1 -1
  28. package/dist/mcp/knowledge/elevations.js +26 -12
  29. package/dist/mcp/knowledge/index.js +23 -87
  30. package/dist/mcp/knowledge/layout-docs.d.ts +1 -1
  31. package/dist/mcp/knowledge/multipliers.js +5 -0
  32. package/dist/mcp/knowledge/palettes.js +29 -17
  33. package/dist/mcp/knowledge/platforms/angular.js +98 -120
  34. package/dist/mcp/knowledge/platforms/blazor.js +39 -34
  35. package/dist/mcp/knowledge/platforms/common.js +83 -68
  36. package/dist/mcp/knowledge/platforms/index.js +265 -242
  37. package/dist/mcp/knowledge/platforms/react.js +43 -35
  38. package/dist/mcp/knowledge/platforms/webcomponents.js +266 -292
  39. package/dist/mcp/knowledge/sass-api.js +1 -0
  40. package/dist/mcp/knowledge/typography.js +13 -5
  41. package/dist/mcp/resources/index.js +1 -0
  42. package/dist/mcp/resources/presets.js +409 -508
  43. package/dist/mcp/theming/dist/json/colors/meta/multipliers.js +50 -0
  44. package/dist/mcp/theming/dist/json/colors/presets/palettes.js +85 -0
  45. package/dist/mcp/theming/dist/json/components/themes.js +5792 -0
  46. package/dist/mcp/theming/dist/json/elevations/indigo.js +29 -0
  47. package/dist/mcp/theming/dist/json/elevations/material.js +3 -0
  48. package/dist/mcp/theming/dist/json/typography/presets/typescales.js +621 -0
  49. package/dist/mcp/tools/descriptions.js +98 -154
  50. package/dist/mcp/tools/handlers/color.js +58 -56
  51. package/dist/mcp/tools/handlers/component-theme.js +163 -225
  52. package/dist/mcp/tools/handlers/component-tokens.js +159 -219
  53. package/dist/mcp/tools/handlers/custom-palette.js +138 -179
  54. package/dist/mcp/tools/handlers/elevations.js +27 -28
  55. package/dist/mcp/tools/handlers/index.js +11 -0
  56. package/dist/mcp/tools/handlers/layout.js +125 -176
  57. package/dist/mcp/tools/handlers/palette.js +105 -120
  58. package/dist/mcp/tools/handlers/platform.js +289 -311
  59. package/dist/mcp/tools/handlers/resource.js +22 -31
  60. package/dist/mcp/tools/handlers/theme.js +86 -103
  61. package/dist/mcp/tools/handlers/typography.js +29 -30
  62. package/dist/mcp/tools/index.js +13 -0
  63. package/dist/mcp/tools/schemas.js +239 -218
  64. package/dist/mcp/utils/color.js +277 -239
  65. package/dist/mcp/utils/preprocessing.js +57 -30
  66. package/dist/mcp/utils/result.js +43 -45
  67. package/dist/mcp/utils/sass.js +271 -191
  68. package/dist/mcp/utils/theming-resolve.d.ts +19 -0
  69. package/dist/mcp/utils/theming-resolve.js +57 -0
  70. package/dist/mcp/utils/types.js +96 -53
  71. package/dist/mcp/validators/custom-palette.js +218 -243
  72. package/dist/mcp/validators/index.js +3 -0
  73. package/dist/mcp/validators/palette.js +231 -229
  74. package/dist/tailwind/utilities/bootstrap.css +1 -0
  75. package/dist/tailwind/utilities/fluent.css +1 -0
  76. package/dist/tailwind/utilities/indigo.css +1 -0
  77. package/dist/tailwind/utilities/material.css +1 -0
  78. package/package.json +45 -64
  79. package/sass/json/README.md +12 -7
  80. package/sass/themes/_mixins.scss +1 -0
  81. package/sass/themes/components/button-group/_button-group-theme.scss +42 -0
  82. package/sass/themes/components/grid/_grid-theme.scss +1 -1
  83. package/sass/themes/schemas/components/dark/_button-group.scss +173 -50
  84. package/sass/themes/schemas/components/dark/_grid.scss +0 -16
  85. package/sass/themes/schemas/components/light/_button-group.scss +221 -99
  86. package/sass/themes/schemas/components/light/_grid.scss +14 -20
  87. package/LICENSE +0 -21
  88. package/README.md +0 -391
  89. package/dist/mcp/json/colors/presets/palettes.json.js +0 -13
  90. package/dist/mcp/json/components/themes.json.js +0 -143
  91. package/dist/mcp/json/elevations/indigo.json.js +0 -8
  92. package/dist/mcp/json/elevations/material.json.js +0 -8
  93. package/dist/mcp/json/typography/presets/typescales.json.js +0 -17
  94. package/dist/mcp/knowledge/docs/colors/guidance.md.js +0 -4
  95. package/dist/mcp/knowledge/docs/colors/usage.md.js +0 -4
  96. package/dist/mcp/knowledge/docs/layout/functions/border-radius.md.js +0 -4
  97. package/dist/mcp/knowledge/docs/layout/functions/pad.md.js +0 -4
  98. package/dist/mcp/knowledge/docs/layout/functions/sizable.md.js +0 -4
  99. package/dist/mcp/knowledge/docs/layout/mixins/sizable.md.js +0 -4
  100. package/dist/mcp/knowledge/docs/layout/mixins/sizing.md.js +0 -4
  101. package/dist/mcp/knowledge/docs/layout/mixins/spacing.md.js +0 -4
  102. package/dist/mcp/knowledge/docs/layout/overview.md.js +0 -4
  103. package/dist/mcp/knowledge/docs/setup/platform.md.js +0 -4
  104. package/dist/mcp/vite-env.d.ts +0 -18
  105. package/index.js +0 -5
  106. package/json/components/bootstrap.json +0 -1
  107. package/json/components/fluent.json +0 -1
  108. package/json/components/indigo.json +0 -1
  109. package/json/components/material.json +0 -1
  110. package/tailwind/utilities/bootstrap.css +0 -1
  111. package/tailwind/utilities/fluent.css +0 -1
  112. package/tailwind/utilities/indigo.css +0 -1
  113. package/tailwind/utilities/material.css +0 -1
  114. /package/{json → dist/json}/colors/meta/multipliers.json +0 -0
  115. /package/{json → dist/json}/colors/meta/palette.json +0 -0
  116. /package/{json → dist/json}/colors/presets/palettes.json +0 -0
  117. /package/{json → dist/json}/elevations/indigo.json +0 -0
  118. /package/{json → dist/json}/elevations/material.json +0 -0
  119. /package/{json → dist/json}/typography/presets/typescales.json +0 -0
  120. /package/{tailwind → dist/tailwind}/themes/base.css +0 -0
  121. /package/{tailwind → dist/tailwind}/themes/dark/bootstrap.css +0 -0
  122. /package/{tailwind → dist/tailwind}/themes/dark/fluent.css +0 -0
  123. /package/{tailwind → dist/tailwind}/themes/dark/indigo.css +0 -0
  124. /package/{tailwind → dist/tailwind}/themes/dark/material.css +0 -0
  125. /package/{tailwind → dist/tailwind}/themes/light/bootstrap.css +0 -0
  126. /package/{tailwind → dist/tailwind}/themes/light/fluent.css +0 -0
  127. /package/{tailwind → dist/tailwind}/themes/light/indigo.css +0 -0
  128. /package/{tailwind → dist/tailwind}/themes/light/material.css +0 -0
@@ -1,334 +1,312 @@
1
- import { readFile } from "node:fs/promises";
2
- import { resolve, dirname } from "node:path";
1
+ import { PLATFORM_METADATA, detectPlatformFromDependencies, isLicensedPackage } from "../../knowledge/platforms/index.js";
3
2
  import { z } from "zod";
4
- import { detectPlatformFromDependencies, isLicensedPackage, PLATFORM_METADATA } from "../../knowledge/platforms/index.js";
5
- const packageJsonSchema = z.object({
6
- dependencies: z.record(z.string()).optional(),
7
- devDependencies: z.record(z.string()).optional()
3
+ import { dirname, resolve } from "node:path";
4
+ import { readFile } from "node:fs/promises";
5
+ //#region src/tools/handlers/platform.ts
6
+ /**
7
+ * Handler for detect_platform tool.
8
+ *
9
+ * Detects the target platform (Angular, Web Components, React, Blazor, or Generic)
10
+ * from package.json dependencies and project config files.
11
+ *
12
+ * Uses a multi-signal detection approach:
13
+ * 1. Ignite UI packages (HIGH confidence)
14
+ * 2. Config files like angular.json, vite.config.ts, etc. (MEDIUM-HIGH confidence)
15
+ * 3. Framework packages as fallback (LOW confidence)
16
+ * 4. Generic (standalone) mode when no Ignite UI product is detected
17
+ *
18
+ * When multiple platforms are detected with significant confidence,
19
+ * returns an ambiguous result prompting user to specify explicitly.
20
+ *
21
+ * When no Ignite UI product is found, returns "generic" platform
22
+ * with tool eligibility guidance and Sass load path configuration help.
23
+ */
24
+ /**
25
+ * Zod schema for validating package.json structure.
26
+ * Only validates the fields we need for platform detection.
27
+ */
28
+ var packageJsonSchema = z.object({
29
+ dependencies: z.record(z.string()).optional(),
30
+ devDependencies: z.record(z.string()).optional()
8
31
  });
32
+ /**
33
+ * Format a detection signal for human-readable output.
34
+ */
9
35
  function formatSignal(signal) {
10
- switch (signal.type) {
11
- case "ignite_package":
12
- return `package: ${signal.package}`;
13
- case "config_file":
14
- return `config: ${signal.file}`;
15
- case "framework_package":
16
- return `framework: ${signal.package}`;
17
- default:
18
- return "unknown";
19
- }
36
+ switch (signal.type) {
37
+ case "ignite_package": return `package: ${signal.package}`;
38
+ case "config_file": return `config: ${signal.file}`;
39
+ case "framework_package": return `framework: ${signal.package}`;
40
+ default: return "unknown";
41
+ }
20
42
  }
21
- const SASS_CONFIG_GUIDANCE = [
22
- {
23
- match: "exact",
24
- key: "angular.json",
25
- description: "An `angular.json` config file was detected. To use Sass output from this MCP, ensure your Angular project includes `node_modules` in the Sass load paths:",
26
- lang: "json",
27
- code: [
28
- "// In angular.json → architect → build → options:",
29
- '"stylePreprocessorOptions": {',
30
- ' "includePaths": ["node_modules"]',
31
- "}"
32
- ].join("\n")
33
- },
34
- {
35
- match: "prefix",
36
- key: "vite.config",
37
- description: "A Vite config file was detected. To use Sass output from this MCP, ensure your Vite config includes `node_modules` in the Sass load paths:",
38
- lang: "js",
39
- code: [
40
- "// In vite.config.ts/js:",
41
- "css: {",
42
- " preprocessorOptions: {",
43
- " scss: {",
44
- " loadPaths: ['node_modules']",
45
- " }",
46
- " }",
47
- "}"
48
- ].join("\n")
49
- },
50
- {
51
- match: "prefix",
52
- key: "next.config",
53
- description: "A Next.js config file was detected. To use Sass output from this MCP, ensure your Next.js config includes `node_modules` in the Sass load paths:",
54
- lang: "js",
55
- code: [
56
- "// In next.config.js/mjs/ts:",
57
- "sassOptions: {",
58
- " loadPaths: ['node_modules']",
59
- "}"
60
- ].join("\n")
61
- }
43
+ /**
44
+ * Lookup table mapping config file patterns to Sass load path guidance.
45
+ * Add new entries here when supporting additional build tools.
46
+ */
47
+ var SASS_CONFIG_GUIDANCE = [
48
+ {
49
+ match: "exact",
50
+ key: "angular.json",
51
+ description: "An `angular.json` config file was detected. To use Sass output from this MCP, ensure your Angular project includes `node_modules` in the Sass load paths:",
52
+ lang: "json",
53
+ code: [
54
+ "// In angular.json → architect → build → options:",
55
+ "\"stylePreprocessorOptions\": {",
56
+ " \"includePaths\": [\"node_modules\"]",
57
+ "}"
58
+ ].join("\n")
59
+ },
60
+ {
61
+ match: "prefix",
62
+ key: "vite.config",
63
+ description: "A Vite config file was detected. To use Sass output from this MCP, ensure your Vite config includes `node_modules` in the Sass load paths:",
64
+ lang: "js",
65
+ code: [
66
+ "// In vite.config.ts/js:",
67
+ "css: {",
68
+ " preprocessorOptions: {",
69
+ " scss: {",
70
+ " loadPaths: ['node_modules']",
71
+ " }",
72
+ " }",
73
+ "}"
74
+ ].join("\n")
75
+ },
76
+ {
77
+ match: "prefix",
78
+ key: "next.config",
79
+ description: "A Next.js config file was detected. To use Sass output from this MCP, ensure your Next.js config includes `node_modules` in the Sass load paths:",
80
+ lang: "js",
81
+ code: [
82
+ "// In next.config.js/mjs/ts:",
83
+ "sassOptions: {",
84
+ " loadPaths: ['node_modules']",
85
+ "}"
86
+ ].join("\n")
87
+ }
62
88
  ];
63
- const SASS_CONFIG_FALLBACK = "To use Sass output from this MCP, ensure your project's Sass compiler has `node_modules` in its `loadPaths`. The exact configuration depends on your build tool — Angular CLI uses `includePaths` in `angular.json`, while most other tools (Vite, Next.js, sass CLI) use `loadPaths`. Investigate the project's build configuration to find the right place to add this.";
89
+ var SASS_CONFIG_FALLBACK = "To use Sass output from this MCP, ensure your project's Sass compiler has `node_modules` in its `loadPaths`. The exact configuration depends on your build tool — Angular CLI uses `includePaths` in `angular.json`, while most other tools (Vite, Next.js, sass CLI) use `loadPaths`. Investigate the project's build configuration to find the right place to add this.";
90
+ /**
91
+ * Find Sass configuration guidance for a given config file name.
92
+ */
64
93
  function findSassGuidance(fileName) {
65
- return SASS_CONFIG_GUIDANCE.find(
66
- (entry) => entry.match === "exact" ? fileName === entry.key : fileName.startsWith(entry.key)
67
- );
94
+ return SASS_CONFIG_GUIDANCE.find((entry) => entry.match === "exact" ? fileName === entry.key : fileName.startsWith(entry.key));
68
95
  }
96
+ /**
97
+ * Build the "Available Tools" and "Not Available" sections for generic mode.
98
+ */
69
99
  function buildToolEligibilitySection() {
70
- return [
71
- "### Available Tools",
72
- "",
73
- "The following tools work in generic (standalone) mode:",
74
- "",
75
- "- `create_palette` — Generate color palettes",
76
- "- `create_custom_palette` — Generate fully custom palettes",
77
- "- `create_typography` — Set up typography/type scales",
78
- "- `create_elevations` — Configure shadow/elevation system",
79
- "- `create_theme` — Generate a complete theme",
80
- "- `set_size` / `set_spacing` / `set_roundness` — Layout tokens (use `scope` with a custom CSS selector or omit for `:root`; do **not** use `component` as it targets Ignite UI component selectors)",
81
- "- `get_color` — Get CSS variable references for palette colors",
82
- "- `read_resource` — Read theming reference data",
83
- "",
84
- "### Not Available in Generic Mode",
85
- "",
86
- "- `create_component_theme` — Requires a specific Ignite UI product platform (angular, webcomponents, react, or blazor) for component selectors and variable prefixes",
87
- "- `get_component_design_tokens` — Returns tokens for Ignite UI framework components which are not present in generic mode",
88
- ""
89
- ];
100
+ return [
101
+ "### Available Tools",
102
+ "",
103
+ "The following tools work in generic (standalone) mode:",
104
+ "",
105
+ "- `create_palette` — Generate color palettes",
106
+ "- `create_custom_palette` — Generate fully custom palettes",
107
+ "- `create_typography` — Set up typography/type scales",
108
+ "- `create_elevations` — Configure shadow/elevation system",
109
+ "- `create_theme` — Generate a complete theme",
110
+ "- `set_size` / `set_spacing` / `set_roundness` — Layout tokens (use `scope` with a custom CSS selector or omit for `:root`; do **not** use `component` as it targets Ignite UI component selectors)",
111
+ "- `get_color` — Get CSS variable references for palette colors",
112
+ "- `read_resource` — Read theming reference data",
113
+ "",
114
+ "### Not Available in Generic Mode",
115
+ "",
116
+ "- `create_component_theme` — Requires a specific Ignite UI product platform (angular, webcomponents, react, or blazor) for component selectors and variable prefixes",
117
+ "- `get_component_design_tokens` — Returns tokens for Ignite UI framework components which are not present in generic mode",
118
+ ""
119
+ ];
90
120
  }
121
+ /**
122
+ * Build the "Sass Configuration" section based on detected config file signals.
123
+ */
91
124
  function buildSassConfigSection(signals) {
92
- const lines = ["### Sass Configuration", ""];
93
- const configFileSignals = signals.filter((s) => s.type === "config_file");
94
- if (configFileSignals.length === 0) {
95
- lines.push(SASS_CONFIG_FALLBACK, "");
96
- return lines;
97
- }
98
- for (const signal of configFileSignals) {
99
- if (signal.type !== "config_file") continue;
100
- const guidance = findSassGuidance(signal.file);
101
- if (guidance) {
102
- lines.push(
103
- guidance.description,
104
- "",
105
- `\`\`\`${guidance.lang}`,
106
- guidance.code,
107
- "```",
108
- ""
109
- );
110
- }
111
- }
112
- return lines;
125
+ const lines = ["### Sass Configuration", ""];
126
+ const configFileSignals = signals.filter((s) => s.type === "config_file");
127
+ if (configFileSignals.length === 0) {
128
+ lines.push(SASS_CONFIG_FALLBACK, "");
129
+ return lines;
130
+ }
131
+ for (const signal of configFileSignals) {
132
+ if (signal.type !== "config_file") continue;
133
+ const guidance = findSassGuidance(signal.file);
134
+ if (guidance) lines.push(guidance.description, "", `\`\`\`${guidance.lang}`, guidance.code, "```", "");
135
+ }
136
+ return lines;
113
137
  }
138
+ /**
139
+ * Build the "Output Format Notes" section based on whether igniteui-theming is installed.
140
+ */
114
141
  function buildOutputFormatNotes(hasThemingPackage) {
115
- const lines = ["### Output Format Notes", ""];
116
- if (!hasThemingPackage) {
117
- lines.push(
118
- "The `igniteui-theming` package was **not found** in this project's dependencies.",
119
- "",
120
- "- **CSS output** works without any local installation — the MCP compiles Sass to CSS server-side.",
121
- "- **Sass output** requires `igniteui-theming` to be resolvable in your project. Run `npm install igniteui-theming` to install it, then configure `loadPaths` as described above."
122
- );
123
- } else {
124
- lines.push(
125
- "The `igniteui-theming` package is installed in this project.",
126
- "",
127
- "- **CSS output** is compiled server-side by the MCP — no Sass toolchain needed.",
128
- "- **Sass output** uses `@use 'igniteui-theming' as *;` and requires the `loadPaths` configuration described above."
129
- );
130
- }
131
- return lines;
142
+ const lines = ["### Output Format Notes", ""];
143
+ if (!hasThemingPackage) lines.push("The `igniteui-theming` package was **not found** in this project's dependencies.", "", "- **CSS output** works without any local installation — the MCP compiles Sass to CSS server-side.", "- **Sass output** requires `igniteui-theming` to be resolvable in your project. Run `npm install igniteui-theming` to install it, then configure `loadPaths` as described above.");
144
+ else lines.push("The `igniteui-theming` package is installed in this project.", "", "- **CSS output** is compiled server-side by the MCP — no Sass toolchain needed.", "- **Sass output** uses `@use 'igniteui-theming' as *;` and requires the `loadPaths` configuration described above.");
145
+ return lines;
132
146
  }
147
+ /**
148
+ * Build response text for ambiguous detection (multiple Ignite UI platforms found).
149
+ */
133
150
  function buildAmbiguousResponse(result) {
134
- const alternatives = result.alternatives;
135
- const lines = [
136
- "## Platform Detection Result",
137
- "",
138
- "**Status:** Ambiguous - Multiple platforms detected",
139
- "",
140
- "The project appears to contain dependencies for multiple Ignite UI platforms. This might be a monorepo or a project transitioning between frameworks.",
141
- "",
142
- "### Detected Platforms",
143
- ""
144
- ];
145
- for (const alt of alternatives) {
146
- const metadata = PLATFORM_METADATA[alt.platform];
147
- lines.push(
148
- `#### ${metadata.name}`,
149
- `- **Confidence:** ${alt.confidence}%`,
150
- `- **Signals:** ${alt.signals.map(formatSignal).join(", ")}`,
151
- `- **Theming module:** \`${metadata.themingModule}\``,
152
- ""
153
- );
154
- }
155
- lines.push(
156
- "### Action Required",
157
- "",
158
- "Please specify the platform explicitly when calling theme generation tools:",
159
- ""
160
- );
161
- for (const alt of alternatives) {
162
- const metadata = PLATFORM_METADATA[alt.platform];
163
- lines.push(`- Use \`platform: '${alt.platform}'\` for ${metadata.name}`);
164
- }
165
- return lines;
151
+ const alternatives = result.alternatives;
152
+ const lines = [
153
+ "## Platform Detection Result",
154
+ "",
155
+ "**Status:** Ambiguous - Multiple platforms detected",
156
+ "",
157
+ "The project appears to contain dependencies for multiple Ignite UI platforms. This might be a monorepo or a project transitioning between frameworks.",
158
+ "",
159
+ "### Detected Platforms",
160
+ ""
161
+ ];
162
+ for (const alt of alternatives) {
163
+ const metadata = PLATFORM_METADATA[alt.platform];
164
+ lines.push(`#### ${metadata.name}`, `- **Confidence:** ${alt.confidence}%`, `- **Signals:** ${alt.signals.map(formatSignal).join(", ")}`, `- **Theming module:** \`${metadata.themingModule}\``, "");
165
+ }
166
+ lines.push("### Action Required", "", "Please specify the platform explicitly when calling theme generation tools:", "");
167
+ for (const alt of alternatives) {
168
+ const metadata = PLATFORM_METADATA[alt.platform];
169
+ lines.push(`- Use \`platform: '${alt.platform}'\` for ${metadata.name}`);
170
+ }
171
+ return lines;
166
172
  }
173
+ /**
174
+ * Build response text for generic (standalone) mode.
175
+ */
167
176
  function buildGenericResponse(result, hasThemingPackage) {
168
- const metadata = PLATFORM_METADATA.generic;
169
- const lines = [
170
- "## Platform Detection Result",
171
- "",
172
- `**Detected Platform:** ${metadata.name}`,
173
- `**Confidence:** ${result.confidence}`,
174
- `**Theming Module:** \`${metadata.themingModule}\``,
175
- "",
176
- metadata.description,
177
- ""
178
- ];
179
- if (result.signals && result.signals.length > 0) {
180
- lines.push(
181
- `**Detection Signals:** ${result.signals.map(formatSignal).join(", ")}`,
182
- ""
183
- );
184
- }
185
- lines.push(
186
- ...buildToolEligibilitySection(),
187
- ...buildSassConfigSection(result.signals ?? []),
188
- ...buildOutputFormatNotes(hasThemingPackage)
189
- );
190
- return lines;
177
+ const metadata = PLATFORM_METADATA.generic;
178
+ const lines = [
179
+ "## Platform Detection Result",
180
+ "",
181
+ `**Detected Platform:** ${metadata.name}`,
182
+ `**Confidence:** ${result.confidence}`,
183
+ `**Theming Module:** \`${metadata.themingModule}\``,
184
+ "",
185
+ metadata.description,
186
+ ""
187
+ ];
188
+ if (result.signals && result.signals.length > 0) lines.push(`**Detection Signals:** ${result.signals.map(formatSignal).join(", ")}`, "");
189
+ lines.push(...buildToolEligibilitySection(), ...buildSassConfigSection(result.signals ?? []), ...buildOutputFormatNotes(hasThemingPackage));
190
+ return lines;
191
191
  }
192
+ /**
193
+ * Build response text for a single detected Ignite UI platform.
194
+ */
192
195
  function buildPlatformResponse(result, licensed) {
193
- const platform = result.platform;
194
- const metadata = PLATFORM_METADATA[platform];
195
- const lines = [
196
- "## Platform Detection Result",
197
- "",
198
- `**Detected Platform:** ${metadata.name}`,
199
- `**Confidence:** ${result.confidence}`
200
- ];
201
- if (result.detectedPackage) {
202
- lines.push(`**Detected Package:** ${result.detectedPackage}`);
203
- if (platform === "angular") {
204
- const isLicensed = isLicensedPackage(result.detectedPackage);
205
- lines.push(
206
- `**Package Type:** ${isLicensed ? "Licensed (@infragistics)" : "Open Source (npm)"}`
207
- );
208
- }
209
- }
210
- if (result.signals && result.signals.length > 0) {
211
- lines.push(
212
- `**Detection Signals:** ${result.signals.map(formatSignal).join(", ")}`
213
- );
214
- }
215
- const themingModule = platform === "angular" && licensed ? metadata.licensedThemingModule : metadata.themingModule;
216
- lines.push(`**Theming Module:** \`${themingModule}\``, "");
217
- let usageLine = `When generating theme code, use \`platform: '${platform}'\``;
218
- if (platform === "angular" && licensed) {
219
- usageLine += " and `licensed: true`";
220
- }
221
- usageLine += " to ensure the correct Sass syntax is generated for this platform.";
222
- lines.push("### Usage", "", usageLine, "", metadata.description);
223
- if (result.confidence === "low") {
224
- lines.push(
225
- "",
226
- "### Note",
227
- "",
228
- "Detection confidence is **low**. This means no Ignite UI package was found, only framework packages. Please verify this is the correct platform before generating themes."
229
- );
230
- } else if (result.confidence === "medium") {
231
- lines.push(
232
- "",
233
- "### Note",
234
- "",
235
- "Detection confidence is **medium**. Consider verifying the platform if the generated code doesn't work as expected."
236
- );
237
- }
238
- return lines;
196
+ const platform = result.platform;
197
+ const metadata = PLATFORM_METADATA[platform];
198
+ const lines = [
199
+ "## Platform Detection Result",
200
+ "",
201
+ `**Detected Platform:** ${metadata.name}`,
202
+ `**Confidence:** ${result.confidence}`
203
+ ];
204
+ if (result.detectedPackage) {
205
+ lines.push(`**Detected Package:** ${result.detectedPackage}`);
206
+ if (platform === "angular") {
207
+ const isLicensed = isLicensedPackage(result.detectedPackage);
208
+ lines.push(`**Package Type:** ${isLicensed ? "Licensed (@infragistics)" : "Open Source (npm)"}`);
209
+ }
210
+ }
211
+ if (result.signals && result.signals.length > 0) lines.push(`**Detection Signals:** ${result.signals.map(formatSignal).join(", ")}`);
212
+ const themingModule = platform === "angular" && licensed ? metadata.licensedThemingModule : metadata.themingModule;
213
+ lines.push(`**Theming Module:** \`${themingModule}\``, "");
214
+ let usageLine = `When generating theme code, use \`platform: '${platform}'\``;
215
+ if (platform === "angular" && licensed) usageLine += " and `licensed: true`";
216
+ usageLine += " to ensure the correct Sass syntax is generated for this platform.";
217
+ lines.push("### Usage", "", usageLine, "", metadata.description);
218
+ if (result.confidence === "low") lines.push("", "### Note", "", "Detection confidence is **low**. This means no Ignite UI package was found, only framework packages. Please verify this is the correct platform before generating themes.");
219
+ else if (result.confidence === "medium") lines.push("", "### Note", "", "Detection confidence is **medium**. Consider verifying the platform if the generated code doesn't work as expected.");
220
+ return lines;
239
221
  }
222
+ /**
223
+ * Build response text when no platform could be determined (error/null state).
224
+ */
240
225
  function buildNullPlatformResponse(result) {
241
- return [
242
- "## Platform Detection Result",
243
- "",
244
- "**Platform:** Not detected",
245
- `**Reason:** ${result.reason}`,
246
- "",
247
- "### Recommendation",
248
- "",
249
- "Please specify the platform explicitly when calling theme generation tools:",
250
- "",
251
- "- Use `platform: 'angular'` for Ignite UI for Angular",
252
- "- Use `platform: 'webcomponents'` for Ignite UI for Web Components",
253
- "- Use `platform: 'react'` for Ignite UI for React",
254
- "- Use `platform: 'blazor'` for Ignite UI for Blazor",
255
- "- Use `platform: 'generic'` for platform-agnostic output"
256
- ];
226
+ return [
227
+ "## Platform Detection Result",
228
+ "",
229
+ "**Platform:** Not detected",
230
+ `**Reason:** ${result.reason}`,
231
+ "",
232
+ "### Recommendation",
233
+ "",
234
+ "Please specify the platform explicitly when calling theme generation tools:",
235
+ "",
236
+ "- Use `platform: 'angular'` for Ignite UI for Angular",
237
+ "- Use `platform: 'webcomponents'` for Ignite UI for Web Components",
238
+ "- Use `platform: 'react'` for Ignite UI for React",
239
+ "- Use `platform: 'blazor'` for Ignite UI for Blazor",
240
+ "- Use `platform: 'generic'` for platform-agnostic output"
241
+ ];
257
242
  }
243
+ /**
244
+ * Handle the detect_platform tool invocation.
245
+ */
258
246
  async function handleDetectPlatform(params) {
259
- const packageJsonPath = params.packageJsonPath ?? "./package.json";
260
- const resolvedPath = resolve(process.cwd(), packageJsonPath);
261
- const projectRoot = dirname(resolvedPath);
262
- let result;
263
- let parsedDeps = {};
264
- let parsedDevDeps = {};
265
- try {
266
- const packageJsonContent = await readFile(resolvedPath, "utf-8");
267
- const parseResult = packageJsonSchema.safeParse(
268
- JSON.parse(packageJsonContent)
269
- );
270
- if (!parseResult.success) {
271
- result = {
272
- platform: null,
273
- confidence: "none",
274
- signals: [],
275
- reason: `Invalid package.json structure: ${parseResult.error.message}`
276
- };
277
- } else {
278
- const packageJson = parseResult.data;
279
- parsedDeps = packageJson.dependencies ?? {};
280
- parsedDevDeps = packageJson.devDependencies ?? {};
281
- result = detectPlatformFromDependencies(
282
- parsedDeps,
283
- parsedDevDeps,
284
- projectRoot
285
- );
286
- }
287
- } catch (error) {
288
- const errorMessage = error instanceof Error ? error.message : "Unknown error";
289
- result = {
290
- platform: null,
291
- confidence: "none",
292
- signals: [],
293
- reason: `Could not read package.json: ${errorMessage}`
294
- };
295
- }
296
- const allDeps = { ...parsedDeps, ...parsedDevDeps };
297
- const hasThemingPackage = "igniteui-theming" in allDeps;
298
- const response = {
299
- platform: result.platform,
300
- confidence: result.confidence,
301
- reason: result.reason,
302
- signals: result.signals
303
- };
304
- if (result.ambiguous && result.alternatives) {
305
- response.ambiguous = true;
306
- response.alternatives = result.alternatives;
307
- }
308
- if (result.detectedPackage) {
309
- response.detectedPackage = result.detectedPackage;
310
- response.licensed = isLicensedPackage(result.detectedPackage);
311
- }
312
- if (result.platform) {
313
- const metadata = PLATFORM_METADATA[result.platform];
314
- response.platformInfo = {
315
- name: metadata.name,
316
- packageName: metadata.packageName,
317
- themingModule: metadata.themingModule,
318
- description: metadata.description
319
- };
320
- }
321
- const lines = result.ambiguous && result.alternatives ? buildAmbiguousResponse(result) : result.platform === "generic" ? buildGenericResponse(result, hasThemingPackage) : result.platform ? buildPlatformResponse(result, response.licensed) : buildNullPlatformResponse(result);
322
- return {
323
- content: [
324
- {
325
- type: "text",
326
- text: lines.join("\n")
327
- }
328
- ],
329
- structuredData: response
330
- };
247
+ const packageJsonPath = params.packageJsonPath ?? "./package.json";
248
+ const resolvedPath = resolve(process.cwd(), packageJsonPath);
249
+ const projectRoot = dirname(resolvedPath);
250
+ let result;
251
+ let parsedDeps = {};
252
+ let parsedDevDeps = {};
253
+ try {
254
+ const packageJsonContent = await readFile(resolvedPath, "utf-8");
255
+ const parseResult = packageJsonSchema.safeParse(JSON.parse(packageJsonContent));
256
+ if (!parseResult.success) result = {
257
+ platform: null,
258
+ confidence: "none",
259
+ signals: [],
260
+ reason: `Invalid package.json structure: ${parseResult.error.message}`
261
+ };
262
+ else {
263
+ const packageJson = parseResult.data;
264
+ parsedDeps = packageJson.dependencies ?? {};
265
+ parsedDevDeps = packageJson.devDependencies ?? {};
266
+ result = detectPlatformFromDependencies(parsedDeps, parsedDevDeps, projectRoot);
267
+ }
268
+ } catch (error) {
269
+ result = {
270
+ platform: null,
271
+ confidence: "none",
272
+ signals: [],
273
+ reason: `Could not read package.json: ${error instanceof Error ? error.message : "Unknown error"}`
274
+ };
275
+ }
276
+ const hasThemingPackage = "igniteui-theming" in {
277
+ ...parsedDeps,
278
+ ...parsedDevDeps
279
+ };
280
+ const response = {
281
+ platform: result.platform,
282
+ confidence: result.confidence,
283
+ reason: result.reason,
284
+ signals: result.signals
285
+ };
286
+ if (result.ambiguous && result.alternatives) {
287
+ response.ambiguous = true;
288
+ response.alternatives = result.alternatives;
289
+ }
290
+ if (result.detectedPackage) {
291
+ response.detectedPackage = result.detectedPackage;
292
+ response.licensed = isLicensedPackage(result.detectedPackage);
293
+ }
294
+ if (result.platform) {
295
+ const metadata = PLATFORM_METADATA[result.platform];
296
+ response.platformInfo = {
297
+ name: metadata.name,
298
+ packageName: metadata.packageName,
299
+ themingModule: metadata.themingModule,
300
+ description: metadata.description
301
+ };
302
+ }
303
+ return {
304
+ content: [{
305
+ type: "text",
306
+ text: (result.ambiguous && result.alternatives ? buildAmbiguousResponse(result) : result.platform === "generic" ? buildGenericResponse(result, hasThemingPackage) : result.platform ? buildPlatformResponse(result, response.licensed) : buildNullPlatformResponse(result)).join("\n")
307
+ }],
308
+ structuredData: response
309
+ };
331
310
  }
332
- export {
333
- handleDetectPlatform
334
- };
311
+ //#endregion
312
+ export { handleDetectPlatform };