rust-rpa
v0.1.7
Published
Rust-based RPA automation library for Node.js
Downloads
382
Maintainers
Readme
rust-rpa
基于 Rust 的高性能 Node.js RPA(机器人流程自动化)库。
特性
- 🚀 高性能: 使用 Rust 编写,追求极致速度和效率
- 🔄 跨平台: 支持 Windows 和 macOS
- 📦 易于使用: 简洁的面向对象 API,支持 TypeScript
- ⚡ 非阻塞: 异步操作不会阻塞 Node.js 事件循环
- 🔒 类型安全: 完整的 TypeScript 类型定义
- 🖱️ 输入自动化: Mouse 和 Keyboard 类,完整的输入控制
- 🪟 窗口管理: 基于 XCap 的窗口枚举和信息获取
- 🖥️ 多显示器支持: Monitor 类,支持多显示器设置
- 📸 屏幕截图: 支持全屏和窗口截图,多种图片格式
- 🔑 权限管理: Permission 类,自动检测并弹出系统权限授权对话框
安装
npm install rust-rpa支持的平台:
- Windows (x64, x86)
- macOS (Intel x64, Apple Silicon ARM64)
注意: 当前版本不支持 Linux。
系统要求
Windows
- Windows 10 或更高版本 (x64 或 x86)
- 某些操作可能需要管理员权限
macOS
- macOS 10.13 或更高版本 (Intel 或 Apple Silicon)
- 需要辅助功能权限(见下文)
命令行工具
安装后可通过 rpa 命令直接使用,无需编写代码:
# 查看帮助
rpa --help
# 检查权限(macOS 自动弹出授权)
rpa permission --prompt
# 列出所有窗口
rpa window:list
# 将窗口置于最前
rpa window:focus Chrome
# 设置窗口位置和大小
rpa window:bounds Chrome 100 100 800 600
# 截取主显示器
rpa screenshot -o desktop.png
# 截取指定窗口
rpa screenshot --window Chrome -o chrome.png
# 鼠标操作
rpa mouse:position
rpa mouse:move 500 300
rpa mouse:click right
rpa mouse:scroll 0 -3
# 键盘操作
rpa keyboard:type "Hello World"
rpa keyboard:press enter
rpa keyboard:press ctrl+c
# 剪贴板
rpa clipboard:read
rpa clipboard:write "Hello"
rpa clipboard:paste
# 列出所有显示器
rpa monitor:list所有命令支持 --help 查看详细用法,如 rpa screenshot --help。
示例代码
项目提供了丰富的示例代码,位于 examples/ 目录中:
permission-demo.js- 权限检查演示list-windows.js- 窗口管理演示screenshot-demo.js- 屏幕截图功能演示clipboard-demo.js- 剪贴板操作演示image-processing-demo.js- 图像处理功能演示find-icon-demo.js- 完整的 findIcon 功能演示find-icon-basic.js- findIcon 基础用法示例
运行示例:
node examples/permission-demo.js
node examples/list-windows.js使用方法
JavaScript
const { Window, Monitor, Mouse, Keyboard, Permission } = require('rust-rpa');
async function main() {
try {
// 检查权限(macOS 上无权限会自动弹出授权对话框)
Permission.checkAccessibility();
Permission.checkScreenCapture();
// 获取所有窗口
const windows = Window.all();
console.log(`Found ${windows.length} windows`);
windows.forEach(win => {
console.log(`${win.appName()}: ${win.title()}`);
console.log(` PID: ${win.pid()}, Parent PID: ${win.parentId()}`);
console.log(` Position: (${win.x()}, ${win.y()}), Size: ${win.width()}x${win.height()}`);
});
// 将窗口置于最前
await windows[0].bringToFront();
// 获取所有显示器
const monitors = Monitor.all();
const primaryMonitor = monitors.find(m => m.isPrimary());
// 截图
const image = await primaryMonitor.captureImage();
const pngBuffer = await image.toPng();
// 鼠标和键盘控制
await Mouse.moveTo(100, 100);
await Mouse.click('left');
await Keyboard.typeText('Hello World!');
} catch (error) {
console.error('Error:', error.message);
}
}
main();TypeScript
import { Window, Monitor, Mouse, Keyboard } from 'rust-rpa';
async function main(): Promise<void> {
try {
// 获取所有窗口
const windows = Window.all();
console.log(`Found ${windows.length} windows`);
windows.forEach((win) => {
console.log(`${win.appName()}: ${win.title()}`);
});
// 截图
const monitors = Monitor.all();
const image = await monitors[0].captureImage();
// 输入控制
await Mouse.moveTo(100, 100);
await Keyboard.typeText('Hello!');
} catch (error) {
console.error('Error:', error);
}
}
main();API
Window 类
窗口管理类,基于 XCap 实现。
静态方法
Window.all(): Window[]
获取所有可见窗口列表。
const windows = Window.all();实例方法
id(): number- 获取窗口 IDpid(): number- 获取窗口进程 IDparentId(): number- 获取窗口所属进程的父进程 IDtitle(): string- 获取窗口标题appName(): string- 获取应用程序名称x(): number- 获取窗口 X 坐标y(): number- 获取窗口 Y 坐标width(): number- 获取窗口宽度height(): number- 获取窗口高度getSize(): WindowSize- 获取窗口尺寸(宽度和高度)isMinimized(): boolean- 是否最小化isMaximized(): boolean- 是否最大化isFocused(): boolean- 是否聚焦bringToFront(): Promise<void>- 将窗口置于最前(激活并置顶)currentMonitor(): Monitor- 获取窗口所在显示器currentMonitorId(): number- 获取窗口所在显示器的 IDgetBounds(): WindowBounds- 获取窗口边界(位置和大小)setBounds(bounds): Promise<void>- 设置窗口边界(位置和大小)toJSON(): WindowToJson- 转为 JSON 可序列化对象captureImage(options?: CaptureImageOptions): Promise<ImageData>- 截取窗口图像;可选options.size指定目标宽高并自动缩放;options.from为'screen'时截取窗口所在显示器整屏;options.region指定仅截取逻辑像素区域;options.autoSize为 true 时在 Windows 高 DPI 下自动缩放到逻辑像素尺寸
Monitor 类
显示器管理类。
静态方法
Monitor.all(): Monitor[]
获取所有显示器列表。
const monitors = Monitor.all();
const primary = monitors.find(m => m.isPrimary());实例方法
id(): number- 获取显示器 IDname(): string- 获取显示器名称x(): number- 获取显示器 X 坐标y(): number- 获取显示器 Y 坐标width(): number- 获取显示器宽度height(): number- 获取显示器高度scaleFactor(): number- 获取缩放因子isPrimary(): boolean- 是否主显示器captureImage(options?: CaptureImageOptions): Promise<ImageData>- 截取显示器图像;可选options.size指定目标宽高并自动缩放
ImageData 类
图像数据类,用于截图和图像处理操作。
静态工厂方法
ImageData.fromFile(filePath): Promise<ImageData>- 从文件加载图片ImageData.fromBuffer(buffer): Promise<ImageData>- 从 Buffer 解码图片
属性
width: number- 图像宽度(只读)height: number- 图像高度(只读)
方法
格式转换与保存
toPng(): Promise<Buffer>- 转换为 PNG 格式toJpeg(): Promise<Buffer>- 转换为 JPEG 格式toBmp(): Promise<Buffer>- 转换为 BMP 格式toFile(filePath): Promise<void>- 保存到文件(根据扩展名自动识别格式:.png/.jpg/.jpeg/.bmp)
图像处理
crop(x, y, width, height): Promise<ImageData>- 裁剪图像resize(width, height): Promise<ImageData>- 缩放图像(使用 Lanczos3 高质量算法)grayscale(): Promise<ImageData>- 转换为灰度图(使用标准加权算法:0.299R + 0.587G + 0.114B)findIcon(template, options?): Promise<MatchResult>- 查找模板图标(模板匹配)
数据访问
getRawData(): Buffer- 获取原始像素数据(RGBA 格式,每像素 4 字节)metadata(): ImageMetadata- 获取图片元信息
findIcon 参数:
template: ImageData- 模板图片(要查找的图标)options?: MatchOptions- 可选的匹配选项对象threshold?: number- 匹配阈值 (0.0-1.0),默认 0.8regions?: Array<{x, y, width, height}>- 可选的搜索区域列表,默认为空(全图搜索)- 区域可超出图片边界,会自动裁剪为与图片的交集范围进行查找
MatchOptions 说明:
threshold(匹配阈值)
- 取值范围:0.0 - 1.0
- 默认值:0.8
- 说明:相似度分数必须大于此阈值才算匹配成功
- 建议:0.8 是个好的起点,根据实际效果调整
regions(搜索区域)
- 格式:
[{x: number, y: number, width: number, height: number}] - 默认:空数组(全图搜索)
- 说明:限定在指定区域内搜索,可提高效率和准确性
- 示例:
[{x: 0, y: 0, width: 100, height: 100}]
- 格式:
使用建议:
- 大多数情况使用默认参数即可:
findIcon(template) - 需要调整灵敏度时指定阈值:
findIcon(template, { threshold: 0.9 }) - 在特定区域查找可提高性能:
findIcon(template, { threshold: 0.8, regions: [...] })
MatchResult 类型:
interface MatchResult {
found: boolean; // 是否找到匹配
score: number; // 相似度分数 (0.0-1.0)
x: number; // 匹配位置 x 坐标
y: number; // 匹配位置 y 坐标
width: number; // 模板宽度
height: number; // 模板高度
}ImageMetadata 类型:
interface ImageMetadata {
width: number; // 图片宽度
height: number; // 图片高度
channels: number; // 通道数(固定为 4,RGBA)
bytesPerPixel: number; // 每像素字节数(固定为 4)
dataSize: number; // 数据总字节数
}captureImage 选项(Monitor / Window):
// 截取区域(逻辑像素),仅对 Window.captureImage 有效
interface CaptureRegion {
x: number; // 区域左上角 x(相对截图画布左上角)
y: number; // 区域左上角 y
width: number; // 区域宽度
height: number; // 区域高度
}
interface CaptureImageOptions {
size?: { width: number; height: number }; // 可选,截取后缩放到指定宽高
// 以下仅对 Window.captureImage 有效:
from?: 'window' | 'screen'; // 默认 'window' 截窗口;'screen' 截窗口所在显示器整屏(可配合 getBounds 与 image.crop 裁剪出窗口)
region?: CaptureRegion | null; // 仅截取该逻辑像素区域,不填则截全图
autoSize?: boolean | null; // 为 true 时在 Windows 存在 DPI 缩放时自动将图像缩放到逻辑像素宽高(与 getSize/getBounds 一致)
}Mouse 类
鼠标控制类,所有方法都是静态的。
静态方法
Mouse.moveTo(x, y, duration?): Promise<void>- 移动鼠标到指定坐标。duration 可选,移动动画持续时间(秒),默认为 0(瞬间移动)。如果设置 > 0,鼠标将以平滑动画的方式移动到目标点,动画过程中会触发 mouseMove 事件Mouse.click(button?): Promise<void>- 点击鼠标。button 可选,可用枚举MouseButton.Left/MouseButton.Right或字符串'left'/'right'等,默认左键Mouse.doubleClick(button?): Promise<void>- 双击鼠标,button 同上Mouse.down(button?): Promise<void>- 按下鼠标按钮,button 同上Mouse.up(button?): Promise<void>- 释放鼠标按钮,button 同上- MouseButton 枚举:
MouseButton.Left|Right|Middle|Back|Forward,用于上述 button 参数 Mouse.scroll(deltaX?, deltaY?): Promise<void>- 滚动鼠标滚轮。方向:deltaY正数向上、负数向下;deltaX正数向右、负数向左。如scroll(0, 3)向上滚、scroll(0, -3)向下滚Mouse.position(): Promise<{ x: number, y: number }>- 获取当前鼠标位置
Keyboard 类
键盘控制类,所有方法都是静态的。
静态方法
Keyboard.typeText(text): Promise<void>- 输入文本Keyboard.click(key): Promise<void>- 按下并释放按键Keyboard.down(key): Promise<void>- 按下按键Keyboard.up(key): Promise<void>- 释放按键Keyboard.sequence(keys): Promise<void>- 按键序列(如[Key.Ctrl, Key.C])
key 参数:可使用 Key 枚举(如 Key.Enter、Key.Ctrl、Key.F1)或字符串(如 'enter'、'ctrl')。Key 提供字母 A–Z、数字 Num0–Num9、功能键 F1–F12、修饰键 Control/Ctrl/Shift/Alt/Meta/Command/Win、控制键 Enter/Escape/Tab/Space/Backspace/Delete/Insert、方向键 Left/Right/Up/Down、Home/End/PageUp/PageDown、数字键盘 Numpad0–Numpad9、NumpadAdd/NumpadEnter 等。
Clipboard 类
剪贴板控制类,所有方法都是静态的。
静态方法
Clipboard.readText(): Promise<string>- 读取剪贴板文本内容Clipboard.writeText(text): Promise<void>- 将文本写入剪贴板Clipboard.writeImage(source): Promise<void>- 将图片写入剪贴板,source支持文件路径(string)、base64 字符串、data URI 或 BufferClipboard.writeFile(paths): Promise<void>- 将文件路径写入剪贴板,粘贴时在资源管理器/访达中为文件(paths可为单个路径字符串或路径数组)Clipboard.paste(): Promise<void>- 执行粘贴操作(使用 Cmd/Ctrl+V 快捷键)Clipboard.pasteText(text): Promise<void>- 将文本写入剪贴板并粘贴,完成后自动恢复剪贴板原始内容Clipboard.pasteImage(source): Promise<void>- 将图片写入剪贴板并粘贴,完成后自动恢复剪贴板原始内容(source同writeImage)Clipboard.pasteFile(paths): Promise<void>- 将文件路径写入剪贴板并粘贴,不会自动恢复剪贴板原始内容
Permission 类
权限检查工具类,所有方法都是静态的。
静态方法
Permission.checkAccessibility(prompt?): boolean- 检查辅助功能权限(鼠标、键盘、窗口操作等需要此权限)Permission.checkScreenCapture(prompt?): boolean- 检查屏幕录制权限(截图功能需要此权限)
参数说明:
prompt(可选,默认true):无权限时是否自动弹出系统授权对话框(仅 macOS 生效)
平台行为差异:
| 平台 | checkAccessibility | checkScreenCapture |
|------|--------------------|--------------------|
| macOS | 调用 AXIsProcessTrustedWithOptions,prompt=true 时弹出辅助功能授权对话框 | 调用 CGRequestScreenCaptureAccess,prompt=true 时弹出屏幕录制授权对话框 |
| Windows | 检查是否以管理员身份运行(管理员即有权限) | 检查是否以管理员身份运行 |
const { Permission } = require('rust-rpa');
// 检查并弹出授权(推荐在应用启动时调用)
const hasAccessibility = Permission.checkAccessibility();
const hasScreenCapture = Permission.checkScreenCapture();
console.log('辅助功能权限:', hasAccessibility);
console.log('屏幕录制权限:', hasScreenCapture);
// 仅检查,不弹窗
const check = Permission.checkAccessibility(false);macOS 权限要求
辅助功能权限(输入控制)
在 macOS 上使用鼠标和键盘控制功能时,需要授予辅助功能权限:
- 打开 系统偏好设置
- 进入 安全性与隐私 > 隐私 > 辅助功能
- 点击锁图标进行更改
- 将您的终端应用程序或 IDE 添加到列表中
- 重启应用程序
屏幕录制权限(截图功能)
使用截图功能时,需要授予屏幕录制权限:
- 打开 系统偏好设置
- 进入 安全性与隐私 > 隐私 > 屏幕录制
- 点击锁图标进行更改
- 勾选您的终端应用程序或 IDE
- 重启应用程序
示例
窗口管理
const { Window } = require('rust-rpa');
// 获取所有窗口
const windows = Window.all();
console.log(`Found ${windows.length} windows`);
// 遍历窗口
windows.forEach(win => {
console.log(`${win.appName()}: ${win.title()}`);
console.log(` PID: ${win.pid()}, Parent PID: ${win.parentId()}`);
console.log(` Position: (${win.x()}, ${win.y()})`);
console.log(` Size: ${win.width()}x${win.height()}`);
console.log(` Minimized: ${win.isMinimized()}, Focused: ${win.isFocused()}`);
});
// 将窗口置于最前
const target = windows.find(w => w.appName().includes('Chrome'));
if (target) {
await target.bringToFront();
}
// 获取/设置窗口边界
const bounds = windows[0].getBounds();
console.log(`Bounds: ${JSON.stringify(bounds)}`);
await windows[0].setBounds({ x: 100, y: 100, width: 800, height: 600 });
// 获取窗口所在显示器
const monitor = windows[0].currentMonitor();
console.log(`Window is on monitor: ${monitor.name()}`);
// 转为 JSON 对象
console.log(JSON.stringify(windows[0].toJSON(), null, 2));显示器管理
const { Monitor } = require('rust-rpa');
// 获取所有显示器
const monitors = Monitor.all();
monitors.forEach(m => {
console.log(`Monitor: ${m.name()}`);
console.log(` Resolution: ${m.width()}x${m.height()}`);
console.log(` Scale: ${m.scaleFactor()}x`);
console.log(` Primary: ${m.isPrimary()}`);
});
// 获取主显示器
const primary = monitors.find(m => m.isPrimary());屏幕截图
const fs = require('fs');
const { Monitor, Window, ImageData } = require('rust-rpa');
// 截取显示器
const monitors = Monitor.all();
const image = await monitors[0].captureImage();
// 截取时指定目标尺寸(自动缩放,便于后续扩展其他选项)
const imageResized = await monitors[0].captureImage({ size: { width: 800, height: 600 } });
// 保存为不同格式(方式 1:转换为 Buffer)
const pngBuffer = await image.toPng();
fs.writeFileSync('screenshot.png', pngBuffer);
const jpegBuffer = await image.toJpeg();
fs.writeFileSync('screenshot.jpg', jpegBuffer);
// 保存为不同格式(方式 2:直接保存文件)
await image.toFile('screenshot.png');
await image.toFile('screenshot.jpg');
await image.toFile('screenshot.bmp');
// 裁剪图像
const cropped = await image.crop(0, 0, 800, 600);
await cropped.toFile('cropped.png');
// 截取窗口
const windows = Window.all();
if (windows.length > 0) {
const windowImage = await windows[0].captureImage();
await windowImage.toFile('window.png');
}截图选项:captureImage(options?) 接受可选对象 CaptureImageOptions:
size?: { width, height }:截取后缩放到指定宽高,不传则返回原始尺寸。- Window 专用:
from?: 'window' | 'screen'默认截窗口,设为'screen'可截窗口所在显示器整屏再配合getBounds()与image.crop()裁剪;region?: { x, y, width, height }仅截取该逻辑像素区域;autoSize?: true在 Windows 高 DPI 下自动将图像缩放到逻辑像素宽高(与getSize/getBounds一致)。
输入自动化
const { Mouse, MouseButton, Keyboard, Key } = require('rust-rpa');
// 鼠标操作
await Mouse.moveTo(100, 200); // 瞬间移动鼠标
await Mouse.moveTo(100, 200, 0.5); // 平滑动画移动,持续 0.5 秒
await Mouse.click(MouseButton.Left); // 左键点击
await Mouse.click(MouseButton.Right); // 右键点击
await Mouse.doubleClick(); // 双击
await Mouse.scroll(0, 3); // 向上滚动(正值 = 远离用户)
await Mouse.scroll(0, -3); // 向下滚动(负值 = 朝向用户)
// 获取鼠标位置
const { x, y } = await Mouse.position();
console.log(`Mouse at (${x}, ${y})`);
// 键盘操作(key 可用 Key 枚举或字符串)
await Keyboard.typeText('Hello World!'); // 输入文本
await Keyboard.click(Key.Enter); // 按 Enter
// 组合键
await Keyboard.sequence([Key.Ctrl, Key.A]); // Ctrl+A (全选)
await Keyboard.sequence([Key.Ctrl, Key.C]); // Ctrl+C (复制)
await Keyboard.sequence([Key.Meta, Key.V]); // Cmd+V (粘贴,macOS)
// 按键按下和释放
await Keyboard.down(Key.Shift);
await Keyboard.click(Key.A);
await Keyboard.up(Key.Shift);剪贴板操作
const path = require('path');
const { Clipboard, ImageData } = require('rust-rpa');
// 读取剪贴板
const text = await Clipboard.readText();
console.log('剪贴板内容:', text);
// 写入剪贴板
await Clipboard.writeText('Hello, Clipboard!');
// 写入图片到剪贴板(支持文件路径、Buffer、base64)
await Clipboard.writeImage(path.join(__dirname, 'test/images/ding_copy.png'));
// 写入文件到剪贴板(粘贴时为文件)
await Clipboard.writeFile(path.join(__dirname, 'rust-rpa.darwin-x64.node'));
// 执行粘贴操作(使用 Cmd/Ctrl+V)
await Clipboard.paste();
// pasteText / pasteImage 会自动恢复剪贴板;pasteFile 不会自动恢复
await Clipboard.pasteText('直接粘贴文本');
await Clipboard.pasteImage('image.png');
await Clipboard.pasteFile('/path/to/file.pdf');图像处理
const fs = require('fs');
const { Monitor, ImageData } = require('rust-rpa');
// 方式 1: 从截图获取图片
const monitors = Monitor.all();
const screenshot = await monitors[0].captureImage();
// 方式 2: 从文件加载图片
const image = await ImageData.fromFile('input.png');
// 方式 3: 从 Buffer 加载图片
const buffer = fs.readFileSync('input.png');
const imageFromBuffer = await ImageData.fromBuffer(buffer);
// 缩放图片
const resized = await image.resize(800, 600);
// 裁剪图片
const cropped = await image.crop(100, 100, 400, 300);
// 转换为灰度图
const grayscale = await image.grayscale();
// 保存图片(自动根据扩展名识别格式)
await resized.toFile('output.png'); // 保存为 PNG
await grayscale.toFile('gray.jpg'); // 保存为 JPEG
await cropped.toFile('cropped.bmp'); // 保存为 BMP
// 转换为 Buffer(用于进一步处理)
const pngBuffer = await image.toPng();
fs.writeFileSync('screenshot.png', pngBuffer);
const jpegBuffer = await image.toJpeg();
fs.writeFileSync('screenshot.jpg', jpegBuffer);
// 获取原始像素数据
const rawData = image.getRawData(); // Buffer,RGBA 格式
console.log(`Data size: ${rawData.length} bytes`);
console.log(`First pixel: R=${rawData[0]}, G=${rawData[1]}, B=${rawData[2]}, A=${rawData[3]}`);
// 获取图片元信息
const meta = image.metadata();
console.log(`Size: ${meta.width}x${meta.height}`);
console.log(`Channels: ${meta.channels}, Data: ${meta.dataSize} bytes`);
// 链式操作
const processed = await ImageData.fromFile('input.png')
.then(img => img.resize(800, 600))
.then(img => img.grayscale())
.then(img => img.crop(100, 100, 400, 300));
await processed.toFile('processed.png');
// 查找图标(模板匹配)
const template = await ImageData.fromFile('icon.png');
// 使用默认参数(threshold=0.8,全图搜索)
const result = await image.findIcon(template);
if (result.found) {
console.log(`找到匹配!`);
console.log(` 相似度: ${(result.score * 100).toFixed(2)}%`);
console.log(` 位置: (${result.x}, ${result.y})`);
} else {
console.log('未找到匹配');
}
// 自定义阈值
const result1 = await image.findIcon(template, { threshold: 0.9 });
// 在指定区域内查找
const result2 = await image.findIcon(template, {
threshold: 0.8,
regions: [
{ x: 0, y: 0, width: 800, height: 600 } // 只在左上角区域搜索
]
});更多示例请查看 examples/ 和 test/ 目录:
# 运行截屏示例
node examples/screenshot-demo.js
# 运行窗口列表示例
node examples/list-windows.js
# 运行剪贴板示例
node examples/clipboard-demo.js
# 运行图像处理示例
node examples/image-processing-demo.js
# 运行测试
npm test平台支持
| 平台 | 架构 | 状态 | 备注 | |----------|-------------|--------|-------| | Windows | x64 | ✅ 支持 | 可能需要管理员权限 | | Windows | x86 (32位) | ✅ 支持 | 可能需要管理员权限 | | macOS | x64 (Intel) | ✅ 支持 | 需要辅助功能权限 | | macOS | ARM64 (Apple Silicon) | ✅ 支持 | 需要辅助功能权限 | | Linux | 全部 | ❌ 不支持 | 当前版本无计划 |
性能
- 窗口枚举通常在 < 100ms 内完成
- 异步操作不会阻塞 Node.js 事件循环
- 最小的内存开销
开发
从源码构建和开发指南请参阅 dev.md。
路线图
- [x] 窗口枚举和信息获取(基于 XCap)
- [x] 鼠标和键盘自动化(Mouse/Keyboard 类)
- [x] 屏幕截图(Monitor/Window 截图)
- [x] 多显示器支持
- [x] 图像格式转换(PNG/JPEG/BMP)
- [x] 图像裁剪功能
- [x] 图像缩放功能(Lanczos3 高质量算法)
- [x] 图像识别(模板匹配)
- [x] 图像灰度化
- [x] 图像数据访问(原始像素、元信息)
- [x] 图像保存到文件
- [x] 剪贴板操作
- [x] 窗口操作(置顶、移动、调整大小、父进程查询)
- [ ] 定位文字findText(text, {prompt: '', model: '', timeout: number})
- [ ] 等待文字、图标、指定时间
- [ ] 点击文字、图标等操作
- [ ] 进程管理
更新日志
0.1.7
BREAKCHANGE
Mouse.scroll滚动单位改为像素:Mouse.scroll(dx, dy)的dx和dy参数现在表示像素值,而非滚动行数- 此前 Windows 和 macOS 使用不同的滚动单位,行为不一致
- 现在统一按像素处理:传入 200 表示尝试滚动约 200 像素
- Windows: 30 像素 ≈ 1 个 WHEEL_DELTA (120)
- macOS: 20 像素 ≈ 1 行滚动
- Windows 平台
Mouse.scroll方向调整:dy正数现在表示向下滚动(与 macOS 保持一致)- 此前 Windows 上正数向上滚动,与 macOS 相反
- 现在双平台方向一致:正数向下,负数向上
0.1.6
BREAKCHANGE
- Windows 平台鼠标坐标统一为逻辑像素:
Mouse.moveTo()和Mouse.position()现在统一使用逻辑像素坐标,与Window.getBounds()等 API 保持一致- 此前 Windows 平台上鼠标操作使用物理像素,在高 DPI 缩放环境下与窗口坐标不一致
- 现在 Windows 平台会自动处理 DPI 缩放,传入和返回的坐标均为逻辑像素
- macOS 平台不受影响(原本就使用逻辑像素)
0.1.5
功能
Mouse.moveTo增强: 新增可选duration参数,支持平滑动画移动duration为移动动画持续时间(秒),默认为 0(瞬间移动)- 当
duration > 0时,鼠标以平滑动画方式移动到目标点,使用 easeInOutCubic 缓动函数 - 动画过程中会触发 mouseMove 事件,网页等应用可正常响应鼠标移动
findIcon参数优化:options参数中的threshold和regions字段均改为可选threshold不传时默认使用 0.8regions中的区域可超出图片边界,会自动裁剪为与图片的交集范围进行查找
- Window.captureImage 选项
from:新增'window'(默认)与'screen'。设为'screen'时先截取窗口所在显示器整屏,再自动裁剪为当前窗口与显示器的交集区域(超出显示器的部分忽略)。 - Window.captureImage 选项
region:新增可选参数,仅截取指定逻辑像素区域{ x, y, width, height },不填则截取全图。 - Window.captureImage 选项
auto:新增可选参数。为true时,在 Windows 存在 DPI 缩放的情况下自动将图像缩放到逻辑像素宽高(与getSize/getBounds一致)。 - CaptureImageOptions 处理顺序:内部先执行
autoSize(缩放到逻辑像素),再执行region(按逻辑像素裁剪),最后应用size(若有)。启用autoSize后,region与逻辑像素坐标一一对应。
0.1.4
BREAKCHANGE
toJson()改为toJSON()windowtoJSON()返回对象中取消bounds- Window 数据一致性修复: 统一
getBounds()和toJSON()、setBounds()中屏幕宽高和窗口大小位置都使用逻辑像素,解决 Windows 环境下坐标不一致问题。
0.1.3
功能
- 新增
Window.getSize()方法: 返回{width, height}对象,提供便捷的窗口尺寸获取方式 - 新增
Window.currentMonitorId()方法: 获取窗口所在显示器的 ID,比currentMonitor()更轻量 Window.toJSON()增强: 现在包含monitorId字段,提供窗口所在显示器的 ID 信息
修复
bringToFront在mac中仅将当前窗口顶置,避免顶置其父窗口- windows缩放后,应用程序大小和屏幕大小使用的逻辑像素逻辑不一致,都改为使用真实逻辑像素,减少后续处理
0.1.2
修复
windows上的.node文件调用失败修复
0.1.1
新增
- CLI 命令行工具:安装后可通过
rpa命令直接使用,支持窗口/鼠标/键盘/剪贴板/截图/显示器/权限等全部功能 - Permission 类:新增
checkAccessibility()和checkScreenCapture(),macOS 自动弹出系统授权对话框,Windows 检查管理员权限 - Window.parentId():获取窗口所属进程的父进程 ID
- Window.bringToFront():将窗口置于最前,改为异步方法返回
Promise<void> - Window.setBounds():设置窗口位置和大小,改为异步方法返回
Promise<void> - Clipboard.pasteText(text):写入文本并粘贴,完成后自动恢复剪贴板原始内容
- Clipboard.pasteImage(source):写入图片并粘贴,完成后自动恢复剪贴板原始内容
- Clipboard.pasteFile(paths):写入文件并粘贴,不会自动恢复剪贴板原始内容
- ImageData.fromBase64(base64):从 base64 字符串或 data URI 创建 ImageData 对象
BreakChange
- Clipboard.writeImage(source):简化接口,不再需要手动传
width/height,支持文件路径、Buffer、base64 字符串、data URI 四种输入方式 - bringWindowToFrontById:已移除公开暴露,改为
bringToFront()实例方法内部调用 - WindowToJson / MonitorToJson:修正 TypeScript 定义中的字段命名为 camelCase(与 napi-rs 实际输出一致)
- Node.js 版本要求:最低版本从 14 提升至 18
许可证
MIT
致谢
构建基于以下项目:
- Rust - Rust 编程语言
- napi-rs - Node.js 的 Rust 绑定
- XCap - 跨平台截图库
- windows-rs - Windows 的 Rust 绑定
- core-foundation-rs - macOS 的 Rust 绑定
