/** * Func 标签:image-area-cropping * * 约定:src/pages/processing/func/ 目录下每个文件名就是一个"可用标签/能力"。 * 本文件用于声明该标签存在(供文档/提示词/后续动态加载使用)。 * * 语义:根据区域坐标裁剪当前截图(ScreenShot.jpg)的指定区域,并保存到指定路径。 */ const electronAPI = require('../node-api.js') const tagName = 'image-area-cropping' const schema = { description: '根据区域坐标裁剪当前截图(ScreenShot.jpg)的指定区域,并保存到指定路径。', inputs: { area: '区域坐标(JSON字符串格式,包含 topLeft 和 bottomRight,或包含 x, y, width, height)', savePath: '保存路径(相对于工作流目录或绝对路径)', }, outputs: { result: '保存结果(成功返回 "1",失败返回 "0")', }, }; /** * 执行 image-area-cropping 功能 * 这个函数会被 ActionParser 调用 * * @param {Object} params - 参数对象 * @param {string} params.area - 区域坐标(JSON字符串或对象,格式:{topLeft: {x, y}, bottomRight: {x, y}} 或 {x, y, width, height}) * @param {string} params.savePath - 保存路径 * @param {string} params.folderPath - 工作流文件夹路径 * @param {string} params.device - 设备 ID/IP:Port(可选,用于获取最新截图) * @returns {Promise<{success: boolean, error?: string}>} */ async function executeImageAreaCropping({ area, savePath, folderPath, device }) { try { // 解析区域坐标 let areaObj = area; if (typeof area === 'string') { try { areaObj = JSON.parse(area); } catch (e) { return { success: false, error: `区域坐标格式错误,无法解析JSON: ${e.message}` }; } } if (!areaObj || typeof areaObj !== 'object') { return { success: false, error: '区域坐标必须是对象格式' }; } // 提取坐标信息(支持多种格式) let x, y, width, height; if (areaObj.topLeft && areaObj.bottomRight) { // 格式1:{topLeft: {x, y}, bottomRight: {x, y}} x = parseInt(areaObj.topLeft.x); y = parseInt(areaObj.topLeft.y); width = parseInt(areaObj.bottomRight.x - areaObj.topLeft.x); height = parseInt(areaObj.bottomRight.y - areaObj.topLeft.y); } else if (areaObj.topLeft && areaObj.topRight && areaObj.bottomLeft && areaObj.bottomRight) { // 格式1.5:{topLeft, topRight, bottomLeft, bottomRight} - 使用 topLeft 和 bottomRight x = parseInt(areaObj.topLeft.x); y = parseInt(areaObj.topLeft.y); width = parseInt(areaObj.bottomRight.x - areaObj.topLeft.x); height = parseInt(areaObj.bottomRight.y - areaObj.topLeft.y); } else if (areaObj.x !== undefined && areaObj.y !== undefined && areaObj.width !== undefined && areaObj.height !== undefined) { // 格式2:{x, y, width, height} x = parseInt(areaObj.x); y = parseInt(areaObj.y); width = parseInt(areaObj.width); height = parseInt(areaObj.height); } else { return { success: false, error: '区域坐标格式不正确,需要包含 topLeft/bottomRight 或 x/y/width/height' }; } // 验证坐标有效性 if (isNaN(x) || isNaN(y) || isNaN(width) || isNaN(height) || width <= 0 || height <= 0) { return { success: false, error: `区域坐标无效: x=${x}, y=${y}, width=${width}, height=${height}` }; } // 先通过 ADB 截图当前手机屏幕并保存到 history 文件夹 // 参考 screenshot.js 的实现方式 let imageBase64 = null; // 声明 imageBase64 变量 let screenshotPath; const fileExtension = 'jpg' if (folderPath.includes(':')) { // 绝对路径 screenshotPath = `${folderPath}/history/ScreenShot.${fileExtension}`; } else { // 相对路径,构建相对于项目根目录的路径 screenshotPath = `${folderPath}/history/ScreenShot.${fileExtension}`; } if (device && electronAPI.getCachedScreenshot) { const screenshotResult = await electronAPI.getCachedScreenshot(device) if (screenshotResult && screenshotResult.success && screenshotResult.data) { imageBase64 = screenshotResult.data } } if (!imageBase64 && electronAPI.captureScreenshot) { const screenshotResult = await electronAPI.captureScreenshot(device) if (screenshotResult && screenshotResult.success && screenshotResult.data) { imageBase64 = screenshotResult.data } } if (imageBase64 && electronAPI.saveBase64Image) { await electronAPI.saveBase64Image(imageBase64, screenshotPath) } // 处理保存路径(如果是相对路径,相对于工作流目录) let absoluteSavePath = savePath; if (!savePath.includes(':')) { // 相对路径,相对于工作流目录 if (folderPath.includes(':')) { absoluteSavePath = `${folderPath}/${savePath}`; } else { absoluteSavePath = `${folderPath}/${savePath}`; } } if (!imageBase64 && electronAPI.readImageFileAsBase64) { const fileContent = await electronAPI.readImageFileAsBase64(screenshotPath) if (!fileContent || !fileContent.success) { return { success: false, error: `无法读取截图文件: ${fileContent?.error || '未知错误'}` } } imageBase64 = fileContent.data } // 验证 base64 数据是否有效(至少应该是几百字节) if (!imageBase64 || imageBase64.length < 100) { return { success: false, error: `截图数据无效,base64长度: ${imageBase64?.length || 0},应该是至少几百字节。可能是截图失败或读取失败` }; } return { success: false, error: 'image-area-cropping 需使用 sharp/jimp 等 Node 图片库实现' } } catch (error) { return { success: false, error: error.message || '裁剪图片失败' }; } } module.exports = { tagName, schema, executeImageAreaCropping }