mta-mcp 3.15.0 → 3.15.2
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.js
CHANGED
|
@@ -2893,7 +2893,7 @@ init_smartAgentMatcher();
|
|
|
2893
2893
|
async function getCompactStandards(args) {
|
|
2894
2894
|
const logger4 = new ConsoleLogger();
|
|
2895
2895
|
const manager = new StandardsManager();
|
|
2896
|
-
const mode = args.mode
|
|
2896
|
+
const mode = normalizeMode(args.mode);
|
|
2897
2897
|
try {
|
|
2898
2898
|
let context = detectContext(args, logger4);
|
|
2899
2899
|
if (args.projectPath && fs12.existsSync(args.projectPath)) {
|
|
@@ -2905,16 +2905,16 @@ async function getCompactStandards(args) {
|
|
|
2905
2905
|
imports: context.imports,
|
|
2906
2906
|
scenario: args.scenario || context.scenario
|
|
2907
2907
|
});
|
|
2908
|
-
|
|
2908
|
+
const responseContent = {};
|
|
2909
2909
|
switch (mode) {
|
|
2910
2910
|
case "summary":
|
|
2911
|
-
responseContent
|
|
2911
|
+
Object.assign(responseContent, buildSummaryResponse(standardUris, manager));
|
|
2912
2912
|
break;
|
|
2913
2913
|
case "key-rules":
|
|
2914
|
-
responseContent
|
|
2914
|
+
Object.assign(responseContent, buildKeyRulesResponse(standardUris, context, manager));
|
|
2915
2915
|
break;
|
|
2916
2916
|
case "full":
|
|
2917
|
-
responseContent
|
|
2917
|
+
Object.assign(responseContent, buildFullResponse(standardUris, manager));
|
|
2918
2918
|
break;
|
|
2919
2919
|
}
|
|
2920
2920
|
if (args.projectPath && context.userConfiguredAgents) {
|
|
@@ -2942,6 +2942,12 @@ async function getCompactStandards(args) {
|
|
|
2942
2942
|
};
|
|
2943
2943
|
}
|
|
2944
2944
|
}
|
|
2945
|
+
function normalizeMode(mode) {
|
|
2946
|
+
if (mode === "summary" || mode === "key-rules" || mode === "full") {
|
|
2947
|
+
return mode;
|
|
2948
|
+
}
|
|
2949
|
+
return "key-rules";
|
|
2950
|
+
}
|
|
2945
2951
|
async function analyzeProject2(projectPath, logger4) {
|
|
2946
2952
|
var _a, _b;
|
|
2947
2953
|
const result = {
|
|
@@ -3330,7 +3336,12 @@ function getFlutterDesignRestorationKeyRules() {
|
|
|
3330
3336
|
### \u6570\u636E\u4F7F\u7528\u4F18\u5148\u7EA7
|
|
3331
3337
|
- \u9996\u8F6E\u4EE3\u7801\u751F\u6210: \`compact.restorationContract\` + \`compact.tree\`
|
|
3332
3338
|
- \u5C40\u90E8\u6837\u5F0F/\u56FE\u6807\u8BCA\u65AD: \u518D\u8865\u8BFB \`measure\` \u6216 \`style\`
|
|
3333
|
-
- \u53EA\u8981 contract \u4E0E\u73B0\u6709\u4E1A\u52A1\u7EC4\u4EF6\u51B2\u7A81\uFF0C\u4F18\u5148\u4FDD\u8BC1\u8BBE\u8BA1\u7A3F\u7ED3\u6784\u4E00\u81F4\uFF0C\u518D\u51B3\u5B9A\u662F\u5426\u590D\u7528\u7EC4\u4EF6
|
|
3339
|
+
- \u53EA\u8981 contract \u4E0E\u73B0\u6709\u4E1A\u52A1\u7EC4\u4EF6\u51B2\u7A81\uFF0C\u4F18\u5148\u4FDD\u8BC1\u8BBE\u8BA1\u7A3F\u7ED3\u6784\u4E00\u81F4\uFF0C\u518D\u51B3\u5B9A\u662F\u5426\u590D\u7528\u7EC4\u4EF6`,
|
|
3340
|
+
`### SVG / \u8D44\u6E90\u56DE\u5F52\u68C0\u67E5
|
|
3341
|
+
- \u5982\u679C \`iconSvgMeta.colorStrategy=preserve\`\uFF0C\u6216 SVG \u81EA\u5E26 \`fill-opacity\` / \u6E10\u53D8 / \u591A\u8272\u8DEF\u5F84\uFF0C**\u7981\u6B62**\u518D\u52A0 \`ColorFilter\`
|
|
3342
|
+
- \u65E7\u9875\u9762\u7684\u540C\u540D\u7BAD\u5934/\u56FE\u6807\u8D44\u6E90\u9ED8\u8BA4\u4E0D\u53EF\u4FE1\uFF1B\u5C40\u90E8\u56FE\u6807\u504F\u5DEE\u4F18\u5148\u4ECE\u5F53\u524D\u753B\u677F\u91CD\u65B0\u5BFC\u51FA\uFF0C\u4E0D\u76F4\u63A5\u590D\u7528\u5168\u5C40\u8D44\u4EA7
|
|
3343
|
+
- 16\xD716 \u901A\u5E38\u662F\u70B9\u51FB\u69FD\u4F4D\u800C\u975E\u56FE\u5F62\u771F\u5B9E\u5C3A\u5BF8\uFF1B\u5F53 compact/measure \u7ED9\u51FA \`containerSize\` \u4E0E \`contentSize\` \u65F6\uFF0C\u5FC5\u987B\u5206\u79BB\u5360\u4F4D\u4E0E\u6E32\u67D3
|
|
3344
|
+
- Sketch \u7684 \`Image\` \u56FE\u5C42\u5FC5\u987B\u5BFC\u51FA PNG + \`Image.asset\`\uFF0C\u4E0D\u8981\u4F2A\u88C5\u6210 SVG \u7528 \`SvgPicture\``
|
|
3334
3345
|
];
|
|
3335
3346
|
}
|
|
3336
3347
|
function isDesignRestorationScenario(scenario, imports) {
|
|
@@ -5495,6 +5506,59 @@ function ensureWorkspaceConfig(workspacePath) {
|
|
|
5495
5506
|
|
|
5496
5507
|
// src/tools/queryTroubleshootingCases.ts
|
|
5497
5508
|
var logger2 = createLogger("QueryTroubleshootingCases");
|
|
5509
|
+
var KEYWORD_ALIAS_MAP = {
|
|
5510
|
+
svg: ["\u77E2\u91CF", "svgpicture", "svgpicture.asset"],
|
|
5511
|
+
color: ["\u989C\u8272", "\u8272\u503C", "\u53D1\u7070", "\u53D1\u6D45", "\u53D8\u6DE1"],
|
|
5512
|
+
colorfilter: ["color filter", "\u4E8C\u6B21\u67D3\u8272", "\u67D3\u8272"],
|
|
5513
|
+
opacity: ["\u900F\u660E\u5EA6", "fill-opacity", "alpha"],
|
|
5514
|
+
viewbox: ["view box", "\u89C6\u53E3"],
|
|
5515
|
+
icon: ["\u56FE\u6807", "badge", "flag"],
|
|
5516
|
+
chevron: ["arrow", "\u7BAD\u5934"],
|
|
5517
|
+
bitmap: ["\u4F4D\u56FE", "png", "image.asset"],
|
|
5518
|
+
layout: ["\u5E03\u5C40", "spacing", "\u95F4\u8DDD", "\u504F\u79FB"],
|
|
5519
|
+
sketch: ["\u8BBE\u8BA1\u7A3F", "\u8FD8\u539F", "measure", "restoration"]
|
|
5520
|
+
};
|
|
5521
|
+
function normalizeKeyword(value) {
|
|
5522
|
+
return value.trim().toLowerCase();
|
|
5523
|
+
}
|
|
5524
|
+
function expandKeywords(keywords) {
|
|
5525
|
+
const expanded = /* @__PURE__ */ new Set();
|
|
5526
|
+
for (const keyword of keywords) {
|
|
5527
|
+
const normalized = normalizeKeyword(keyword);
|
|
5528
|
+
if (!normalized) continue;
|
|
5529
|
+
expanded.add(normalized);
|
|
5530
|
+
for (const [canonical, aliases] of Object.entries(KEYWORD_ALIAS_MAP)) {
|
|
5531
|
+
if (canonical === normalized || aliases.some((alias) => normalized.includes(alias) || alias.includes(normalized))) {
|
|
5532
|
+
expanded.add(canonical);
|
|
5533
|
+
for (const alias of aliases) {
|
|
5534
|
+
expanded.add(alias);
|
|
5535
|
+
}
|
|
5536
|
+
}
|
|
5537
|
+
}
|
|
5538
|
+
}
|
|
5539
|
+
return [...expanded];
|
|
5540
|
+
}
|
|
5541
|
+
function inferImplicitTags(id, title, problemType, content) {
|
|
5542
|
+
const source = `${id} ${title} ${problemType} ${content}`.toLowerCase();
|
|
5543
|
+
const inferred = /* @__PURE__ */ new Set();
|
|
5544
|
+
for (const [canonical, aliases] of Object.entries(KEYWORD_ALIAS_MAP)) {
|
|
5545
|
+
const candidates = [canonical, ...aliases];
|
|
5546
|
+
if (candidates.some((candidate) => source.includes(candidate))) {
|
|
5547
|
+
inferred.add(canonical);
|
|
5548
|
+
}
|
|
5549
|
+
}
|
|
5550
|
+
if (source.includes("sketch") || source.includes("\u8BBE\u8BA1\u7A3F") || source.includes("\u8FD8\u539F")) {
|
|
5551
|
+
inferred.add("sketch");
|
|
5552
|
+
}
|
|
5553
|
+
return [...inferred];
|
|
5554
|
+
}
|
|
5555
|
+
function extractErrorKeywords(errorMessage) {
|
|
5556
|
+
if (!(errorMessage == null ? void 0 : errorMessage.trim())) {
|
|
5557
|
+
return [];
|
|
5558
|
+
}
|
|
5559
|
+
const rawMatches = errorMessage.toLowerCase().match(/colorfilter|fill-opacity|viewbox|svgpicture(?:\.asset)?|image\.asset|bitmap|chevron|arrow|opacity|颜色|透明度|箭头|图标|位图|设计稿|还原|间距|布局|发灰|发浅|模糊|复用|旧资源|png|svg|icon/g) || [];
|
|
5560
|
+
return expandKeywords([...new Set(rawMatches)]);
|
|
5561
|
+
}
|
|
5498
5562
|
function extractCaseMetadata(filePath, framework) {
|
|
5499
5563
|
try {
|
|
5500
5564
|
const content = fs18.readFileSync(filePath, "utf-8");
|
|
@@ -5531,6 +5595,7 @@ function extractCaseMetadata(filePath, framework) {
|
|
|
5531
5595
|
timeSaved = timeSavedMatch[1] || `${timeSavedMatch[2]}\u8F6E\u5BF9\u8BDD`;
|
|
5532
5596
|
}
|
|
5533
5597
|
const id = path17.basename(filePath, ".md");
|
|
5598
|
+
tags = [.../* @__PURE__ */ new Set([...tags, ...inferImplicitTags(id, title, problemType || "", content)])];
|
|
5534
5599
|
return {
|
|
5535
5600
|
id,
|
|
5536
5601
|
framework,
|
|
@@ -5548,10 +5613,14 @@ function extractCaseMetadata(filePath, framework) {
|
|
|
5548
5613
|
}
|
|
5549
5614
|
function calculateMatchScore(caseData, keywords, errorMessage) {
|
|
5550
5615
|
let score = 0;
|
|
5616
|
+
const expandedKeywords = expandKeywords(keywords);
|
|
5617
|
+
const idLower = caseData.id.toLowerCase();
|
|
5551
5618
|
const titleLower = caseData.title.toLowerCase();
|
|
5552
5619
|
const problemTypeLower = caseData.problemType.toLowerCase();
|
|
5553
5620
|
const tagsLower = caseData.tags.map((t) => t.toLowerCase());
|
|
5554
|
-
|
|
5621
|
+
const caseContent = fs18.readFileSync(caseData.filePath, "utf-8").toLowerCase();
|
|
5622
|
+
const searchableText = `${idLower} ${titleLower} ${problemTypeLower} ${tagsLower.join(" ")} ${caseContent}`;
|
|
5623
|
+
for (const keyword of expandedKeywords) {
|
|
5555
5624
|
const kw = keyword.toLowerCase();
|
|
5556
5625
|
if (tagsLower.includes(kw)) {
|
|
5557
5626
|
score += 20;
|
|
@@ -5559,26 +5628,29 @@ function calculateMatchScore(caseData, keywords, errorMessage) {
|
|
|
5559
5628
|
score += 10;
|
|
5560
5629
|
}
|
|
5561
5630
|
}
|
|
5562
|
-
for (const keyword of
|
|
5631
|
+
for (const keyword of expandedKeywords) {
|
|
5563
5632
|
const kw = keyword.toLowerCase();
|
|
5564
|
-
if (titleLower.includes(kw)) {
|
|
5633
|
+
if (titleLower.includes(kw) || idLower.includes(kw)) {
|
|
5565
5634
|
score += 15;
|
|
5566
5635
|
}
|
|
5567
5636
|
}
|
|
5568
|
-
for (const keyword of
|
|
5637
|
+
for (const keyword of expandedKeywords) {
|
|
5569
5638
|
const kw = keyword.toLowerCase();
|
|
5570
5639
|
if (problemTypeLower.includes(kw)) {
|
|
5571
5640
|
score += 8;
|
|
5572
5641
|
}
|
|
5573
5642
|
}
|
|
5643
|
+
for (const keyword of expandedKeywords) {
|
|
5644
|
+
const kw = keyword.toLowerCase();
|
|
5645
|
+
if (searchableText.includes(kw)) {
|
|
5646
|
+
score += 6;
|
|
5647
|
+
}
|
|
5648
|
+
}
|
|
5574
5649
|
if (errorMessage && errorMessage.trim()) {
|
|
5575
|
-
const
|
|
5576
|
-
const caseContent = fs18.readFileSync(caseData.filePath, "utf-8").toLowerCase();
|
|
5577
|
-
const errorKeywords = errorLower.match(/\b[\u4e00-\u9fa5a-z]{4,}\b/g) || [];
|
|
5578
|
-
const uniqueKeywords = [...new Set(errorKeywords)];
|
|
5650
|
+
const uniqueKeywords = extractErrorKeywords(errorMessage);
|
|
5579
5651
|
let matches = 0;
|
|
5580
5652
|
for (const kw of uniqueKeywords.slice(0, 10)) {
|
|
5581
|
-
if (
|
|
5653
|
+
if (searchableText.includes(kw)) {
|
|
5582
5654
|
matches++;
|
|
5583
5655
|
}
|
|
5584
5656
|
}
|
|
@@ -5586,8 +5658,8 @@ function calculateMatchScore(caseData, keywords, errorMessage) {
|
|
|
5586
5658
|
score += Math.floor(matches / uniqueKeywords.length * 30);
|
|
5587
5659
|
}
|
|
5588
5660
|
}
|
|
5589
|
-
for (const keyword of
|
|
5590
|
-
if (caseData.tags.includes(keyword)) {
|
|
5661
|
+
for (const keyword of expandedKeywords) {
|
|
5662
|
+
if (caseData.tags.map((tag) => tag.toLowerCase()).includes(keyword.toLowerCase())) {
|
|
5591
5663
|
score += 15;
|
|
5592
5664
|
}
|
|
5593
5665
|
}
|
|
@@ -6219,9 +6291,12 @@ var OUTPUT_GUIDE = {
|
|
|
6219
6291
|
"- **isIcon=true**: \u5DF2\u901A\u8FC7 5 \u7279\u5F81\u8BC4\u5206\u8BC6\u522B\u4E3A\u56FE\u6807\uFF0C\u5E94\u5BFC\u51FA SVG \u800C\u975E\u7528 Icons.xxx",
|
|
6220
6292
|
"- **iconSvgMarkup**: \u63D2\u4EF6\u73B0\u5DF2\u76F4\u63A5\u8FD4\u56DE\u4ECE Sketch \u5BFC\u51FA\u7684\u771F\u5B9E SVG \u5B57\u7B26\u4E32\uFF0C\u4E0D\u518D\u53EA\u731C\u6D4B\u6587\u4EF6\u540D",
|
|
6221
6293
|
"- **iconSvgMeta.colorStrategy**: preserve=\u4FDD\u6301\u539F\u8272\uFF08\u591A\u8272/\u6E10\u53D8/\u5185\u5D4C\u4F4D\u56FE\uFF09\uFF0Ctint=\u5355\u8272\u56FE\u6807\u53EF\u7528 colorFilter",
|
|
6294
|
+
"- **\u5173\u952E\u89C4\u5219**: \u5F53 colorStrategy=preserve\uFF0C\u6216 SVG \u672C\u8EAB\u5E26 fill/fill-opacity/\u6E10\u53D8\u65F6\uFF0C**\u7981\u6B62**\u518D\u5957 ColorFilter\uFF0C\u5426\u5219\u4F1A\u51FA\u73B0\u989C\u8272\u53D1\u7070\u3001\u900F\u660E\u5EA6\u88AB\u4E8C\u6B21\u5904\u7406\u7684\u95EE\u9898",
|
|
6222
6295
|
"- **flutterSvgCode**: \u53EF\u76F4\u63A5\u7C98\u8D34\u7684 SvgPicture.string(...) \u4EE3\u7801\uFF1B\u53EA\u6709\u5355\u8272\u56FE\u6807\u624D\u4F1A\u81EA\u52A8\u9644\u5E26 ColorFilter",
|
|
6223
6296
|
"- **iconContentBounds**: \u56FE\u6807\u5185\u5BB9\u5B9E\u9645\u8FB9\u754C\uFF08vs \u5BB9\u5668\u5C3A\u5BF8\uFF09\uFF0C\u7528\u4E8E SizedBox \u7EA6\u675F",
|
|
6224
6297
|
"- **iconExportPath/iconSvgFileName**: \u82E5\u9700\u8981\u843D\u76D8\u8D44\u6E90\uFF0C\u4F7F\u7528\u63D2\u4EF6\u5EFA\u8BAE\u6587\u4EF6\u540D\u4FDD\u5B58\u5230 assets/icons/",
|
|
6298
|
+
"- **\u9875\u9762\u5C40\u90E8\u4FEE\u590D\u89C4\u5219**: \u82E5\u95EE\u9898\u53EA\u51FA\u5728\u67D0\u4E2A chevron/flag/badge/icon\uFF0C\u4F18\u5148\u91CD\u65B0\u9009\u4E2D\u8BE5\u56FE\u5C42\u6267\u884C cmd=svg \u6216\u5C40\u90E8\u5BFC\u51FA\uFF1B\u4E0D\u8981\u76F4\u63A5\u590D\u7528\u5176\u5B83\u9875\u9762\u7684\u540C\u540D\u5168\u5C40\u8D44\u4EA7",
|
|
6299
|
+
"- **\u8D44\u6E90\u590D\u7528\u6821\u9A8C**: \u65E7\u8D44\u4EA7\u5728\u590D\u7528\u524D\uFF0C\u5FC5\u987B\u6838\u5BF9 viewBox\u3001containerSize/contentSize\u3001fill-opacity \u548C\u5F53\u524D\u9875\u9762\u56FE\u5C42\u6765\u6E90\u662F\u5426\u4E00\u81F4",
|
|
6225
6300
|
"- \u8986\u76D6\u4FEE\u6B63: \u56DB\u8FB9\u8DDD\u22642pt \u6216\u5BBD\u9AD8\u2265\u7236\u5BB9\u566888% \u7684 ShapePath \u662F\u80CC\u666F\u5C42\uFF0C\u5DF2\u81EA\u52A8\u6392\u9664",
|
|
6226
6301
|
"",
|
|
6227
6302
|
"### ShapePath \u8BED\u4E49\uFF08v3.0.6 \u66F4\u65B0\uFF09",
|
|
@@ -6337,7 +6412,14 @@ var OUTPUT_GUIDE = {
|
|
|
6337
6412
|
"- **ch**: \u5B50\u5143\u7D20\u6570\u7EC4\uFF08\u9012\u5F52\uFF09",
|
|
6338
6413
|
"",
|
|
6339
6414
|
"### \u4F7F\u7528\u65B9\u5F0F",
|
|
6340
|
-
"\u5148\u8BFB\u53D6 restorationContract.blockOrder/bodyCopyLedger/hardRules \u9501\u5B9A\u7ED3\u6784\uFF0C\u518D\u7528 tree \u4E2D\u7684 flutter* \u5B57\u6BB5\u586B\u5145\u6837\u5F0F\u548C\u5E03\u5C40\u7EC6\u8282\u3002"
|
|
6415
|
+
"\u5148\u8BFB\u53D6 restorationContract.blockOrder/bodyCopyLedger/hardRules \u9501\u5B9A\u7ED3\u6784\uFF0C\u518D\u7528 tree \u4E2D\u7684 flutter* \u5B57\u6BB5\u586B\u5145\u6837\u5F0F\u548C\u5E03\u5C40\u7EC6\u8282\u3002",
|
|
6416
|
+
"",
|
|
6417
|
+
"### \u8FD8\u539F\u540E\u5F3A\u5236\u590D\u67E5\u6E05\u5355",
|
|
6418
|
+
"- \u56FE\u6807\u662F\u5426\u8BEF\u7528\u65E7\u9875\u9762\u8D44\u4EA7\uFF1A\u540C\u540D\u7BAD\u5934/\u56FE\u6807\u9ED8\u8BA4\u4E0D\u53EF\u4FE1\uFF0C\u5148\u6838\u5BF9\u6765\u6E90\u56FE\u5C42",
|
|
6419
|
+
"- SVG \u662F\u5426\u8BEF\u52A0 ColorFilter\uFF1A\u4FDD\u7559\u539F\u8272\u7684 SVG \u4E0D\u5F97\u4E8C\u6B21\u67D3\u8272",
|
|
6420
|
+
"- \u56FE\u6807\u662F\u5426\u7528\u9519\u5C3A\u5BF8\uFF1A16x16 \u69FD\u4F4D\u4E0D\u7B49\u4E8E 16x16 \u56FE\u5F62\uFF0C\u6309 xs/cs \u5206\u79BB\u5360\u4F4D\u4E0E\u6E32\u67D3",
|
|
6421
|
+
"- Bitmap \u662F\u5426\u8BEF\u5F53 SVG\uFF1ASketch Image \u56FE\u5C42\u5FC5\u987B\u5BFC\u51FA PNG + Image.asset",
|
|
6422
|
+
"- \u9875\u9762\u9AA8\u67B6\u662F\u5426\u88AB\u4E1A\u52A1\u7EC4\u4EF6\u6539\u5199\uFF1A\u6807\u9898\u3001\u9876\u90E8\u5165\u53E3\u3001\u641C\u7D22\u5757\u548C\u5E95\u5BFC\u987A\u5E8F\u5FC5\u987B\u56DE\u770B restorationContract"
|
|
6341
6423
|
].join("\n")
|
|
6342
6424
|
};
|
|
6343
6425
|
function findFirst(paths) {
|
|
@@ -6427,7 +6509,27 @@ async function sketchMeasure(args) {
|
|
|
6427
6509
|
scriptPath: tempScriptPath,
|
|
6428
6510
|
scriptLength: fullScript.length,
|
|
6429
6511
|
pluginInstall: installStatus,
|
|
6430
|
-
instruction: "\u5C06 loaderScript \u4F20\u5165 mcp_sketch_run_code({ code: loaderScript }) \u6267\u884C\u3002\u8BBE\u8BA1\u7A3F\u8FD8\u539F\u9996\u8F6E\u4F18\u5148\u4F7F\u7528 cmd=compact\uFF1B\u53EA\u6709\u5F53 compact contract \u4E0D\u8DB3\u4EE5\u89E3\u91CA\u5C40\u90E8\u5E03\u5C40\u6216\u6837\u5F0F\u65F6\uFF0C\u518D\u8865\u7528 measure/style\u3002\u8BF7\u5148\u5728 Sketch \u4E2D\u9009\u4E2D\u76EE\u6807\u753B\u677F/\u56FE\u5C42\u3002",
|
|
6512
|
+
instruction: "\u5C06 loaderScript \u4F20\u5165 mcp_sketch_run_code({ code: loaderScript }) \u6267\u884C\u3002\u8BBE\u8BA1\u7A3F\u8FD8\u539F\u9996\u8F6E\u4F18\u5148\u4F7F\u7528 cmd=compact\uFF1B\u53EA\u6709\u5F53 compact contract \u4E0D\u8DB3\u4EE5\u89E3\u91CA\u5C40\u90E8\u5E03\u5C40\u6216\u6837\u5F0F\u65F6\uFF0C\u518D\u8865\u7528 measure/style\u3002\u82E5\u504F\u5DEE\u96C6\u4E2D\u5728\u5355\u4E2A icon/svg/bitmap\uFF0C\u8BF7\u91CD\u65B0\u9009\u4E2D\u8BE5\u56FE\u5C42\u6267\u884C svg \u6216\u5C40\u90E8\u5BFC\u51FA\uFF0C\u5E76\u590D\u67E5 colorStrategy\u3001viewBox\u3001containerSize/contentSize\uFF0C\u907F\u514D\u590D\u7528\u65E7\u9875\u9762\u8D44\u4EA7\u3002\u8BF7\u5148\u5728 Sketch \u4E2D\u9009\u4E2D\u76EE\u6807\u753B\u677F/\u56FE\u5C42\u3002",
|
|
6513
|
+
restoreChecklist: [
|
|
6514
|
+
"\u5148\u6309 restorationContract \u9501\u5B9A\u9875\u9762\u9AA8\u67B6\uFF0C\u518D\u5904\u7406\u5C40\u90E8\u6837\u5F0F",
|
|
6515
|
+
"\u5355\u4E2A\u56FE\u6807\u504F\u5DEE\u4F18\u5148\u5C40\u90E8\u91CD\u5BFC\u51FA\uFF0C\u4E0D\u590D\u7528\u5176\u5B83\u9875\u9762\u65E7\u8D44\u4EA7",
|
|
6516
|
+
"preserve \u6A21\u5F0F SVG \u7981\u6B62\u8FFD\u52A0 ColorFilter",
|
|
6517
|
+
"16x16 \u69FD\u4F4D\u4E0E 6x8/12x12 \u56FE\u5F62\u8981\u5206\u5F00\u5904\u7406",
|
|
6518
|
+
"Sketch Image \u56FE\u5C42\u5BFC\u51FA PNG\uFF0C\u4E0D\u8981\u4F2A\u88C5\u6210 SVG \u4F7F\u7528",
|
|
6519
|
+
"\u4EE3\u7801\u751F\u6210\u540E\u5FC5\u987B\u8C03\u7528 validate_restoration \u6821\u9A8C\u9875\u9762\u9AA8\u67B6\u3001\u8D44\u4EA7\u6765\u6E90\u548C\u771F\u5B9E\u8DEF\u7531"
|
|
6520
|
+
],
|
|
6521
|
+
postRestoreValidator: {
|
|
6522
|
+
skill: "validate_restoration",
|
|
6523
|
+
required: true,
|
|
6524
|
+
checks: ["contract-validator", "asset-source-validator", "route-review-validator"],
|
|
6525
|
+
exampleArgs: {
|
|
6526
|
+
measurePath: "/path/to/sketch-compact-output.json",
|
|
6527
|
+
codeFilePath: "/path/to/generated_page.dart",
|
|
6528
|
+
routeFilePath: "/path/to/app_pages.dart",
|
|
6529
|
+
routeName: "AppRoutes.xxx",
|
|
6530
|
+
projectPath: "/path/to/project"
|
|
6531
|
+
}
|
|
6532
|
+
},
|
|
6431
6533
|
outputGuide: OUTPUT_GUIDE[cmd] || OUTPUT_GUIDE["measure"],
|
|
6432
6534
|
loaderScript
|
|
6433
6535
|
}, null, 2)
|
|
@@ -6435,6 +6537,330 @@ async function sketchMeasure(args) {
|
|
|
6435
6537
|
};
|
|
6436
6538
|
}
|
|
6437
6539
|
|
|
6540
|
+
// src/tools/validateRestoration.ts
|
|
6541
|
+
import * as fs20 from "fs";
|
|
6542
|
+
import * as path20 from "path";
|
|
6543
|
+
function isRecord2(value) {
|
|
6544
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6545
|
+
}
|
|
6546
|
+
function parseNestedJson(raw) {
|
|
6547
|
+
let candidate = raw.trim();
|
|
6548
|
+
for (let attempt = 0; attempt < 4; attempt += 1) {
|
|
6549
|
+
try {
|
|
6550
|
+
const parsed = JSON.parse(candidate);
|
|
6551
|
+
if (typeof parsed === "string") {
|
|
6552
|
+
candidate = parsed.trim();
|
|
6553
|
+
continue;
|
|
6554
|
+
}
|
|
6555
|
+
if (isRecord2(parsed) && Array.isArray(parsed.content) && parsed.content[0] && isRecord2(parsed.content[0])) {
|
|
6556
|
+
const text = parsed.content[0].text;
|
|
6557
|
+
if (typeof text === "string") {
|
|
6558
|
+
candidate = text.trim();
|
|
6559
|
+
continue;
|
|
6560
|
+
}
|
|
6561
|
+
}
|
|
6562
|
+
return parsed;
|
|
6563
|
+
} catch {
|
|
6564
|
+
if (candidate.startsWith("'") && candidate.endsWith("'") || candidate.startsWith('"') && candidate.endsWith('"')) {
|
|
6565
|
+
candidate = candidate.slice(1, -1);
|
|
6566
|
+
}
|
|
6567
|
+
candidate = candidate.replace(/\\n/g, "\n").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
|
|
6568
|
+
}
|
|
6569
|
+
}
|
|
6570
|
+
return JSON.parse(candidate);
|
|
6571
|
+
}
|
|
6572
|
+
function loadMeasurePayload(args) {
|
|
6573
|
+
const raw = args.measureContent || (args.measurePath ? fs20.readFileSync(args.measurePath, "utf-8") : "");
|
|
6574
|
+
if (!raw) {
|
|
6575
|
+
throw new Error("validate_restoration \u9700\u8981 measureContent \u6216 measurePath");
|
|
6576
|
+
}
|
|
6577
|
+
const parsed = parseNestedJson(raw);
|
|
6578
|
+
if (!isRecord2(parsed)) {
|
|
6579
|
+
throw new Error("measure payload \u89E3\u6790\u5931\u8D25\uFF0C\u7ED3\u679C\u4E0D\u662F\u5BF9\u8C61");
|
|
6580
|
+
}
|
|
6581
|
+
return parsed;
|
|
6582
|
+
}
|
|
6583
|
+
function hasAnyPattern(source, patterns) {
|
|
6584
|
+
return patterns.some((pattern) => pattern.test(source));
|
|
6585
|
+
}
|
|
6586
|
+
function collectAssetContracts(node, bucket = []) {
|
|
6587
|
+
if (!node) {
|
|
6588
|
+
return bucket;
|
|
6589
|
+
}
|
|
6590
|
+
if (typeof node.svgFile === "string") {
|
|
6591
|
+
bucket.push({
|
|
6592
|
+
sketchName: node.iconSketchName || node.n || node.svgFile,
|
|
6593
|
+
fileName: path20.posix.basename(node.svgFile),
|
|
6594
|
+
mode: node.svgMode,
|
|
6595
|
+
sourceType: "vector",
|
|
6596
|
+
containerSize: node.cs,
|
|
6597
|
+
contentSize: node.xs
|
|
6598
|
+
});
|
|
6599
|
+
}
|
|
6600
|
+
if (node.t === "Image") {
|
|
6601
|
+
bucket.push({
|
|
6602
|
+
sketchName: node.n || "Image",
|
|
6603
|
+
sourceType: "bitmap"
|
|
6604
|
+
});
|
|
6605
|
+
}
|
|
6606
|
+
for (const child of node.ch || []) {
|
|
6607
|
+
collectAssetContracts(child, bucket);
|
|
6608
|
+
}
|
|
6609
|
+
return bucket;
|
|
6610
|
+
}
|
|
6611
|
+
function extractCodeAssets(codeContent) {
|
|
6612
|
+
const matches = codeContent.match(/assets\/[A-Za-z0-9_./-]+\.(svg|png|jpg|jpeg|webp)/g);
|
|
6613
|
+
return matches ? [...new Set(matches)] : [];
|
|
6614
|
+
}
|
|
6615
|
+
function extractPageClassName(codeContent) {
|
|
6616
|
+
const match = codeContent.match(/class\s+([A-Za-z0-9_]+)\s+extends\s+(StatelessWidget|StatefulWidget)/);
|
|
6617
|
+
return (match == null ? void 0 : match[1]) || null;
|
|
6618
|
+
}
|
|
6619
|
+
function buildContractValidator(payload, codeContent) {
|
|
6620
|
+
var _a, _b, _c;
|
|
6621
|
+
const findings = [];
|
|
6622
|
+
const blockOrder = ((_a = payload.restorationContract) == null ? void 0 : _a.blockOrder) || [];
|
|
6623
|
+
const navCopyLedger = ((_b = payload.restorationContract) == null ? void 0 : _b.navCopyLedger) || [];
|
|
6624
|
+
const pageTitle = (_c = payload.restorationContract) == null ? void 0 : _c.pageTitle;
|
|
6625
|
+
let requiredBlocks = 0;
|
|
6626
|
+
let matchedBlocks = 0;
|
|
6627
|
+
if (pageTitle) {
|
|
6628
|
+
requiredBlocks += 1;
|
|
6629
|
+
if (codeContent.includes(pageTitle)) {
|
|
6630
|
+
matchedBlocks += 1;
|
|
6631
|
+
} else {
|
|
6632
|
+
findings.push({
|
|
6633
|
+
id: "missing_page_title",
|
|
6634
|
+
severity: "error",
|
|
6635
|
+
message: `\u4EE3\u7801\u4E2D\u672A\u627E\u5230\u8BBE\u8BA1\u6807\u9898\u201C${pageTitle}\u201D`,
|
|
6636
|
+
suggestion: "\u5148\u6309 restorationContract.pageTitle \u5BF9\u9F50\u9875\u9762\u6807\u9898\uFF0C\u518D\u7EE7\u7EED\u4FEE\u5C40\u90E8\u6837\u5F0F\u3002"
|
|
6637
|
+
});
|
|
6638
|
+
}
|
|
6639
|
+
}
|
|
6640
|
+
const needsBackButton = blockOrder.some((block) => /back/i.test(`${block.name || ""} ${block.label || ""}`));
|
|
6641
|
+
if (needsBackButton) {
|
|
6642
|
+
requiredBlocks += 1;
|
|
6643
|
+
if (hasAnyPattern(codeContent, [/Get\.back\b/, /Navigator\.pop\b/, /arrow_back/i, /back_icon\.svg/, /back_button/i])) {
|
|
6644
|
+
matchedBlocks += 1;
|
|
6645
|
+
} else {
|
|
6646
|
+
findings.push({
|
|
6647
|
+
id: "missing_back_button_block",
|
|
6648
|
+
severity: "error",
|
|
6649
|
+
message: "\u8BBE\u8BA1\u7A3F\u5B58\u5728 Back Button\uFF0C\u4F46\u4EE3\u7801\u4E2D\u672A\u68C0\u6D4B\u5230\u8FD4\u56DE\u6309\u94AE\u5B9E\u73B0\u3002",
|
|
6650
|
+
suggestion: "\u8865\u9F50\u8FD4\u56DE\u6309\u94AE\u4EA4\u4E92\u548C\u89C6\u89C9\u5B9E\u73B0\uFF0C\u4E0D\u8981\u53EA\u4FDD\u7559\u6807\u9898\u3002"
|
|
6651
|
+
});
|
|
6652
|
+
}
|
|
6653
|
+
}
|
|
6654
|
+
const needsSupportButton = blockOrder.some((block) => /customer service|support|客服/i.test(`${block.name || ""} ${block.label || ""}`));
|
|
6655
|
+
if (needsSupportButton) {
|
|
6656
|
+
requiredBlocks += 1;
|
|
6657
|
+
if (hasAnyPattern(codeContent, [/customer[_ ]service/i, /support/i, /recipient_customer_service_button/i, /客服/])) {
|
|
6658
|
+
matchedBlocks += 1;
|
|
6659
|
+
} else {
|
|
6660
|
+
findings.push({
|
|
6661
|
+
id: "missing_customer_service_block",
|
|
6662
|
+
severity: "error",
|
|
6663
|
+
message: "\u8BBE\u8BA1\u7A3F\u5B58\u5728 Customer Service Button\uFF0C\u4F46\u4EE3\u7801\u4E2D\u672A\u68C0\u6D4B\u5230\u5BF9\u5E94\u5165\u53E3\u3002",
|
|
6664
|
+
suggestion: "\u8865\u9F50\u5BA2\u670D\u6309\u94AE\uFF0C\u4E0D\u8981\u5728\u751F\u6210\u9636\u6BB5\u76F4\u63A5\u7701\u7565\u53F3\u4E0A\u89D2\u5165\u53E3\u3002"
|
|
6665
|
+
});
|
|
6666
|
+
}
|
|
6667
|
+
}
|
|
6668
|
+
const optionLabels = ["\u652F\u4ED8\u5B9D", "\u5FAE\u4FE1", "\u94F6\u884C\u5361"].filter((label) => navCopyLedger.includes(label));
|
|
6669
|
+
if (optionLabels.length > 0) {
|
|
6670
|
+
requiredBlocks += 1;
|
|
6671
|
+
const missingOptions = optionLabels.filter((label) => !codeContent.includes(label));
|
|
6672
|
+
if (missingOptions.length === 0) {
|
|
6673
|
+
matchedBlocks += 1;
|
|
6674
|
+
} else {
|
|
6675
|
+
findings.push({
|
|
6676
|
+
id: "missing_option_labels",
|
|
6677
|
+
severity: "error",
|
|
6678
|
+
message: `\u4EE3\u7801\u4E2D\u7F3A\u5C11\u8BBE\u8BA1\u7A3F\u9009\u9879\u6587\u6848: ${missingOptions.join(" / ")}`,
|
|
6679
|
+
suggestion: "\u9009\u9879\u5361\u6587\u6848\u9ED8\u8BA4\u4EE5 navCopyLedger \u4E3A\u771F\u503C\uFF0C\u7981\u6B62\u81EA\u884C\u6539\u5199\u6216\u9057\u6F0F\u3002"
|
|
6680
|
+
});
|
|
6681
|
+
}
|
|
6682
|
+
}
|
|
6683
|
+
const needsStatusBar = navCopyLedger.includes("9:41") || hasAnyPattern(JSON.stringify(payload.tree || {}), [/"Signal"/, /"WiFi"/, /"Battery"/]);
|
|
6684
|
+
if (needsStatusBar) {
|
|
6685
|
+
requiredBlocks += 1;
|
|
6686
|
+
if (hasAnyPattern(codeContent, [/9:41/, /Signal/, /WiFi/, /Battery/, /statusBar/i])) {
|
|
6687
|
+
matchedBlocks += 1;
|
|
6688
|
+
} else {
|
|
6689
|
+
findings.push({
|
|
6690
|
+
id: "missing_status_bar_block",
|
|
6691
|
+
severity: "warning",
|
|
6692
|
+
message: "\u8BBE\u8BA1\u7A3F\u9876\u90E8\u72B6\u6001\u680F\u5757\u672A\u843D\u5730\uFF0C\u5F53\u524D\u9875\u9762\u9AA8\u67B6\u4ECD\u7136\u4E0D\u5B8C\u6574\u3002",
|
|
6693
|
+
suggestion: "\u5982\u679C\u76EE\u6807\u662F 1:1 \u8FD8\u539F\uFF0C\u9700\u628A 9:41 / Signal / WiFi / Battery \u8FD9\u7EC4\u9876\u90E8\u5757\u8865\u56DE\u3002"
|
|
6694
|
+
});
|
|
6695
|
+
}
|
|
6696
|
+
}
|
|
6697
|
+
return {
|
|
6698
|
+
passed: !findings.some((item) => item.severity === "error"),
|
|
6699
|
+
requiredBlocks,
|
|
6700
|
+
matchedBlocks,
|
|
6701
|
+
findings
|
|
6702
|
+
};
|
|
6703
|
+
}
|
|
6704
|
+
function buildAssetValidator(payload, codeContent) {
|
|
6705
|
+
const findings = [];
|
|
6706
|
+
const assetContracts = collectAssetContracts(payload.tree);
|
|
6707
|
+
const expectedVectorFiles = new Set(
|
|
6708
|
+
assetContracts.filter((item) => item.sourceType === "vector" && item.fileName).map((item) => item.fileName.toLowerCase())
|
|
6709
|
+
);
|
|
6710
|
+
const expectedBitmapCount = assetContracts.filter((item) => item.sourceType === "bitmap").length;
|
|
6711
|
+
const codeAssets = extractCodeAssets(codeContent);
|
|
6712
|
+
const codeVectorAssets = codeAssets.filter((asset) => asset.endsWith(".svg"));
|
|
6713
|
+
const codeBitmapAssets = codeAssets.filter((asset) => /\.(png|jpg|jpeg|webp)$/.test(asset));
|
|
6714
|
+
const unexpectedVectorAssets = codeVectorAssets.filter((asset) => !expectedVectorFiles.has(path20.posix.basename(asset).toLowerCase()));
|
|
6715
|
+
if (unexpectedVectorAssets.length > 0) {
|
|
6716
|
+
findings.push({
|
|
6717
|
+
id: "unexpected_vector_assets",
|
|
6718
|
+
severity: "error",
|
|
6719
|
+
message: "\u4EE3\u7801\u4E2D\u5B58\u5728\u672A\u5728\u5F53\u524D\u8BBE\u8BA1\u7A3F contract \u4E2D\u51FA\u73B0\u7684 SVG \u8D44\u6E90\uFF0C\u7591\u4F3C\u590D\u7528\u4E86\u65E7\u9875\u9762\u8D44\u4EA7\u3002",
|
|
6720
|
+
evidence: unexpectedVectorAssets,
|
|
6721
|
+
suggestion: "\u5BF9\u8FD9\u4E9B\u56FE\u6807\u91CD\u65B0\u4ECE\u5F53\u524D\u753B\u677F\u5C40\u90E8\u5BFC\u51FA\uFF0C\u6216\u5728 validate_restoration \u901A\u8FC7\u524D\u4E0D\u8981\u7EE7\u7EED\u63D0\u4EA4\u9875\u9762\u3002"
|
|
6722
|
+
});
|
|
6723
|
+
}
|
|
6724
|
+
if (expectedBitmapCount > 0 && codeBitmapAssets.length === 0) {
|
|
6725
|
+
findings.push({
|
|
6726
|
+
id: "missing_bitmap_assets",
|
|
6727
|
+
severity: "warning",
|
|
6728
|
+
message: `\u5F53\u524D\u8BBE\u8BA1\u7A3F\u5305\u542B ${expectedBitmapCount} \u4E2A Image \u56FE\u5C42\uFF0C\u4F46\u4EE3\u7801\u91CC\u6CA1\u6709\u4EFB\u4F55\u4F4D\u56FE\u8D44\u6E90\u5F15\u7528\u3002`,
|
|
6729
|
+
suggestion: "\u68C0\u67E5\u662F\u5426\u628A Sketch Image \u56FE\u5C42\u9519\u8BEF\u5F53\u6210 SVG \u6216\u4EE3\u7801\u7ED8\u5236\u4E86\u3002"
|
|
6730
|
+
});
|
|
6731
|
+
}
|
|
6732
|
+
if (codeBitmapAssets.length > 0) {
|
|
6733
|
+
findings.push({
|
|
6734
|
+
id: "bitmap_asset_source_check_required",
|
|
6735
|
+
severity: "info",
|
|
6736
|
+
message: "\u4EE3\u7801\u4E2D\u5B58\u5728\u4F4D\u56FE\u8D44\u6E90\uFF0C\u9700\u4EBA\u5DE5\u786E\u8BA4\u5B83\u4EEC\u6765\u81EA\u5F53\u524D\u753B\u677F\u800C\u4E0D\u662F\u65E7\u9875\u9762\u7F13\u5B58\u8D44\u6E90\u3002",
|
|
6737
|
+
evidence: codeBitmapAssets,
|
|
6738
|
+
suggestion: "\u4F4D\u56FE\u8D44\u6E90\u4F18\u5148\u6309\u5F53\u524D\u753B\u677F\u76F4\u63A5\u5BFC\u51FA\u5230 assets/icons/\uFF0C\u907F\u514D\u7EE7\u7EED\u590D\u7528\u65E7\u9875\u9762 PNG\u3002"
|
|
6739
|
+
});
|
|
6740
|
+
}
|
|
6741
|
+
const preserveContracts = assetContracts.filter((item) => item.mode === "preserve");
|
|
6742
|
+
if (preserveContracts.length > 0 && /colorFilter\s*:|ColorFilter\./.test(codeContent)) {
|
|
6743
|
+
findings.push({
|
|
6744
|
+
id: "preserve_svg_tint_risk",
|
|
6745
|
+
severity: "warning",
|
|
6746
|
+
message: "\u5F53\u524D\u9875\u9762\u4EE3\u7801\u5305\u542B ColorFilter\uFF0C\u800C contract \u4E2D\u5B58\u5728 preserve \u6A21\u5F0F SVG\uFF0C\u5B58\u5728\u518D\u6B21\u67D3\u8272\u98CE\u9669\u3002",
|
|
6747
|
+
evidence: preserveContracts.map((item) => item.fileName || item.sketchName),
|
|
6748
|
+
suggestion: "\u68C0\u67E5 preserve SVG \u662F\u5426\u88AB\u6574\u4F53 tint\uFF0C\u5FC5\u8981\u65F6\u628A\u56FE\u6807\u6539\u4E3A\u4FDD\u7559\u539F\u8272\u8D44\u6E90\u3002"
|
|
6749
|
+
});
|
|
6750
|
+
}
|
|
6751
|
+
return {
|
|
6752
|
+
passed: !findings.some((item) => item.severity === "error"),
|
|
6753
|
+
expectedAssetContracts: assetContracts,
|
|
6754
|
+
codeAssets,
|
|
6755
|
+
findings
|
|
6756
|
+
};
|
|
6757
|
+
}
|
|
6758
|
+
function buildRouteReviewValidator(args, codeContent) {
|
|
6759
|
+
const findings = [];
|
|
6760
|
+
const pageClassName = extractPageClassName(codeContent);
|
|
6761
|
+
let routeConnected = null;
|
|
6762
|
+
let routeTarget = null;
|
|
6763
|
+
let reviewHarnessReady = null;
|
|
6764
|
+
if (args.routeFilePath && args.routeName) {
|
|
6765
|
+
const routeContent = fs20.readFileSync(args.routeFilePath, "utf-8");
|
|
6766
|
+
const routeIndex = routeContent.indexOf(`name: ${args.routeName}`);
|
|
6767
|
+
if (routeIndex === -1) {
|
|
6768
|
+
findings.push({
|
|
6769
|
+
id: "route_not_found",
|
|
6770
|
+
severity: "error",
|
|
6771
|
+
message: `\u5728\u8DEF\u7531\u6587\u4EF6\u4E2D\u672A\u627E\u5230 ${args.routeName} \u7684\u5B9A\u4E49\u3002`,
|
|
6772
|
+
suggestion: "\u751F\u6210\u9875\u9762\u540E\u5FC5\u987B\u786E\u8BA4\u771F\u5B9E\u8DEF\u7531\u5DF2\u7ECF\u63A5\u901A\uFF0C\u4E0D\u80FD\u505C\u7559\u5728\u5B64\u7ACB\u9875\u9762\u6587\u4EF6\u3002"
|
|
6773
|
+
});
|
|
6774
|
+
} else {
|
|
6775
|
+
const routeChunk = routeContent.slice(routeIndex, routeIndex + 240);
|
|
6776
|
+
routeConnected = true;
|
|
6777
|
+
if (/_ComingSoonPage/.test(routeChunk)) {
|
|
6778
|
+
routeConnected = false;
|
|
6779
|
+
findings.push({
|
|
6780
|
+
id: "route_points_to_placeholder",
|
|
6781
|
+
severity: "error",
|
|
6782
|
+
message: `${args.routeName} \u5F53\u524D\u4ECD\u7136\u6307\u5411 _ComingSoonPage\uFF0C\u5360\u4F4D\u9875\u672A\u66FF\u6362\u3002`,
|
|
6783
|
+
suggestion: "\u628A page factory \u63A5\u5230\u771F\u5B9E\u9875\u9762\u7C7B\uFF0C\u800C\u4E0D\u662F\u4FDD\u7559\u5360\u4F4D\u5B9E\u73B0\u3002"
|
|
6784
|
+
});
|
|
6785
|
+
}
|
|
6786
|
+
const routeTargetMatch = routeChunk.match(/page:\s*\(\)\s*=>\s*const\s+([A-Za-z0-9_]+)/) || routeChunk.match(/page:\s*\(\)\s*=>\s*([A-Za-z0-9_]+)\(/);
|
|
6787
|
+
routeTarget = (routeTargetMatch == null ? void 0 : routeTargetMatch[1]) || null;
|
|
6788
|
+
if (pageClassName && routeTarget && pageClassName !== routeTarget) {
|
|
6789
|
+
findings.push({
|
|
6790
|
+
id: "route_target_mismatch",
|
|
6791
|
+
severity: "warning",
|
|
6792
|
+
message: `\u8DEF\u7531\u76EE\u6807\u7C7B ${routeTarget} \u4E0E\u9875\u9762\u4E3B\u7C7B ${pageClassName} \u4E0D\u4E00\u81F4\u3002`,
|
|
6793
|
+
suggestion: "\u786E\u8BA4\u8DEF\u7531\u63A5\u7684\u5C31\u662F\u5F53\u524D\u751F\u6210\u9875\u9762\uFF0C\u800C\u4E0D\u662F\u540C\u540D\u65E7\u9875\u9762\u3002"
|
|
6794
|
+
});
|
|
6795
|
+
}
|
|
6796
|
+
}
|
|
6797
|
+
}
|
|
6798
|
+
if (args.projectPath) {
|
|
6799
|
+
const reviewHarnessPath = path20.join(args.projectPath, "test/visual_regression/ui_review_golden_test.dart");
|
|
6800
|
+
reviewHarnessReady = fs20.existsSync(reviewHarnessPath);
|
|
6801
|
+
if (!reviewHarnessReady) {
|
|
6802
|
+
findings.push({
|
|
6803
|
+
id: "ui_review_harness_missing",
|
|
6804
|
+
severity: "warning",
|
|
6805
|
+
message: "\u9879\u76EE\u5185\u672A\u68C0\u6D4B\u5230 UI review golden harness\uFF0C\u751F\u6210\u540E\u65E0\u6CD5\u7ACB\u5373\u505A\u7ED3\u6784\u7EA7\u56DE\u5F52\u3002",
|
|
6806
|
+
suggestion: "\u81F3\u5C11\u8865\u4E0A test/visual_regression/ui_review_golden_test.dart\uFF0C\u518D\u8BA9\u8FD8\u539F\u9875\u8FDB\u5165\u81EA\u52A8 review\u3002"
|
|
6807
|
+
});
|
|
6808
|
+
} else {
|
|
6809
|
+
const reviewContent = fs20.readFileSync(reviewHarnessPath, "utf-8");
|
|
6810
|
+
const pageStem = path20.basename(args.codeFilePath || "", path20.extname(args.codeFilePath || "")).replace(/_page$/, "");
|
|
6811
|
+
if (pageStem && !reviewContent.includes(pageStem)) {
|
|
6812
|
+
findings.push({
|
|
6813
|
+
id: "ui_review_scenario_not_registered",
|
|
6814
|
+
severity: "warning",
|
|
6815
|
+
message: "UI review harness \u5DF2\u5B58\u5728\uFF0C\u4F46\u5F53\u524D\u9875\u9762\u8FD8\u6CA1\u6709\u4E13\u5C5E\u573A\u666F\u6CE8\u518C\u3002",
|
|
6816
|
+
evidence: [reviewHarnessPath],
|
|
6817
|
+
suggestion: "\u628A\u5F53\u524D\u9875\u9762\u52A0\u5165 visual regression \u573A\u666F\uFF0C\u907F\u514D\u53EA\u9760\u8089\u773C\u56DE\u5F52\u3002"
|
|
6818
|
+
});
|
|
6819
|
+
}
|
|
6820
|
+
}
|
|
6821
|
+
}
|
|
6822
|
+
const recommendedNextSteps = [
|
|
6823
|
+
"\u5148\u4FEE\u6B63\u6240\u6709 error \u7EA7\u522B\u95EE\u9898\uFF0C\u518D\u5904\u7406 warning",
|
|
6824
|
+
"\u4FEE\u6B63\u8D44\u6E90\u6765\u6E90\u540E\u91CD\u65B0\u8FD0\u884C validate_restoration\uFF0C\u76F4\u5230 unexpected vector assets \u6E05\u96F6"
|
|
6825
|
+
];
|
|
6826
|
+
if (args.routeFilePath && args.routeName) {
|
|
6827
|
+
recommendedNextSteps.push(`\u786E\u8BA4 ${args.routeName} \u4E0D\u518D\u6307\u5411 _ComingSoonPage`);
|
|
6828
|
+
}
|
|
6829
|
+
if (args.projectPath) {
|
|
6830
|
+
recommendedNextSteps.push("\u5C06\u9875\u9762\u6CE8\u518C\u5230 visual regression \u573A\u666F\u540E\u518D\u8FDB\u884C UI review");
|
|
6831
|
+
}
|
|
6832
|
+
return {
|
|
6833
|
+
passed: !findings.some((item) => item.severity === "error"),
|
|
6834
|
+
routeConnected,
|
|
6835
|
+
routeTarget,
|
|
6836
|
+
reviewHarnessReady,
|
|
6837
|
+
findings,
|
|
6838
|
+
recommendedNextSteps
|
|
6839
|
+
};
|
|
6840
|
+
}
|
|
6841
|
+
async function validateRestoration(args) {
|
|
6842
|
+
var _a;
|
|
6843
|
+
if (!args.codeFilePath) {
|
|
6844
|
+
throw new Error("validate_restoration \u9700\u8981 codeFilePath \u53C2\u6570");
|
|
6845
|
+
}
|
|
6846
|
+
if (!fs20.existsSync(args.codeFilePath)) {
|
|
6847
|
+
throw new Error(`\u4EE3\u7801\u6587\u4EF6\u4E0D\u5B58\u5728: ${args.codeFilePath}`);
|
|
6848
|
+
}
|
|
6849
|
+
const payload = loadMeasurePayload(args);
|
|
6850
|
+
const codeContent = fs20.readFileSync(args.codeFilePath, "utf-8");
|
|
6851
|
+
const contractValidator = buildContractValidator(payload, codeContent);
|
|
6852
|
+
const assetValidator = buildAssetValidator(payload, codeContent);
|
|
6853
|
+
const routeReviewValidator = buildRouteReviewValidator(args, codeContent);
|
|
6854
|
+
return {
|
|
6855
|
+
artboardName: (_a = payload.artboard) == null ? void 0 : _a.name,
|
|
6856
|
+
ok: contractValidator.passed && assetValidator.passed && routeReviewValidator.passed,
|
|
6857
|
+
codeFilePath: args.codeFilePath,
|
|
6858
|
+
contractValidator,
|
|
6859
|
+
assetValidator,
|
|
6860
|
+
routeReviewValidator
|
|
6861
|
+
};
|
|
6862
|
+
}
|
|
6863
|
+
|
|
6438
6864
|
// src/tools/skillDefinitions.ts
|
|
6439
6865
|
function wrapAsContent(data) {
|
|
6440
6866
|
return {
|
|
@@ -6444,6 +6870,20 @@ function wrapAsContent(data) {
|
|
|
6444
6870
|
}]
|
|
6445
6871
|
};
|
|
6446
6872
|
}
|
|
6873
|
+
function extractTroubleshootingKeywords(problemText) {
|
|
6874
|
+
const extractedKeywords = [];
|
|
6875
|
+
const keywordPatterns = [
|
|
6876
|
+
/\b(shadow|阴影|透出|transparency|clip|裁剪|layout|布局|spacing|间距|offset|偏移|animation|动画|color|颜色|colorfilter|fill-opacity|opacity|alpha|border|边框|svg|svgpicture|bitmap|png|icon|图标|chevron|arrow|箭头|input|输入|button|按钮|table|表格|form|表单|style|样式|viewbox|view box|design|设计稿|restore|restoration|还原)\b/gi,
|
|
6877
|
+
/(发灰|发浅|模糊|颜色不对|旧资源|复用|不居中|不对齐|尺寸不对|图标过大|图标过粗)/g
|
|
6878
|
+
];
|
|
6879
|
+
for (const pattern of keywordPatterns) {
|
|
6880
|
+
const matches = problemText.match(pattern);
|
|
6881
|
+
if (matches) {
|
|
6882
|
+
extractedKeywords.push(...matches.map((m) => m.toLowerCase()));
|
|
6883
|
+
}
|
|
6884
|
+
}
|
|
6885
|
+
return [...new Set(extractedKeywords)];
|
|
6886
|
+
}
|
|
6447
6887
|
var skillDefinitions = [
|
|
6448
6888
|
// ==================== 规范获取类 ====================
|
|
6449
6889
|
{
|
|
@@ -6711,17 +7151,7 @@ var skillDefinitions = [
|
|
|
6711
7151
|
detectedFramework = "react";
|
|
6712
7152
|
}
|
|
6713
7153
|
}
|
|
6714
|
-
const
|
|
6715
|
-
const keywordPatterns = [
|
|
6716
|
-
/\b(shadow|阴影|透出|transparency|clip|裁剪|layout|布局|animation|动画|color|颜色|border|边框|svg|icon|图标|input|输入|button|按钮|table|表格|form|表单|style|样式)\b/gi
|
|
6717
|
-
];
|
|
6718
|
-
for (const pattern of keywordPatterns) {
|
|
6719
|
-
const matches = problemText.match(pattern);
|
|
6720
|
-
if (matches) {
|
|
6721
|
-
extractedKeywords.push(...matches.map((m) => m.toLowerCase()));
|
|
6722
|
-
}
|
|
6723
|
-
}
|
|
6724
|
-
const uniqueKeywords = [...new Set(extractedKeywords)];
|
|
7154
|
+
const uniqueKeywords = extractTroubleshootingKeywords(problemText);
|
|
6725
7155
|
return wrapAsContent(await queryTroubleshootingCases({
|
|
6726
7156
|
framework: detectedFramework,
|
|
6727
7157
|
keywords: uniqueKeywords.length > 0 ? uniqueKeywords : void 0,
|
|
@@ -6781,6 +7211,29 @@ var skillDefinitions = [
|
|
|
6781
7211
|
outputPath: params.outputPath
|
|
6782
7212
|
});
|
|
6783
7213
|
}
|
|
7214
|
+
},
|
|
7215
|
+
{
|
|
7216
|
+
name: "validate_restoration",
|
|
7217
|
+
category: "sketch",
|
|
7218
|
+
description: "\u6821\u9A8C\u8BBE\u8BA1\u8FD8\u539F\u7ED3\u679C\u3002\u5BF9\u7167 sketch_measure \u7684 restorationContract \u68C0\u67E5\u9875\u9762\u9AA8\u67B6\u3001\u8D44\u6E90\u6765\u6E90\u3001\u771F\u5B9E\u8DEF\u7531\u662F\u5426\u63A5\u901A\uFF0C\u4EE5\u53CA UI review \u573A\u666F\u662F\u5426\u51C6\u5907\u5B8C\u6210",
|
|
7219
|
+
paramHints: [
|
|
7220
|
+
{ name: "measurePath", type: "string", description: "sketch_measure \u6267\u884C\u7ED3\u679C\u6587\u4EF6\u8DEF\u5F84\uFF0C\u6216\u4FDD\u5B58\u540E\u7684 compact JSON \u8DEF\u5F84" },
|
|
7221
|
+
{ name: "measureContent", type: "string", description: "\u76F4\u63A5\u4F20\u5165 sketch_measure \u7684 JSON \u6587\u672C\uFF08measurePath \u4E0E measureContent \u4E8C\u9009\u4E00\uFF09" },
|
|
7222
|
+
{ name: "codeFilePath", type: "string", required: true, description: "\u751F\u6210\u540E\u7684\u9875\u9762\u4EE3\u7801\u6587\u4EF6\u8DEF\u5F84" },
|
|
7223
|
+
{ name: "routeFilePath", type: "string", description: "\u8DEF\u7531\u6587\u4EF6\u8DEF\u5F84\uFF08\u7528\u4E8E\u68C0\u67E5\u662F\u5426\u4ECD\u6307\u5411\u5360\u4F4D\u9875\uFF09" },
|
|
7224
|
+
{ name: "routeName", type: "string", description: "\u8DEF\u7531\u540D\uFF0C\u5982 AppRoutes.recipientAdd" },
|
|
7225
|
+
{ name: "projectPath", type: "string", description: "\u9879\u76EE\u6839\u8DEF\u5F84\uFF08\u7528\u4E8E\u68C0\u67E5 visual review harness\uFF09" }
|
|
7226
|
+
],
|
|
7227
|
+
handler: async (params) => {
|
|
7228
|
+
return wrapAsContent(await validateRestoration({
|
|
7229
|
+
measurePath: params.measurePath,
|
|
7230
|
+
measureContent: params.measureContent,
|
|
7231
|
+
codeFilePath: params.codeFilePath,
|
|
7232
|
+
routeFilePath: params.routeFilePath,
|
|
7233
|
+
routeName: params.routeName,
|
|
7234
|
+
projectPath: params.projectPath
|
|
7235
|
+
}));
|
|
7236
|
+
}
|
|
6784
7237
|
}
|
|
6785
7238
|
];
|
|
6786
7239
|
|