mcp-probe-kit 1.13.0 → 1.15.1

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 (46) hide show
  1. package/README.md +54 -3
  2. package/build/index.js +14 -1
  3. package/build/schemas/index.d.ts +108 -0
  4. package/build/schemas/index.js +2 -0
  5. package/build/schemas/ui-ux-schemas.d.ts +248 -0
  6. package/build/schemas/ui-ux-schemas.js +147 -0
  7. package/build/tools/__tests__/start_ui.integration.test.d.ts +6 -0
  8. package/build/tools/__tests__/start_ui.integration.test.js +179 -0
  9. package/build/tools/__tests__/start_ui.property.test.d.ts +6 -0
  10. package/build/tools/__tests__/start_ui.property.test.js +263 -0
  11. package/build/tools/__tests__/start_ui.unit.test.d.ts +6 -0
  12. package/build/tools/__tests__/start_ui.unit.test.js +109 -0
  13. package/build/tools/index.d.ts +4 -0
  14. package/build/tools/index.js +5 -0
  15. package/build/tools/init_component_catalog.d.ts +22 -0
  16. package/build/tools/init_component_catalog.js +809 -0
  17. package/build/tools/render_ui.d.ts +22 -0
  18. package/build/tools/render_ui.js +384 -0
  19. package/build/tools/start_ui.d.ts +25 -0
  20. package/build/tools/start_ui.js +299 -0
  21. package/build/tools/ui-ux-tools.d.ts +116 -0
  22. package/build/tools/ui-ux-tools.js +756 -0
  23. package/build/tools/ui-ux-tools.test.d.ts +6 -0
  24. package/build/tools/ui-ux-tools.test.js +132 -0
  25. package/build/utils/ascii-box-formatter.d.ts +29 -0
  26. package/build/utils/ascii-box-formatter.js +195 -0
  27. package/build/utils/bm25.d.ts +60 -0
  28. package/build/utils/bm25.js +139 -0
  29. package/build/utils/cache-manager.d.ts +65 -0
  30. package/build/utils/cache-manager.js +156 -0
  31. package/build/utils/design-reasoning-engine.d.ts +158 -0
  32. package/build/utils/design-reasoning-engine.js +363 -0
  33. package/build/utils/design-system-json-formatter.d.ts +41 -0
  34. package/build/utils/design-system-json-formatter.js +165 -0
  35. package/build/utils/ui-data-loader.d.ts +56 -0
  36. package/build/utils/ui-data-loader.js +164 -0
  37. package/build/utils/ui-search-engine.d.ts +57 -0
  38. package/build/utils/ui-search-engine.js +123 -0
  39. package/build/utils/ui-sync.d.ts +13 -0
  40. package/build/utils/ui-sync.js +241 -0
  41. package/docs/BEST_PRACTICES.md +257 -1
  42. package/docs/HOW_TO_TRIGGER.md +71 -1
  43. package/docs/MCP-Probe-Kit-/344/275/277/347/224/250/346/211/213/345/206/214.html +89 -29
  44. package/docs/MCP-Probe-Kit-/344/275/277/347/224/250/346/211/213/345/206/214.md +582 -1
  45. package/package.json +19 -6
  46. package/docs/HOW_TO_TRIGGER.html +0 -255
@@ -0,0 +1,156 @@
1
+ /**
2
+ * 缓存管理器
3
+ *
4
+ * 管理 UI/UX 数据的缓存,支持版本检查和自动更新
5
+ */
6
+ import * as fs from 'fs';
7
+ import * as path from 'path';
8
+ import * as os from 'os';
9
+ import * as https from 'https';
10
+ /**
11
+ * 缓存管理器
12
+ */
13
+ export class CacheManager {
14
+ cacheDir;
15
+ packageName;
16
+ autoUpdate;
17
+ constructor(options = {}) {
18
+ this.cacheDir = options.cacheDir || path.join(os.homedir(), '.mcp-probe-kit', 'ui-ux-data');
19
+ this.packageName = options.packageName || 'uipro-cli';
20
+ this.autoUpdate = options.autoUpdate ?? true;
21
+ this.ensureCacheDir();
22
+ }
23
+ /**
24
+ * 确保缓存目录存在
25
+ */
26
+ ensureCacheDir() {
27
+ if (!fs.existsSync(this.cacheDir)) {
28
+ fs.mkdirSync(this.cacheDir, { recursive: true });
29
+ }
30
+ }
31
+ /**
32
+ * 获取缓存元数据
33
+ */
34
+ getMetadata() {
35
+ const metadataPath = path.join(this.cacheDir, 'metadata.json');
36
+ if (!fs.existsSync(metadataPath)) {
37
+ return null;
38
+ }
39
+ try {
40
+ const content = fs.readFileSync(metadataPath, 'utf-8');
41
+ return JSON.parse(content);
42
+ }
43
+ catch (error) {
44
+ console.error('Failed to read cache metadata:', error);
45
+ return null;
46
+ }
47
+ }
48
+ /**
49
+ * 检查缓存是否存在
50
+ */
51
+ hasCache() {
52
+ return this.getMetadata() !== null;
53
+ }
54
+ /**
55
+ * 获取 npm 包的最新版本
56
+ */
57
+ async getLatestVersion() {
58
+ return new Promise((resolve, reject) => {
59
+ const url = `https://registry.npmjs.org/${this.packageName}/latest`;
60
+ https.get(url, (res) => {
61
+ let data = '';
62
+ res.on('data', (chunk) => {
63
+ data += chunk;
64
+ });
65
+ res.on('end', () => {
66
+ try {
67
+ const pkg = JSON.parse(data);
68
+ resolve(pkg.version);
69
+ }
70
+ catch (error) {
71
+ reject(new Error(`Failed to parse package metadata: ${error}`));
72
+ }
73
+ });
74
+ }).on('error', (error) => {
75
+ reject(new Error(`Failed to fetch package metadata: ${error}`));
76
+ });
77
+ });
78
+ }
79
+ /**
80
+ * 检查是否有更新
81
+ */
82
+ async checkUpdate() {
83
+ const metadata = this.getMetadata();
84
+ const latestVersion = await this.getLatestVersion();
85
+ if (!metadata) {
86
+ return {
87
+ hasUpdate: true,
88
+ latestVersion,
89
+ };
90
+ }
91
+ return {
92
+ hasUpdate: metadata.version !== latestVersion,
93
+ currentVersion: metadata.version,
94
+ latestVersion,
95
+ };
96
+ }
97
+ /**
98
+ * 读取缓存文件
99
+ */
100
+ readFile(filename) {
101
+ const filePath = path.join(this.cacheDir, filename);
102
+ if (!fs.existsSync(filePath)) {
103
+ return null;
104
+ }
105
+ try {
106
+ const content = fs.readFileSync(filePath, 'utf-8');
107
+ if (filename.endsWith('.json')) {
108
+ return JSON.parse(content);
109
+ }
110
+ return content;
111
+ }
112
+ catch (error) {
113
+ console.error(`Failed to read cache file ${filename}:`, error);
114
+ return null;
115
+ }
116
+ }
117
+ /**
118
+ * 列出所有缓存文件
119
+ */
120
+ listFiles() {
121
+ if (!fs.existsSync(this.cacheDir)) {
122
+ return [];
123
+ }
124
+ const files = [];
125
+ const walk = (dir, prefix = '') => {
126
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
127
+ for (const entry of entries) {
128
+ const fullPath = path.join(dir, entry.name);
129
+ const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
130
+ if (entry.isDirectory()) {
131
+ walk(fullPath, relativePath);
132
+ }
133
+ else if (entry.isFile() && entry.name !== 'metadata.json') {
134
+ files.push(relativePath);
135
+ }
136
+ }
137
+ };
138
+ walk(this.cacheDir);
139
+ return files;
140
+ }
141
+ /**
142
+ * 清空缓存
143
+ */
144
+ clear() {
145
+ if (fs.existsSync(this.cacheDir)) {
146
+ fs.rmSync(this.cacheDir, { recursive: true, force: true });
147
+ }
148
+ this.ensureCacheDir();
149
+ }
150
+ /**
151
+ * 获取缓存目录路径
152
+ */
153
+ getCacheDir() {
154
+ return this.cacheDir;
155
+ }
156
+ }
@@ -0,0 +1,158 @@
1
+ /**
2
+ * 设计推理引擎
3
+ *
4
+ * 根据产品类型和需求,智能推荐完整的设计系统
5
+ * 基于 100 条行业规则和最佳实践
6
+ *
7
+ * 完全匹配原版 Python 实现:
8
+ * https://github.com/nextlevelbuilder/ui-ux-pro-max-skill
9
+ */
10
+ export interface DesignRequest {
11
+ productType: string;
12
+ description?: string;
13
+ stack?: string;
14
+ targetAudience?: string;
15
+ keywords?: string[];
16
+ }
17
+ export interface ReasoningRule {
18
+ No: string;
19
+ UI_Category: string;
20
+ Recommended_Pattern: string;
21
+ Style_Priority: string;
22
+ Color_Mood: string;
23
+ Typography_Mood: string;
24
+ Key_Effects: string;
25
+ Decision_Rules: Record<string, string>;
26
+ Anti_Patterns: string;
27
+ Severity: string;
28
+ }
29
+ export interface DesignSystemRecommendation {
30
+ target: string;
31
+ pattern: LandingPagePattern;
32
+ style: StyleRecommendation;
33
+ colors: ColorPalette;
34
+ typography: TypographyPairing;
35
+ effects: EffectsRecommendation;
36
+ antiPatterns: string[];
37
+ checklist: string[];
38
+ reasoning: string;
39
+ }
40
+ export interface LandingPagePattern {
41
+ name: string;
42
+ description: string;
43
+ conversion: string;
44
+ cta: string;
45
+ sections: string[];
46
+ }
47
+ export interface StyleRecommendation {
48
+ primary: string;
49
+ keywords: string[];
50
+ bestFor: string[];
51
+ performance: string;
52
+ accessibility: string;
53
+ }
54
+ export interface ColorPalette {
55
+ primary: string;
56
+ secondary: string;
57
+ cta: string;
58
+ background: string;
59
+ text: string;
60
+ notes: string;
61
+ }
62
+ export interface TypographyPairing {
63
+ heading: string;
64
+ body: string;
65
+ mood: string;
66
+ bestFor: string[];
67
+ googleFontsUrl: string;
68
+ }
69
+ export interface EffectsRecommendation {
70
+ shadows: string;
71
+ transitions: string;
72
+ hover: string;
73
+ animations: string;
74
+ }
75
+ /**
76
+ * 设计推理引擎(完全匹配 Python 版本)
77
+ */
78
+ export declare class DesignReasoningEngine {
79
+ private productsData;
80
+ private stylesData;
81
+ private colorsData;
82
+ private typographyData;
83
+ private landingData;
84
+ private uxGuidelinesData;
85
+ private reasoningData;
86
+ /**
87
+ * 加载数据
88
+ */
89
+ loadData(data: {
90
+ products: any[];
91
+ styles: any[];
92
+ colors: any[];
93
+ typography: any[];
94
+ landing: any[];
95
+ uxGuidelines: any[];
96
+ reasoning: ReasoningRule[];
97
+ }): void;
98
+ /**
99
+ * 生成设计系统推荐(完全匹配 Python 版本)
100
+ */
101
+ generateRecommendation(request: DesignRequest): DesignSystemRecommendation;
102
+ /**
103
+ * 应用推理规则
104
+ */
105
+ private applyReasoning;
106
+ /**
107
+ * 查找推理规则
108
+ */
109
+ private findReasoningRule;
110
+ /**
111
+ * 搜索风格(带优先级)
112
+ */
113
+ private searchStyles;
114
+ /**
115
+ * 搜索配色
116
+ */
117
+ private searchColors;
118
+ /**
119
+ * 搜索字体
120
+ */
121
+ private searchTypography;
122
+ /**
123
+ * 搜索落地页模式
124
+ */
125
+ private searchLanding;
126
+ /**
127
+ * 带评分的搜索
128
+ */
129
+ private searchWithScore;
130
+ /**
131
+ * 选择最佳匹配(基于优先级)
132
+ */
133
+ private selectBestMatch;
134
+ /**
135
+ * 提取阴影效果
136
+ */
137
+ private extractShadows;
138
+ /**
139
+ * 提取过渡时间
140
+ */
141
+ private extractTransitions;
142
+ /**
143
+ * 提取悬停效果
144
+ */
145
+ private extractHover;
146
+ /**
147
+ * 匹配产品类型(增强版 - 使用 BM25 风格的匹配)
148
+ */
149
+ private matchProductType;
150
+ /**
151
+ * 生成检查清单
152
+ */
153
+ private generateChecklist;
154
+ /**
155
+ * 生成推理说明
156
+ */
157
+ private generateReasoningText;
158
+ }
@@ -0,0 +1,363 @@
1
+ /**
2
+ * 设计推理引擎
3
+ *
4
+ * 根据产品类型和需求,智能推荐完整的设计系统
5
+ * 基于 100 条行业规则和最佳实践
6
+ *
7
+ * 完全匹配原版 Python 实现:
8
+ * https://github.com/nextlevelbuilder/ui-ux-pro-max-skill
9
+ */
10
+ /**
11
+ * 设计推理引擎(完全匹配 Python 版本)
12
+ */
13
+ export class DesignReasoningEngine {
14
+ productsData = [];
15
+ stylesData = [];
16
+ colorsData = [];
17
+ typographyData = [];
18
+ landingData = [];
19
+ uxGuidelinesData = [];
20
+ reasoningData = [];
21
+ /**
22
+ * 加载数据
23
+ */
24
+ loadData(data) {
25
+ this.productsData = data.products;
26
+ this.stylesData = data.styles;
27
+ this.colorsData = data.colors;
28
+ this.typographyData = data.typography;
29
+ this.landingData = data.landing;
30
+ this.uxGuidelinesData = data.uxGuidelines;
31
+ this.reasoningData = data.reasoning;
32
+ }
33
+ /**
34
+ * 生成设计系统推荐(完全匹配 Python 版本)
35
+ */
36
+ generateRecommendation(request) {
37
+ // Step 1: 匹配产品类型
38
+ const productRule = this.matchProductType(request);
39
+ const category = productRule['Product Type'] || 'General';
40
+ // Step 2: 应用推理规则
41
+ const reasoning = this.applyReasoning(category);
42
+ const stylePriority = reasoning.stylePriority;
43
+ // Step 3: 使用优先级搜索各个领域
44
+ const styleResults = this.searchStyles(request, stylePriority);
45
+ const colorResults = this.searchColors(request);
46
+ const typographyResults = this.searchTypography(request);
47
+ const landingResults = this.searchLanding(request);
48
+ // Step 4: 选择最佳匹配
49
+ const bestStyle = this.selectBestMatch(styleResults, stylePriority);
50
+ const bestColor = colorResults[0] || {};
51
+ const bestTypography = typographyResults[0] || {};
52
+ const bestLanding = landingResults[0] || {};
53
+ // Step 5: 组合效果(优先使用搜索结果)
54
+ const styleEffects = bestStyle['Effects & Animation'] || '';
55
+ const reasoningEffects = reasoning.keyEffects;
56
+ const combinedEffects = styleEffects || reasoningEffects;
57
+ // Step 6: 构建最终推荐
58
+ return {
59
+ target: request.productType,
60
+ pattern: {
61
+ name: bestLanding.Pattern || reasoning.pattern,
62
+ description: bestLanding.Description || '',
63
+ conversion: bestLanding['Conversion Focus'] || '',
64
+ cta: bestLanding['CTA Placement'] || 'Above fold',
65
+ sections: (bestLanding['Section Order'] || 'Hero, Features, CTA').split(',').map((s) => s.trim()),
66
+ },
67
+ style: {
68
+ primary: bestStyle['Style Category'] || 'Minimalism',
69
+ keywords: (bestStyle.Keywords || '').split(',').map((k) => k.trim()),
70
+ bestFor: (bestStyle['Best For'] || '').split(',').map((b) => b.trim()),
71
+ performance: bestStyle.Performance || 'Good',
72
+ accessibility: bestStyle.Accessibility || 'WCAG AA',
73
+ },
74
+ colors: {
75
+ primary: bestColor['Primary (Hex)'] || '#2563EB',
76
+ secondary: bestColor['Secondary (Hex)'] || '#3B82F6',
77
+ cta: bestColor['CTA (Hex)'] || '#F97316',
78
+ background: bestColor['Background (Hex)'] || '#F8FAFC',
79
+ text: bestColor['Text (Hex)'] || '#1E293B',
80
+ notes: bestColor.Notes || bestColor.Usage || '',
81
+ },
82
+ typography: {
83
+ heading: bestTypography['Heading Font'] || 'Inter',
84
+ body: bestTypography['Body Font'] || 'Inter',
85
+ mood: bestTypography['Mood/Style Keywords'] || reasoning.typographyMood,
86
+ bestFor: (bestTypography['Best For'] || '').split(',').map((b) => b.trim()),
87
+ googleFontsUrl: bestTypography['Google Fonts URL'] || '',
88
+ },
89
+ effects: {
90
+ shadows: this.extractShadows(combinedEffects),
91
+ transitions: this.extractTransitions(combinedEffects),
92
+ hover: this.extractHover(combinedEffects),
93
+ animations: combinedEffects,
94
+ },
95
+ antiPatterns: (reasoning.antiPatterns || '').split('+').map((p) => p.trim()).filter(Boolean),
96
+ checklist: this.generateChecklist(bestStyle, request),
97
+ reasoning: this.generateReasoningText(productRule, bestStyle, bestColor, bestTypography, reasoning),
98
+ };
99
+ }
100
+ /**
101
+ * 应用推理规则
102
+ */
103
+ applyReasoning(category) {
104
+ const rule = this.findReasoningRule(category);
105
+ if (!rule) {
106
+ return {
107
+ pattern: 'Hero + Features + CTA',
108
+ stylePriority: ['Minimalism', 'Flat Design'],
109
+ colorMood: 'Professional',
110
+ typographyMood: 'Clean',
111
+ keyEffects: 'Subtle hover transitions',
112
+ antiPatterns: '',
113
+ decisionRules: {},
114
+ severity: 'MEDIUM',
115
+ };
116
+ }
117
+ return {
118
+ pattern: rule.Recommended_Pattern || 'Hero + Features + CTA',
119
+ stylePriority: (rule.Style_Priority || '').split('+').map((s) => s.trim()),
120
+ colorMood: rule.Color_Mood || '',
121
+ typographyMood: rule.Typography_Mood || '',
122
+ keyEffects: rule.Key_Effects || '',
123
+ antiPatterns: rule.Anti_Patterns || '',
124
+ decisionRules: rule.Decision_Rules || {},
125
+ severity: rule.Severity || 'MEDIUM',
126
+ };
127
+ }
128
+ /**
129
+ * 查找推理规则
130
+ */
131
+ findReasoningRule(category) {
132
+ const categoryLower = category.toLowerCase();
133
+ // 1. 精确匹配
134
+ for (const rule of this.reasoningData) {
135
+ if (rule.UI_Category.toLowerCase() === categoryLower) {
136
+ return rule;
137
+ }
138
+ }
139
+ // 2. 部分匹配
140
+ for (const rule of this.reasoningData) {
141
+ const uiCat = rule.UI_Category.toLowerCase();
142
+ if (uiCat.includes(categoryLower) || categoryLower.includes(uiCat)) {
143
+ return rule;
144
+ }
145
+ }
146
+ // 3. 关键词匹配
147
+ for (const rule of this.reasoningData) {
148
+ const uiCat = rule.UI_Category.toLowerCase();
149
+ const keywords = uiCat.replace(/[\/\-]/g, ' ').split(/\s+/);
150
+ if (keywords.some(kw => categoryLower.includes(kw))) {
151
+ return rule;
152
+ }
153
+ }
154
+ return null;
155
+ }
156
+ /**
157
+ * 搜索风格(带优先级)
158
+ */
159
+ searchStyles(request, priority) {
160
+ const query = `${request.productType} ${request.description || ''} ${priority.join(' ')}`.toLowerCase();
161
+ return this.searchWithScore(this.stylesData, query, ['Style Category', 'Keywords', 'Best For']).slice(0, 3);
162
+ }
163
+ /**
164
+ * 搜索配色
165
+ */
166
+ searchColors(request) {
167
+ const query = `${request.productType} ${request.description || ''}`.toLowerCase();
168
+ return this.searchWithScore(this.colorsData, query, ['Name', 'Usage', 'Product Type']).slice(0, 2);
169
+ }
170
+ /**
171
+ * 搜索字体
172
+ */
173
+ searchTypography(request) {
174
+ const query = `${request.productType} ${request.description || ''}`.toLowerCase();
175
+ return this.searchWithScore(this.typographyData, query, ['Mood/Style Keywords', 'Best For']).slice(0, 2);
176
+ }
177
+ /**
178
+ * 搜索落地页模式
179
+ */
180
+ searchLanding(request) {
181
+ const query = `${request.productType} ${request.description || ''}`.toLowerCase();
182
+ return this.searchWithScore(this.landingData, query, ['Pattern', 'Keywords']).slice(0, 2);
183
+ }
184
+ /**
185
+ * 带评分的搜索
186
+ */
187
+ searchWithScore(data, query, fields) {
188
+ const scored = data.map(item => {
189
+ let score = 0;
190
+ const text = fields.map(f => item[f] || '').join(' ').toLowerCase();
191
+ // 简单的关键词匹配评分
192
+ const queryWords = query.split(/\s+/).filter(w => w.length > 2);
193
+ for (const word of queryWords) {
194
+ if (text.includes(word)) {
195
+ score += 1;
196
+ }
197
+ }
198
+ return { item, score };
199
+ });
200
+ return scored
201
+ .filter(s => s.score > 0)
202
+ .sort((a, b) => b.score - a.score)
203
+ .map(s => s.item);
204
+ }
205
+ /**
206
+ * 选择最佳匹配(基于优先级)
207
+ */
208
+ selectBestMatch(results, priorityKeywords) {
209
+ if (!results || results.length === 0) {
210
+ return {};
211
+ }
212
+ if (!priorityKeywords || priorityKeywords.length === 0) {
213
+ return results[0];
214
+ }
215
+ // 1. 尝试精确风格名称匹配
216
+ for (const priority of priorityKeywords) {
217
+ const priorityLower = priority.toLowerCase().trim();
218
+ for (const result of results) {
219
+ const styleName = (result['Style Category'] || '').toLowerCase();
220
+ if (priorityLower === styleName || styleName.includes(priorityLower) || priorityLower.includes(styleName)) {
221
+ return result;
222
+ }
223
+ }
224
+ }
225
+ // 2. 按关键词评分
226
+ const scored = results.map(result => {
227
+ let score = 0;
228
+ const resultStr = JSON.stringify(result).toLowerCase();
229
+ for (const kw of priorityKeywords) {
230
+ const kwLower = kw.toLowerCase().trim();
231
+ // 风格名称匹配(高分)
232
+ if ((result['Style Category'] || '').toLowerCase().includes(kwLower)) {
233
+ score += 10;
234
+ }
235
+ // 关键词字段匹配(中分)
236
+ else if ((result.Keywords || '').toLowerCase().includes(kwLower)) {
237
+ score += 3;
238
+ }
239
+ // 其他字段匹配(低分)
240
+ else if (resultStr.includes(kwLower)) {
241
+ score += 1;
242
+ }
243
+ }
244
+ return { result, score };
245
+ });
246
+ scored.sort((a, b) => b.score - a.score);
247
+ return scored[0] && scored[0].score > 0 ? scored[0].result : results[0];
248
+ }
249
+ /**
250
+ * 提取阴影效果
251
+ */
252
+ extractShadows(effects) {
253
+ if (effects.toLowerCase().includes('shadow')) {
254
+ const match = effects.match(/([^+]*shadow[^+]*)/i);
255
+ return match ? match[1].trim() : 'Subtle shadows';
256
+ }
257
+ return 'Subtle shadows';
258
+ }
259
+ /**
260
+ * 提取过渡时间
261
+ */
262
+ extractTransitions(effects) {
263
+ const match = effects.match(/(\d+[-–]\d+ms|\d+ms)/);
264
+ return match ? match[0] : '200-300ms';
265
+ }
266
+ /**
267
+ * 提取悬停效果
268
+ */
269
+ extractHover(effects) {
270
+ if (effects.toLowerCase().includes('hover')) {
271
+ const match = effects.match(/([^+]*hover[^+]*)/i);
272
+ return match ? match[1].trim() : 'Gentle hover states';
273
+ }
274
+ return 'Gentle hover states';
275
+ }
276
+ /**
277
+ * 匹配产品类型(增强版 - 使用 BM25 风格的匹配)
278
+ */
279
+ matchProductType(request) {
280
+ const query = `${request.productType} ${request.description || ''} ${request.keywords?.join(' ') || ''}`.toLowerCase();
281
+ // 使用更智能的匹配算法
282
+ let bestMatch = this.productsData[0] || {}; // 默认使用第一个
283
+ let bestScore = 0;
284
+ for (const product of this.productsData) {
285
+ const keywords = (product.Keywords || '').toLowerCase();
286
+ const productType = (product['Product Type'] || '').toLowerCase();
287
+ const considerations = (product['Key Considerations'] || '').toLowerCase();
288
+ let score = 0;
289
+ // 1. 精确匹配产品类型(最高权重)
290
+ if (query.includes(productType)) {
291
+ score += 20;
292
+ }
293
+ // 2. 部分匹配产品类型
294
+ const productWords = productType.split(/[\s/]+/);
295
+ for (const word of productWords) {
296
+ if (word.length > 2 && query.includes(word)) {
297
+ score += 5;
298
+ }
299
+ }
300
+ // 3. 关键词匹配(中等权重)
301
+ const keywordList = keywords.split(',').map((k) => k.trim());
302
+ for (const keyword of keywordList) {
303
+ if (keyword.length > 2 && query.includes(keyword)) {
304
+ score += 2;
305
+ }
306
+ }
307
+ // 4. 考虑因素匹配(低权重)
308
+ const considerationWords = considerations.split(/[\s,\.]+/);
309
+ for (const word of considerationWords) {
310
+ if (word.length > 3 && query.includes(word)) {
311
+ score += 0.5;
312
+ }
313
+ }
314
+ if (score > bestScore) {
315
+ bestScore = score;
316
+ bestMatch = product;
317
+ }
318
+ }
319
+ return bestMatch;
320
+ }
321
+ /**
322
+ * 生成检查清单
323
+ */
324
+ generateChecklist(style, request) {
325
+ const transitions = this.extractTransitions(style['Effects & Animation'] || '');
326
+ const checklist = [
327
+ 'No emojis as icons (use SVG: Heroicons/Lucide)',
328
+ 'cursor-pointer on all clickable elements',
329
+ `Hover states with smooth transitions (${transitions})`,
330
+ 'Light mode: text contrast 4.5:1 minimum',
331
+ 'Focus states visible for keyboard nav',
332
+ 'prefers-reduced-motion respected',
333
+ 'Responsive: 375px, 768px, 1024px, 1440px',
334
+ 'Loading states for async operations',
335
+ 'Error states with clear messages',
336
+ 'Alt text for all images',
337
+ ];
338
+ // 根据技术栈添加特定检查项
339
+ if (request.stack?.toLowerCase().includes('react')) {
340
+ checklist.push('React keys on list items');
341
+ checklist.push('useCallback for event handlers');
342
+ }
343
+ else if (request.stack?.toLowerCase().includes('vue')) {
344
+ checklist.push('v-for with :key bindings');
345
+ checklist.push('Computed properties for derived state');
346
+ }
347
+ return checklist;
348
+ }
349
+ /**
350
+ * 生成推理说明
351
+ */
352
+ generateReasoningText(productRule, style, colors, typography, reasoning) {
353
+ const parts = [];
354
+ parts.push(`Product Type: ${productRule['Product Type']}`);
355
+ parts.push(`Recommended Style: ${style['Style Category'] || reasoning.stylePriority[0]}`);
356
+ parts.push(`Reasoning: ${(style['Best For'] || '').split(',').slice(0, 3).join(', ')}`);
357
+ parts.push(`Color Strategy: ${colors.Notes || colors.Usage || reasoning.colorMood}`);
358
+ parts.push(`Typography Mood: ${typography['Mood/Style Keywords'] || reasoning.typographyMood}`);
359
+ parts.push(`Performance: ${style.Performance || 'Good'}`);
360
+ parts.push(`Accessibility: ${style.Accessibility || 'WCAG AA'}`);
361
+ return parts.join('\n');
362
+ }
363
+ }