"use strict"; import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; import { Type } from "typebox"; import { getConfig } from "../config"; import { processImage } from "../media"; import { visionCompletion } from "../client"; import { UI_DIFF_CHECK } from "../prompts"; import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, truncateHead, } from "@earendil-works/pi-coding-agent"; export function registerUiDiffCheckTool(pi: ExtensionAPI) { pi.registerTool({ name: "ui_diff_check", label: "UI Diff Check", description: "Compare two UI screenshots — expected/reference design vs actual/current implementation — to identify visual differences, layout issues, styling discrepancies, and content errors. Ideal for design-to-implementation QA and visual regression testing. Supports local files (png, jpg) and HTTP(S) URLs.", promptSnippet: "Compare two UI screenshots — find visual differences, layout issues, styling bugs", promptGuidelines: [ "Use ui_diff_check when the user wants to compare a design mockup against an implementation screenshot. The first image (expectedImagePath) should be the expected/reference design, and the second (actualImagePath) should be the actual implementation.", ], parameters: Type.Object({ expectedImagePath: Type.String({ description: "Path to the expected/reference design screenshot (relative to cwd, supports @ prefix) or HTTP(S) URL.", }), actualImagePath: Type.String({ description: "Path to the actual/current implementation screenshot (relative to cwd, supports @ prefix) or HTTP(S) URL.", }), prompt: Type.Optional( Type.String({ description: "What specific aspects to compare? Defaults to a comprehensive visual regression analysis.", }) ), }), async execute(_toolCallId, params, signal, onUpdate, ctx) { const config = getConfig(); onUpdate?.({ content: [{ type: "text", text: "正在调用 GLM-4.6V 比对 UI 差异..." }], }); const expectedImg = processImage(params.expectedImagePath, ctx.cwd, config.maxImageSizeMB); const actualImg = processImage(params.actualImagePath, ctx.cwd, config.maxImageSizeMB); let userPrompt = params.prompt || "Compare these two screenshots and identify all visual differences, layout issues, styling discrepancies, and content errors."; // 前置图片角色说明 userPrompt = "\n" + "The first image is the EXPECTED/REFERENCE design (the target).\n" + "The second image is the ACTUAL/CURRENT implementation (what needs to be checked).\n" + "\n\n" + userPrompt; const result = await visionCompletion( config, UI_DIFF_CHECK, [expectedImg, actualImg], userPrompt, signal ); const truncated = truncateHead(result, { maxLines: DEFAULT_MAX_LINES, maxBytes: DEFAULT_MAX_BYTES, }); let text = truncated.content; if (truncated.truncated) { text += `\n\n[输出已截断: ${truncated.outputLines}/${truncated.totalLines} 行]`; } return { content: [{ type: "text", text }], details: { raw: result }, }; }, }); }