| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160 |
- /**
- * 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 }
|