mta-mcp 3.15.2 → 3.15.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -6403,13 +6403,17 @@ var OUTPUT_GUIDE = {
6403
6403
  "- **token**: AppShadows.xxx token \u540D\uFF0C\u4F18\u5148\u4F7F\u7528\u4EE3\u66FF\u786C\u7F16\u7801 BoxShadow",
6404
6404
  "- **r**: borderRadius \u6570\u503C",
6405
6405
  "- **rp**: [left,top,right,bottom] \u76F8\u5BF9\u7236\u5BB9\u5668\u8FB9\u8DDD",
6406
+ "- **vf**: [x,y,w,h] \u89C6\u89C9\u8FB9\u754C\uFF1B\u5F53\u80CC\u666F\u5C42/\u9634\u5F71\u8D85\u51FA\u7236 Group frame \u65F6\uFF0C\u5BB9\u5668\u5C3A\u5BF8\u548C\u89C6\u89C9\u5E95\u8FB9\u8DDD\u4F18\u5148\u770B vf",
6406
6407
  "- **icon/svg/cs/xs**: \u56FE\u6807\u6807\u8BC6/\u771F\u5B9E SvgPicture.string(...) \u4EE3\u7801/containerSize/contentSize",
6408
+ "- **image/img/bitmapPath/bitmapScales**: \u4F4D\u56FE\u56FE\u5C42\u6807\u8BC6/Image.asset \u4EE3\u7801/\u5EFA\u8BAE\u843D\u76D8\u8DEF\u5F84/1x-3x \u5BFC\u51FA\u8981\u6C42",
6407
6409
  "- **svgMode/svgReason/svgFile**: preserve|tint / \u989C\u8272\u7B56\u7565\u539F\u56E0 / \u5EFA\u8BAE\u843D\u76D8\u6587\u4EF6\u540D",
6408
6410
  "- **text/style/family/textHeight**: \u6587\u672C\u5185\u5BB9/\u5B8C\u6574 TextStyle/\u5B57\u4F53\u65CF/\u884C\u9AD8\u6BD4",
6409
6411
  "- **lh/strut**: strutHeight / \u5B8C\u6574 StrutStyle(...)",
6410
6412
  "- **tag**: {bg,ph,pv,r,text} Tag\u5BB9\u5668\u4FE1\u606F",
6411
6413
  "- **layout**: {dir,gap,main,cross,pad} \u5E03\u5C40\u610F\u56FE",
6412
6414
  "- **ch**: \u5B50\u5143\u7D20\u6570\u7EC4\uFF08\u9012\u5F52\uFF09",
6415
+ "- **designSurface**: {width,height,maxWidth,wideScreenRule} \u8BBE\u8BA1\u753B\u677F\u7EA6\u675F\uFF1B\u79FB\u52A8\u7AEF\u7A84\u753B\u677F\u5728\u5BBD\u5C4F\u5FC5\u987B\u505A\u5C45\u4E2D\u5939\u6301",
6416
+ "- **selectionContext**: compact \u82E5\u53D1\u73B0\u9009\u4E2D\u7684\u662F\u5B50\u56FE\u5C42\uFF0C\u4F1A\u81EA\u52A8\u63D0\u5347\u5230\u6240\u5C5E\u753B\u677F/\u9876\u5C42\u5BB9\u5668\u5E76\u8BB0\u5F55 warning",
6413
6417
  "",
6414
6418
  "### \u4F7F\u7528\u65B9\u5F0F",
6415
6419
  "\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",
@@ -6418,7 +6422,10 @@ var OUTPUT_GUIDE = {
6418
6422
  "- \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
6423
  "- SVG \u662F\u5426\u8BEF\u52A0 ColorFilter\uFF1A\u4FDD\u7559\u539F\u8272\u7684 SVG \u4E0D\u5F97\u4E8C\u6B21\u67D3\u8272",
6420
6424
  "- \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",
6425
+ "- Bitmap \u662F\u5426\u8BEF\u5F53 SVG\uFF1ASketch Image \u56FE\u5C42\u5FC5\u987B\u5BFC\u51FA PNG + Image.asset\uFF0C\u4E14\u8865\u9F50 2.0x/3.0x \u53D8\u4F53",
6426
+ "- \u79FB\u52A8\u7AEF\u7A84\u753B\u677F\uFF08\u5982 390 \u5BBD\uFF09\u5728\u5BBD\u5C4F\u5FC5\u987B\u7528 Center + ConstrainedBox(maxWidth: designSurface.maxWidth) \u5939\u6301\uFF0C\u7981\u6B62\u8BA9 Positioned(left/right) \u76F4\u63A5\u62C9\u4F38\u5230\u5168\u5C4F",
6427
+ "- \u82E5\u8282\u70B9\u540C\u65F6\u7ED9\u51FA f \u4E0E vf\uFF0C\u8BF4\u660E\u80CC\u666F\u5C42/\u9634\u5F71\u8D85\u51FA\u903B\u8F91 frame\uFF1B\u5BB9\u5668\u5C3A\u5BF8\u548C\u5E95\u90E8\u89C6\u89C9\u95F4\u8DDD\u4F18\u5148\u4F7F\u7528 vf",
6428
+ "- \u82E5 selectionContext.autoPromoted=true\uFF0C\u8BF4\u660E\u4E4B\u524D\u9009\u4E2D\u4E86\u5185\u5C42 glyph\uFF1B\u540E\u7EED\u5C40\u90E8\u5BFC\u51FA\u56FE\u6807\u65F6\u5E94\u6539\u7528 cmd=svg \u9009\u4E2D\u5177\u4F53\u56FE\u5C42",
6422
6429
  "- \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"
6423
6430
  ].join("\n")
6424
6431
  };
@@ -6509,13 +6516,16 @@ async function sketchMeasure(args) {
6509
6516
  scriptPath: tempScriptPath,
6510
6517
  scriptLength: fullScript.length,
6511
6518
  pluginInstall: installStatus,
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",
6519
+ 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\u3001vf \u4E0E bitmapScales\uFF0C\u907F\u514D\u590D\u7528\u65E7\u9875\u9762\u8D44\u4EA7\u3002\u79FB\u52A8\u7AEF\u7A84\u753B\u677F\u8981\u6309 designSurface.maxWidth \u505A\u5BBD\u5C4F\u5939\u6301\u3002compact \u82E5\u68C0\u6D4B\u5230\u4F60\u9009\u4E2D\u7684\u662F\u5185\u5C42 glyph\uFF0C\u4F1A\u81EA\u52A8\u63D0\u5347\u5230\u6240\u5C5E\u753B\u677F\u5E76\u901A\u8FC7 selectionContext \u56DE\u62A5\u3002\u8BF7\u5148\u5728 Sketch \u4E2D\u9009\u4E2D\u76EE\u6807\u753B\u677F/\u56FE\u5C42\u3002",
6513
6520
  restoreChecklist: [
6514
6521
  "\u5148\u6309 restorationContract \u9501\u5B9A\u9875\u9762\u9AA8\u67B6\uFF0C\u518D\u5904\u7406\u5C40\u90E8\u6837\u5F0F",
6515
6522
  "\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
6523
  "preserve \u6A21\u5F0F SVG \u7981\u6B62\u8FFD\u52A0 ColorFilter",
6517
6524
  "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",
6525
+ "Sketch Image \u56FE\u5C42\u5BFC\u51FA PNG\uFF0C\u4E0D\u8981\u4F2A\u88C5\u6210 SVG \u4F7F\u7528\uFF0C\u5E76\u8865\u9F50 2.0x/3.0x \u53D8\u4F53",
6526
+ "\u79FB\u52A8\u7AEF\u7A84\u753B\u677F\u5728\u5BBD\u5C4F\u5FC5\u987B\u7528 Center + ConstrainedBox(maxWidth: designSurface.maxWidth) \u5939\u6301\uFF0C\u7981\u6B62\u5168\u5C4F\u62C9\u4F38",
6527
+ "\u82E5\u8282\u70B9\u540C\u65F6\u51FA\u73B0 f \u4E0E vf\uFF0C\u5BB9\u5668\u5C3A\u5BF8\u548C\u5E95\u90E8\u89C6\u89C9\u95F4\u8DDD\u4F18\u5148\u4F7F\u7528 vf\uFF0C\u4E0D\u8981\u53EA\u6284\u903B\u8F91 frame",
6528
+ "\u82E5 compact \u8FD4\u56DE selectionContext.autoPromoted=true\uFF0C\u8BF4\u660E\u4E4B\u524D\u8BEF\u9009\u4E2D\u4E86\u5B50\u56FE\u5C42\uFF0C\u4E0D\u8981\u628A\u5185\u5C42 glyph \u5F53\u6574\u9875\u5408\u540C",
6519
6529
  "\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
6530
  ],
6521
6531
  postRestoreValidator: {
@@ -6600,7 +6610,9 @@ function collectAssetContracts(node, bucket = []) {
6600
6610
  if (node.t === "Image") {
6601
6611
  bucket.push({
6602
6612
  sketchName: node.n || "Image",
6603
- sourceType: "bitmap"
6613
+ fileName: typeof node.bitmapFile === "string" ? path20.posix.basename(node.bitmapFile) : typeof node.bitmapPath === "string" ? path20.posix.basename(node.bitmapPath) : void 0,
6614
+ sourceType: "bitmap",
6615
+ scaleVariants: node.bitmapScales
6604
6616
  });
6605
6617
  }
6606
6618
  for (const child of node.ch || []) {
@@ -6612,12 +6624,64 @@ function extractCodeAssets(codeContent) {
6612
6624
  const matches = codeContent.match(/assets\/[A-Za-z0-9_./-]+\.(svg|png|jpg|jpeg|webp)/g);
6613
6625
  return matches ? [...new Set(matches)] : [];
6614
6626
  }
6627
+ function hasCodePropertyLiteral(codeContent, property, value) {
6628
+ const escapedValue = String(value).replace(".", "\\.");
6629
+ return new RegExp(`${property}\\s*:\\s*${escapedValue}(?:\\.0+)?\\b`).test(codeContent);
6630
+ }
6631
+ function hasResponsiveSurfaceConstraint(codeContent, designWidth) {
6632
+ const escapedWidth = String(designWidth).replace(".", "\\.");
6633
+ const directPatterns = [
6634
+ new RegExp(`maxWidth\\s*:\\s*${escapedWidth}(?:\\.0+)?\\b`),
6635
+ new RegExp(`width\\s*:\\s*${escapedWidth}(?:\\.0+)?\\b`),
6636
+ /ConstrainedBox\s*\(/,
6637
+ /BoxConstraints\s*\([^)]*maxWidth\s*:/,
6638
+ /LayoutBuilder\s*\(/,
6639
+ /MediaQuery\.(sizeOf|of)\(/,
6640
+ /math\.(min|max)\(/,
6641
+ /clamp\(/
6642
+ ];
6643
+ return directPatterns.some((pattern) => pattern.test(codeContent));
6644
+ }
6645
+ function hasWideStretchRisk(codeContent) {
6646
+ const edgePinnedPositioned = /Positioned\([\s\S]{0,220}left\s*:\s*[-\d.]+[\s\S]{0,220}right\s*:\s*[-\d.]+/.test(codeContent) || /Positioned\([\s\S]{0,220}right\s*:\s*[-\d.]+[\s\S]{0,220}left\s*:\s*[-\d.]+/.test(codeContent);
6647
+ return edgePinnedPositioned || /SizedBox\.expand\s*\(/.test(codeContent) || /double\.infinity/.test(codeContent);
6648
+ }
6649
+ function collectVisualFrameContracts(node, bucket = []) {
6650
+ if (!node) {
6651
+ return bucket;
6652
+ }
6653
+ if (node.f && node.vf) {
6654
+ const hasVisualDelta = node.f.some((value, index) => value !== node.vf[index]);
6655
+ if (hasVisualDelta) {
6656
+ bucket.push({
6657
+ nodeName: node.n || node.t || "unknown",
6658
+ frame: node.f,
6659
+ visualFrame: node.vf
6660
+ });
6661
+ }
6662
+ }
6663
+ for (const child of node.ch || []) {
6664
+ collectVisualFrameContracts(child, bucket);
6665
+ }
6666
+ return bucket;
6667
+ }
6668
+ function resolveProjectAssetPath(projectPath, assetPath) {
6669
+ return path20.join(projectPath, assetPath);
6670
+ }
6671
+ function getBitmapScaleVariantPaths(projectPath, assetPath) {
6672
+ const relativeDir = path20.posix.dirname(assetPath);
6673
+ const fileName = path20.posix.basename(assetPath);
6674
+ return [
6675
+ path20.join(projectPath, relativeDir, "2.0x", fileName),
6676
+ path20.join(projectPath, relativeDir, "3.0x", fileName)
6677
+ ];
6678
+ }
6615
6679
  function extractPageClassName(codeContent) {
6616
6680
  const match = codeContent.match(/class\s+([A-Za-z0-9_]+)\s+extends\s+(StatelessWidget|StatefulWidget)/);
6617
6681
  return (match == null ? void 0 : match[1]) || null;
6618
6682
  }
6619
6683
  function buildContractValidator(payload, codeContent) {
6620
- var _a, _b, _c;
6684
+ var _a, _b, _c, _d, _e;
6621
6685
  const findings = [];
6622
6686
  const blockOrder = ((_a = payload.restorationContract) == null ? void 0 : _a.blockOrder) || [];
6623
6687
  const navCopyLedger = ((_b = payload.restorationContract) == null ? void 0 : _b.navCopyLedger) || [];
@@ -6694,6 +6758,34 @@ function buildContractValidator(payload, codeContent) {
6694
6758
  });
6695
6759
  }
6696
6760
  }
6761
+ const designWidth = ((_d = payload.designSurface) == null ? void 0 : _d.maxWidth) || ((_e = payload.artboard) == null ? void 0 : _e.w);
6762
+ if (designWidth && designWidth <= 430) {
6763
+ const isClamped = hasResponsiveSurfaceConstraint(codeContent, designWidth);
6764
+ if (hasWideStretchRisk(codeContent) && !isClamped) {
6765
+ findings.push({
6766
+ id: "design_surface_constraint_missing",
6767
+ severity: "error",
6768
+ message: `\u5F53\u524D\u8BBE\u8BA1\u7A3F\u4E3A ${designWidth} \u5BBD\u79FB\u52A8\u7AEF\u753B\u677F\uFF0C\u4F46\u4EE3\u7801\u672A\u68C0\u6D4B\u5230\u5BBD\u5C4F\u5939\u6301\u7EA6\u675F\uFF0C\u5BBD\u7A97\u53E3\u4E0B\u4F1A\u88AB\u76F4\u63A5\u62C9\u4F38\u3002`,
6769
+ suggestion: "\u5916\u5C42\u5148\u7528 Center + ConstrainedBox(maxWidth: designSurface.maxWidth) \u6216\u7B49\u6548\u7EA6\u675F\uFF0C\u518D\u5728\u7EA6\u675F\u5185\u4F7F\u7528 Positioned/Stack\u3002"
6770
+ });
6771
+ }
6772
+ }
6773
+ const visualFrameContracts = collectVisualFrameContracts(payload.tree);
6774
+ for (const contract of visualFrameContracts) {
6775
+ const [, , frameWidth, frameHeight] = contract.frame;
6776
+ const [, , visualWidth, visualHeight] = contract.visualFrame;
6777
+ const widthMismatch = frameWidth !== visualWidth && hasCodePropertyLiteral(codeContent, "width", frameWidth) && !hasCodePropertyLiteral(codeContent, "width", visualWidth);
6778
+ const heightMismatch = frameHeight !== visualHeight && hasCodePropertyLiteral(codeContent, "height", frameHeight) && !hasCodePropertyLiteral(codeContent, "height", visualHeight);
6779
+ if (widthMismatch || heightMismatch) {
6780
+ findings.push({
6781
+ id: "visual_frame_literal_mismatch",
6782
+ severity: "warning",
6783
+ message: `${contract.nodeName} \u7684\u89C6\u89C9\u8FB9\u754C\u662F ${visualWidth}x${visualHeight}\uFF0C\u4F46\u4EE3\u7801\u770B\u8D77\u6765\u4ECD\u5728\u4F7F\u7528\u903B\u8F91 frame ${frameWidth}x${frameHeight}\uFF0C\u5BB9\u6613\u9020\u6210\u5E95\u8FB9\u8DDD\u6216\u9634\u5F71\u89C6\u89C9\u8BEF\u5DEE\u3002`,
6784
+ evidence: [`${contract.nodeName}: frame=${contract.frame.join(",")} visualFrame=${contract.visualFrame.join(",")}`],
6785
+ suggestion: "\u5F53 compact \u540C\u65F6\u7ED9\u51FA f \u4E0E vf \u65F6\uFF0C\u5916\u5C42\u5BB9\u5668\u5C3A\u5BF8\u548C\u7531\u6B64\u63A8\u5BFC\u7684\u5E95\u90E8\u89C6\u89C9\u95F4\u8DDD\u4F18\u5148\u4EE5 vf \u4E3A\u51C6\u3002"
6786
+ });
6787
+ }
6788
+ }
6697
6789
  return {
6698
6790
  passed: !findings.some((item) => item.severity === "error"),
6699
6791
  requiredBlocks,
@@ -6701,7 +6793,7 @@ function buildContractValidator(payload, codeContent) {
6701
6793
  findings
6702
6794
  };
6703
6795
  }
6704
- function buildAssetValidator(payload, codeContent) {
6796
+ function buildAssetValidator(payload, codeContent, args) {
6705
6797
  const findings = [];
6706
6798
  const assetContracts = collectAssetContracts(payload.tree);
6707
6799
  const expectedVectorFiles = new Set(
@@ -6738,6 +6830,54 @@ function buildAssetValidator(payload, codeContent) {
6738
6830
  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
6831
  });
6740
6832
  }
6833
+ if (expectedBitmapCount > 0 && codeBitmapAssets.length > 0) {
6834
+ const pageClassName = extractPageClassName(codeContent) || "";
6835
+ const normalizedStem = pageClassName.replace(/Page$/, "").replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase();
6836
+ if (normalizedStem) {
6837
+ const hasPageLocalBitmap = codeBitmapAssets.some(
6838
+ (asset) => path20.posix.basename(asset).toLowerCase().includes(normalizedStem)
6839
+ );
6840
+ if (!hasPageLocalBitmap) {
6841
+ findings.push({
6842
+ id: "bitmap_assets_not_page_local",
6843
+ severity: "warning",
6844
+ message: "\u5F53\u524D\u9875\u9762\u4F4D\u56FE\u8D44\u6E90\u770B\u8D77\u6765\u4E0D\u662F page-local \u547D\u540D\uFF0C\u5B58\u5728\u7EE7\u7EED\u590D\u7528\u65E7\u9875\u9762 PNG \u7684\u98CE\u9669\u3002",
6845
+ evidence: codeBitmapAssets,
6846
+ suggestion: "\u4F4D\u56FE\u8D44\u6E90\u4F18\u5148\u6309\u5F53\u524D\u9875\u9762\u547D\u540D\u5BFC\u51FA\uFF0C\u4F8B\u5982\u5E26\u4E0A\u9875\u9762 stem\uFF0C\u907F\u514D\u6DF7\u5165\u65E7\u9875\u9762\u901A\u7528 PNG\u3002"
6847
+ });
6848
+ }
6849
+ }
6850
+ }
6851
+ const expectedBitmapScales = new Set(
6852
+ assetContracts.filter((item) => item.sourceType === "bitmap").flatMap((item) => item.scaleVariants || [])
6853
+ );
6854
+ if (expectedBitmapCount > 0 && codeBitmapAssets.length > 0 && args.projectPath) {
6855
+ const requiresRetinaVariants = expectedBitmapScales.size === 0 || expectedBitmapScales.has("2.0x") || expectedBitmapScales.has("3.0x");
6856
+ if (requiresRetinaVariants) {
6857
+ const missingRetinaEvidence = [];
6858
+ for (const asset of codeBitmapAssets) {
6859
+ const assetOnDisk = resolveProjectAssetPath(args.projectPath, asset);
6860
+ if (!fs20.existsSync(assetOnDisk)) {
6861
+ missingRetinaEvidence.push(`${asset} (base missing)`);
6862
+ continue;
6863
+ }
6864
+ const variantPaths = getBitmapScaleVariantPaths(args.projectPath, asset);
6865
+ const missingVariants = variantPaths.filter((variantPath) => !fs20.existsSync(variantPath)).map((variantPath) => path20.relative(args.projectPath, variantPath));
6866
+ if (missingVariants.length > 0) {
6867
+ missingRetinaEvidence.push(`${asset} -> missing ${missingVariants.join(", ")}`);
6868
+ }
6869
+ }
6870
+ if (missingRetinaEvidence.length > 0) {
6871
+ findings.push({
6872
+ id: "bitmap_retina_variants_missing",
6873
+ severity: "error",
6874
+ message: "\u4F4D\u56FE\u8D44\u6E90\u7F3A\u5C11 2.0x/3.0x \u53D8\u4F53\uFF0C\u9AD8\u5206\u5C4F\u4E0B\u5BB9\u6613\u53D1\u7CCA\u3002",
6875
+ evidence: missingRetinaEvidence,
6876
+ suggestion: "\u6309\u5F53\u524D\u753B\u677F\u91CD\u65B0\u5BFC\u51FA PNG\uFF0C\u5E76\u8865\u9F50 assets/.../2.0x \u4E0E 3.0x \u53D8\u4F53\u540E\u518D\u9A8C\u6536\u3002"
6877
+ });
6878
+ }
6879
+ }
6880
+ }
6741
6881
  const preserveContracts = assetContracts.filter((item) => item.mode === "preserve");
6742
6882
  if (preserveContracts.length > 0 && /colorFilter\s*:|ColorFilter\./.test(codeContent)) {
6743
6883
  findings.push({
@@ -6849,7 +6989,7 @@ async function validateRestoration(args) {
6849
6989
  const payload = loadMeasurePayload(args);
6850
6990
  const codeContent = fs20.readFileSync(args.codeFilePath, "utf-8");
6851
6991
  const contractValidator = buildContractValidator(payload, codeContent);
6852
- const assetValidator = buildAssetValidator(payload, codeContent);
6992
+ const assetValidator = buildAssetValidator(payload, codeContent, args);
6853
6993
  const routeReviewValidator = buildRouteReviewValidator(args, codeContent);
6854
6994
  return {
6855
6995
  artboardName: (_a = payload.artboard) == null ? void 0 : _a.name,