rust-rpa 0.1.4-beta.2

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/README.md ADDED
@@ -0,0 +1,787 @@
1
+ # rust-rpa
2
+
3
+ 基于 Rust 的高性能 Node.js RPA(机器人流程自动化)库。
4
+
5
+ ## 特性
6
+
7
+ - 🚀 **高性能**: 使用 Rust 编写,追求极致速度和效率
8
+ - 🔄 **跨平台**: 支持 Windows 和 macOS
9
+ - 📦 **易于使用**: 简洁的面向对象 API,支持 TypeScript
10
+ - ⚡ **非阻塞**: 异步操作不会阻塞 Node.js 事件循环
11
+ - 🔒 **类型安全**: 完整的 TypeScript 类型定义
12
+ - 🖱️ **输入自动化**: Mouse 和 Keyboard 类,完整的输入控制
13
+ - 🪟 **窗口管理**: 基于 XCap 的窗口枚举和信息获取
14
+ - 🖥️ **多显示器支持**: Monitor 类,支持多显示器设置
15
+ - 📸 **屏幕截图**: 支持全屏和窗口截图,多种图片格式
16
+ - 🔑 **权限管理**: Permission 类,自动检测并弹出系统权限授权对话框
17
+
18
+ ## 安装
19
+
20
+ ```bash
21
+ npm install rust-rpa
22
+ ```
23
+
24
+ **支持的平台:**
25
+ - Windows (x64, x86)
26
+ - macOS (Intel x64, Apple Silicon ARM64)
27
+
28
+ **注意:** 当前版本不支持 Linux。
29
+
30
+ ## 系统要求
31
+
32
+ ### Windows
33
+ - Windows 10 或更高版本 (x64 或 x86)
34
+ - 某些操作可能需要管理员权限
35
+
36
+ ### macOS
37
+ - macOS 10.13 或更高版本 (Intel 或 Apple Silicon)
38
+ - **需要辅助功能权限**(见下文)
39
+
40
+ ## 命令行工具
41
+
42
+ 安装后可通过 `rpa` 命令直接使用,无需编写代码:
43
+
44
+ ```bash
45
+ # 查看帮助
46
+ rpa --help
47
+
48
+ # 检查权限(macOS 自动弹出授权)
49
+ rpa permission --prompt
50
+
51
+ # 列出所有窗口
52
+ rpa window:list
53
+
54
+ # 将窗口置于最前
55
+ rpa window:focus Chrome
56
+
57
+ # 设置窗口位置和大小
58
+ rpa window:bounds Chrome 100 100 800 600
59
+
60
+ # 截取主显示器
61
+ rpa screenshot -o desktop.png
62
+
63
+ # 截取指定窗口
64
+ rpa screenshot --window Chrome -o chrome.png
65
+
66
+ # 鼠标操作
67
+ rpa mouse:position
68
+ rpa mouse:move 500 300
69
+ rpa mouse:click right
70
+ rpa mouse:scroll 0 -3
71
+
72
+ # 键盘操作
73
+ rpa keyboard:type "Hello World"
74
+ rpa keyboard:press enter
75
+ rpa keyboard:press ctrl+c
76
+
77
+ # 剪贴板
78
+ rpa clipboard:read
79
+ rpa clipboard:write "Hello"
80
+ rpa clipboard:paste
81
+
82
+ # 列出所有显示器
83
+ rpa monitor:list
84
+ ```
85
+
86
+ 所有命令支持 `--help` 查看详细用法,如 `rpa screenshot --help`。
87
+
88
+ ## 示例代码
89
+
90
+ 项目提供了丰富的示例代码,位于 `examples/` 目录中:
91
+
92
+ - `permission-demo.js` - 权限检查演示
93
+ - `list-windows.js` - 窗口管理演示
94
+ - `screenshot-demo.js` - 屏幕截图功能演示
95
+ - `clipboard-demo.js` - 剪贴板操作演示
96
+ - `image-processing-demo.js` - 图像处理功能演示
97
+ - `find-icon-demo.js` - 完整的 findIcon 功能演示
98
+ - `find-icon-basic.js` - findIcon 基础用法示例
99
+
100
+ 运行示例:
101
+ ```bash
102
+ node examples/permission-demo.js
103
+ node examples/list-windows.js
104
+ ```
105
+
106
+ ## 使用方法
107
+
108
+ ### JavaScript
109
+
110
+ ```javascript
111
+ const { Window, Monitor, Mouse, Keyboard, Permission } = require('rust-rpa');
112
+
113
+ async function main() {
114
+ try {
115
+ // 检查权限(macOS 上无权限会自动弹出授权对话框)
116
+ Permission.checkAccessibility();
117
+ Permission.checkScreenCapture();
118
+
119
+ // 获取所有窗口
120
+ const windows = Window.all();
121
+ console.log(`Found ${windows.length} windows`);
122
+
123
+ windows.forEach(win => {
124
+ console.log(`${win.appName()}: ${win.title()}`);
125
+ console.log(` PID: ${win.pid()}, Parent PID: ${win.parentId()}`);
126
+ console.log(` Position: (${win.x()}, ${win.y()}), Size: ${win.width()}x${win.height()}`);
127
+ });
128
+
129
+ // 将窗口置于最前
130
+ await windows[0].bringToFront();
131
+
132
+ // 获取所有显示器
133
+ const monitors = Monitor.all();
134
+ const primaryMonitor = monitors.find(m => m.isPrimary());
135
+
136
+ // 截图
137
+ const image = await primaryMonitor.captureImage();
138
+ const pngBuffer = await image.toPng();
139
+
140
+ // 鼠标和键盘控制
141
+ await Mouse.moveTo(100, 100);
142
+ await Mouse.click('left');
143
+ await Keyboard.typeText('Hello World!');
144
+ } catch (error) {
145
+ console.error('Error:', error.message);
146
+ }
147
+ }
148
+
149
+ main();
150
+ ```
151
+
152
+ ### TypeScript
153
+
154
+ ```typescript
155
+ import { Window, Monitor, Mouse, Keyboard } from 'rust-rpa';
156
+
157
+ async function main(): Promise<void> {
158
+ try {
159
+ // 获取所有窗口
160
+ const windows = Window.all();
161
+ console.log(`Found ${windows.length} windows`);
162
+
163
+ windows.forEach((win) => {
164
+ console.log(`${win.appName()}: ${win.title()}`);
165
+ });
166
+
167
+ // 截图
168
+ const monitors = Monitor.all();
169
+ const image = await monitors[0].captureImage();
170
+
171
+ // 输入控制
172
+ await Mouse.moveTo(100, 100);
173
+ await Keyboard.typeText('Hello!');
174
+ } catch (error) {
175
+ console.error('Error:', error);
176
+ }
177
+ }
178
+
179
+ main();
180
+ ```
181
+
182
+ ## API
183
+
184
+ ### Window 类
185
+
186
+ 窗口管理类,基于 XCap 实现。
187
+
188
+ #### 静态方法
189
+
190
+ **`Window.all(): Window[]`**
191
+
192
+ 获取所有可见窗口列表。
193
+
194
+ ```javascript
195
+ const windows = Window.all();
196
+ ```
197
+
198
+ #### 实例方法
199
+
200
+ - `id(): number` - 获取窗口 ID
201
+ - `pid(): number` - 获取窗口进程 ID
202
+ - `parentId(): number` - 获取窗口所属进程的父进程 ID
203
+ - `title(): string` - 获取窗口标题
204
+ - `appName(): string` - 获取应用程序名称
205
+ - `x(): number` - 获取窗口 X 坐标
206
+ - `y(): number` - 获取窗口 Y 坐标
207
+ - `width(): number` - 获取窗口宽度
208
+ - `height(): number` - 获取窗口高度
209
+ - `getSize(): WindowSize` - 获取窗口尺寸(宽度和高度)
210
+ - `isMinimized(): boolean` - 是否最小化
211
+ - `isMaximized(): boolean` - 是否最大化
212
+ - `isFocused(): boolean` - 是否聚焦
213
+ - `bringToFront(): Promise<void>` - 将窗口置于最前(激活并置顶)
214
+ - `currentMonitor(): Monitor` - 获取窗口所在显示器
215
+ - `currentMonitorId(): number` - 获取窗口所在显示器的 ID
216
+ - `getBounds(): WindowBounds` - 获取窗口边界(位置和大小)
217
+ - `setBounds(bounds): Promise<void>` - 设置窗口边界(位置和大小)
218
+ - `toJSON(): WindowToJson` - 转为 JSON 可序列化对象
219
+ - `captureImage(options?: CaptureImageOptions): Promise<ImageData>` - 截取窗口图像;可选 `options.size` 指定目标宽高并自动缩放
220
+
221
+ ### Monitor 类
222
+
223
+ 显示器管理类。
224
+
225
+ #### 静态方法
226
+
227
+ **`Monitor.all(): Monitor[]`**
228
+
229
+ 获取所有显示器列表。
230
+
231
+ ```javascript
232
+ const monitors = Monitor.all();
233
+ const primary = monitors.find(m => m.isPrimary());
234
+ ```
235
+
236
+ #### 实例方法
237
+
238
+ - `id(): number` - 获取显示器 ID
239
+ - `name(): string` - 获取显示器名称
240
+ - `x(): number` - 获取显示器 X 坐标
241
+ - `y(): number` - 获取显示器 Y 坐标
242
+ - `width(): number` - 获取显示器宽度
243
+ - `height(): number` - 获取显示器高度
244
+ - `scaleFactor(): number` - 获取缩放因子
245
+ - `isPrimary(): boolean` - 是否主显示器
246
+ - `captureImage(options?: CaptureImageOptions): Promise<ImageData>` - 截取显示器图像;可选 `options.size` 指定目标宽高并自动缩放
247
+
248
+ ### ImageData 类
249
+
250
+ 图像数据类,用于截图和图像处理操作。
251
+
252
+ #### 静态工厂方法
253
+
254
+ - `ImageData.fromFile(filePath): Promise<ImageData>` - 从文件加载图片
255
+ - `ImageData.fromBuffer(buffer): Promise<ImageData>` - 从 Buffer 解码图片
256
+
257
+ #### 属性
258
+
259
+ - `width: number` - 图像宽度(只读)
260
+ - `height: number` - 图像高度(只读)
261
+
262
+ #### 方法
263
+
264
+ **格式转换与保存**
265
+
266
+ - `toPng(): Promise<Buffer>` - 转换为 PNG 格式
267
+ - `toJpeg(): Promise<Buffer>` - 转换为 JPEG 格式
268
+ - `toBmp(): Promise<Buffer>` - 转换为 BMP 格式
269
+ - `toFile(filePath): Promise<void>` - 保存到文件(根据扩展名自动识别格式:.png/.jpg/.jpeg/.bmp)
270
+
271
+ **图像处理**
272
+
273
+ - `crop(x, y, width, height): Promise<ImageData>` - 裁剪图像
274
+ - `resize(width, height): Promise<ImageData>` - 缩放图像(使用 Lanczos3 高质量算法)
275
+ - `grayscale(): Promise<ImageData>` - 转换为灰度图(使用标准加权算法:0.299R + 0.587G + 0.114B)
276
+ - `findIcon(template, options?): Promise<MatchResult>` - 查找模板图标(模板匹配)
277
+
278
+ **数据访问**
279
+
280
+ - `getRawData(): Buffer` - 获取原始像素数据(RGBA 格式,每像素 4 字节)
281
+ - `metadata(): ImageMetadata` - 获取图片元信息
282
+
283
+ **findIcon 参数:**
284
+ - `template: ImageData` - 模板图片(要查找的图标)
285
+ - `options: MatchOptions` - 可选的匹配选项对象
286
+ - `threshold: number` - 匹配阈值 (0.0-1.0),默认 0.8
287
+ - `regions: Array<{x, y, width, height}>` - 可选的搜索区域列表,默认为空(全图搜索)
288
+
289
+ **MatchOptions 说明:**
290
+
291
+ 1. **threshold(匹配阈值)**
292
+ - 取值范围:0.0 - 1.0
293
+ - 默认值:0.8
294
+ - 说明:相似度分数必须大于此阈值才算匹配成功
295
+ - 建议:0.8 是个好的起点,根据实际效果调整
296
+
297
+ 2. **regions(搜索区域)**
298
+ - 格式:`[{x: number, y: number, width: number, height: number}]`
299
+ - 默认:空数组(全图搜索)
300
+ - 说明:限定在指定区域内搜索,可提高效率和准确性
301
+ - 示例:`[{x: 0, y: 0, width: 100, height: 100}]`
302
+
303
+ **使用建议:**
304
+ - 大多数情况使用默认参数即可:`findIcon(template)`
305
+ - 需要调整灵敏度时指定阈值:`findIcon(template, { threshold: 0.9 })`
306
+ - 在特定区域查找可提高性能:`findIcon(template, { threshold: 0.8, regions: [...] })`
307
+
308
+ **MatchResult 类型:**
309
+ ```typescript
310
+ interface MatchResult {
311
+ found: boolean; // 是否找到匹配
312
+ score: number; // 相似度分数 (0.0-1.0)
313
+ x: number; // 匹配位置 x 坐标
314
+ y: number; // 匹配位置 y 坐标
315
+ width: number; // 模板宽度
316
+ height: number; // 模板高度
317
+ }
318
+ ```
319
+
320
+ **ImageMetadata 类型:**
321
+ ```typescript
322
+ interface ImageMetadata {
323
+ width: number; // 图片宽度
324
+ height: number; // 图片高度
325
+ channels: number; // 通道数(固定为 4,RGBA)
326
+ bytesPerPixel: number; // 每像素字节数(固定为 4)
327
+ dataSize: number; // 数据总字节数
328
+ }
329
+ ```
330
+
331
+ **captureImage 选项(Monitor / Window):**
332
+ ```typescript
333
+ interface CaptureImageOptions {
334
+ size?: { width: number; height: number }; // 可选,截取后缩放到指定宽高
335
+ }
336
+ ```
337
+
338
+ ### Mouse 类
339
+
340
+ 鼠标控制类,所有方法都是静态的。
341
+
342
+ #### 静态方法
343
+
344
+ - `Mouse.moveTo(x, y): Promise<void>` - 移动鼠标到指定坐标
345
+ - `Mouse.click(button?): Promise<void>` - 点击鼠标。**button** 可选,可用枚举 `MouseButton.Left` / `MouseButton.Right` 或字符串 `'left'` / `'right'` 等,默认左键
346
+ - `Mouse.doubleClick(button?): Promise<void>` - 双击鼠标,button 同上
347
+ - `Mouse.down(button?): Promise<void>` - 按下鼠标按钮,button 同上
348
+ - `Mouse.up(button?): Promise<void>` - 释放鼠标按钮,button 同上
349
+ - **MouseButton** 枚举:`MouseButton.Left` | `Right` | `Middle` | `Back` | `Forward`,用于上述 button 参数
350
+ - `Mouse.scroll(deltaX?, deltaY?): Promise<void>` - 滚动鼠标滚轮。**方向**:`deltaY` 正数向上、负数向下;`deltaX` 正数向右、负数向左。如 `scroll(0, 3)` 向上滚、`scroll(0, -3)` 向下滚
351
+ - `Mouse.position(): Promise<{ x: number, y: number }>` - 获取当前鼠标位置
352
+
353
+ ### Keyboard 类
354
+
355
+ 键盘控制类,所有方法都是静态的。
356
+
357
+ #### 静态方法
358
+
359
+ - `Keyboard.typeText(text): Promise<void>` - 输入文本
360
+ - `Keyboard.click(key): Promise<void>` - 按下并释放按键
361
+ - `Keyboard.down(key): Promise<void>` - 按下按键
362
+ - `Keyboard.up(key): Promise<void>` - 释放按键
363
+ - `Keyboard.sequence(keys): Promise<void>` - 按键序列(如 `[Key.Ctrl, Key.C]`)
364
+
365
+ **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 等。
366
+
367
+ ### Clipboard 类
368
+
369
+ 剪贴板控制类,所有方法都是静态的。
370
+
371
+ #### 静态方法
372
+
373
+ - `Clipboard.readText(): Promise<string>` - 读取剪贴板文本内容
374
+ - `Clipboard.writeText(text): Promise<void>` - 将文本写入剪贴板
375
+ - `Clipboard.writeImage(source): Promise<void>` - 将图片写入剪贴板,`source` 支持文件路径(string)、base64 字符串、data URI 或 Buffer
376
+ - `Clipboard.writeFile(paths): Promise<void>` - 将文件路径写入剪贴板,粘贴时在资源管理器/访达中为文件(`paths` 可为单个路径字符串或路径数组)
377
+ - `Clipboard.paste(): Promise<void>` - 执行粘贴操作(使用 Cmd/Ctrl+V 快捷键)
378
+ - `Clipboard.pasteText(text): Promise<void>` - 将文本写入剪贴板并粘贴,完成后自动恢复剪贴板原始内容
379
+ - `Clipboard.pasteImage(source): Promise<void>` - 将图片写入剪贴板并粘贴,完成后自动恢复剪贴板原始内容(`source` 同 `writeImage`)
380
+ - `Clipboard.pasteFile(paths): Promise<void>` - 将文件路径写入剪贴板并粘贴,不会自动恢复剪贴板原始内容
381
+
382
+ ### Permission 类
383
+
384
+ 权限检查工具类,所有方法都是静态的。
385
+
386
+ #### 静态方法
387
+
388
+ - `Permission.checkAccessibility(prompt?): boolean` - 检查辅助功能权限(鼠标、键盘、窗口操作等需要此权限)
389
+ - `Permission.checkScreenCapture(prompt?): boolean` - 检查屏幕录制权限(截图功能需要此权限)
390
+
391
+ **参数说明:**
392
+ - `prompt`(可选,默认 `true`):无权限时是否自动弹出系统授权对话框(仅 macOS 生效)
393
+
394
+ **平台行为差异:**
395
+
396
+ | 平台 | checkAccessibility | checkScreenCapture |
397
+ |------|--------------------|--------------------|
398
+ | macOS | 调用 `AXIsProcessTrustedWithOptions`,prompt=true 时弹出辅助功能授权对话框 | 调用 `CGRequestScreenCaptureAccess`,prompt=true 时弹出屏幕录制授权对话框 |
399
+ | Windows | 检查是否以管理员身份运行(管理员即有权限) | 检查是否以管理员身份运行 |
400
+
401
+ ```javascript
402
+ const { Permission } = require('rust-rpa');
403
+
404
+ // 检查并弹出授权(推荐在应用启动时调用)
405
+ const hasAccessibility = Permission.checkAccessibility();
406
+ const hasScreenCapture = Permission.checkScreenCapture();
407
+
408
+ console.log('辅助功能权限:', hasAccessibility);
409
+ console.log('屏幕录制权限:', hasScreenCapture);
410
+
411
+ // 仅检查,不弹窗
412
+ const check = Permission.checkAccessibility(false);
413
+ ```
414
+
415
+ ## macOS 权限要求
416
+
417
+ ### 辅助功能权限(输入控制)
418
+
419
+ 在 macOS 上使用鼠标和键盘控制功能时,需要授予辅助功能权限:
420
+
421
+ 1. 打开 **系统偏好设置**
422
+ 2. 进入 **安全性与隐私** > **隐私** > **辅助功能**
423
+ 3. 点击锁图标进行更改
424
+ 4. 将您的终端应用程序或 IDE 添加到列表中
425
+ 5. 重启应用程序
426
+
427
+ ### 屏幕录制权限(截图功能)
428
+
429
+ 使用截图功能时,需要授予屏幕录制权限:
430
+
431
+ 1. 打开 **系统偏好设置**
432
+ 2. 进入 **安全性与隐私** > **隐私** > **屏幕录制**
433
+ 3. 点击锁图标进行更改
434
+ 4. 勾选您的终端应用程序或 IDE
435
+ 5. 重启应用程序
436
+
437
+ ## 示例
438
+
439
+ ### 窗口管理
440
+
441
+ ```javascript
442
+ const { Window } = require('rust-rpa');
443
+
444
+ // 获取所有窗口
445
+ const windows = Window.all();
446
+ console.log(`Found ${windows.length} windows`);
447
+
448
+ // 遍历窗口
449
+ windows.forEach(win => {
450
+ console.log(`${win.appName()}: ${win.title()}`);
451
+ console.log(` PID: ${win.pid()}, Parent PID: ${win.parentId()}`);
452
+ console.log(` Position: (${win.x()}, ${win.y()})`);
453
+ console.log(` Size: ${win.width()}x${win.height()}`);
454
+ console.log(` Minimized: ${win.isMinimized()}, Focused: ${win.isFocused()}`);
455
+ });
456
+
457
+ // 将窗口置于最前
458
+ const target = windows.find(w => w.appName().includes('Chrome'));
459
+ if (target) {
460
+ await target.bringToFront();
461
+ }
462
+
463
+ // 获取/设置窗口边界
464
+ const bounds = windows[0].getBounds();
465
+ console.log(`Bounds: ${JSON.stringify(bounds)}`);
466
+ await windows[0].setBounds({ x: 100, y: 100, width: 800, height: 600 });
467
+
468
+ // 获取窗口所在显示器
469
+ const monitor = windows[0].currentMonitor();
470
+ console.log(`Window is on monitor: ${monitor.name()}`);
471
+
472
+ // 转为 JSON 对象
473
+ console.log(JSON.stringify(windows[0].toJSON(), null, 2));
474
+ ```
475
+
476
+ ### 显示器管理
477
+
478
+ ```javascript
479
+ const { Monitor } = require('rust-rpa');
480
+
481
+ // 获取所有显示器
482
+ const monitors = Monitor.all();
483
+ monitors.forEach(m => {
484
+ console.log(`Monitor: ${m.name()}`);
485
+ console.log(` Resolution: ${m.width()}x${m.height()}`);
486
+ console.log(` Scale: ${m.scaleFactor()}x`);
487
+ console.log(` Primary: ${m.isPrimary()}`);
488
+ });
489
+
490
+ // 获取主显示器
491
+ const primary = monitors.find(m => m.isPrimary());
492
+ ```
493
+
494
+ ### 屏幕截图
495
+
496
+ ```javascript
497
+ const fs = require('fs');
498
+ const { Monitor, Window, ImageData } = require('rust-rpa');
499
+
500
+ // 截取显示器
501
+ const monitors = Monitor.all();
502
+ const image = await monitors[0].captureImage();
503
+
504
+ // 截取时指定目标尺寸(自动缩放,便于后续扩展其他选项)
505
+ const imageResized = await monitors[0].captureImage({ size: { width: 800, height: 600 } });
506
+
507
+ // 保存为不同格式(方式 1:转换为 Buffer)
508
+ const pngBuffer = await image.toPng();
509
+ fs.writeFileSync('screenshot.png', pngBuffer);
510
+
511
+ const jpegBuffer = await image.toJpeg();
512
+ fs.writeFileSync('screenshot.jpg', jpegBuffer);
513
+
514
+ // 保存为不同格式(方式 2:直接保存文件)
515
+ await image.toFile('screenshot.png');
516
+ await image.toFile('screenshot.jpg');
517
+ await image.toFile('screenshot.bmp');
518
+
519
+ // 裁剪图像
520
+ const cropped = await image.crop(0, 0, 800, 600);
521
+ await cropped.toFile('cropped.png');
522
+
523
+ // 截取窗口
524
+ const windows = Window.all();
525
+ if (windows.length > 0) {
526
+ const windowImage = await windows[0].captureImage();
527
+ await windowImage.toFile('window.png');
528
+ }
529
+ ```
530
+
531
+ **截图选项**:`captureImage(options?)` 接受可选对象 `CaptureImageOptions`,其中 `size?: { width: number, height: number }` 表示截取后缩放到指定宽高,不传则返回原始尺寸。
532
+
533
+ ### 输入自动化
534
+
535
+ ```javascript
536
+ const { Mouse, MouseButton, Keyboard, Key } = require('rust-rpa');
537
+
538
+ // 鼠标操作
539
+ await Mouse.moveTo(100, 200); // 移动鼠标
540
+ await Mouse.click(MouseButton.Left); // 左键点击
541
+ await Mouse.click(MouseButton.Right); // 右键点击
542
+ await Mouse.doubleClick(); // 双击
543
+ await Mouse.scroll(0, 3); // 向上滚动(正值 = 远离用户)
544
+ await Mouse.scroll(0, -3); // 向下滚动(负值 = 朝向用户)
545
+
546
+ // 获取鼠标位置
547
+ const { x, y } = await Mouse.position();
548
+ console.log(`Mouse at (${x}, ${y})`);
549
+
550
+ // 键盘操作(key 可用 Key 枚举或字符串)
551
+ await Keyboard.typeText('Hello World!'); // 输入文本
552
+ await Keyboard.click(Key.Enter); // 按 Enter
553
+
554
+ // 组合键
555
+ await Keyboard.sequence([Key.Ctrl, Key.A]); // Ctrl+A (全选)
556
+ await Keyboard.sequence([Key.Ctrl, Key.C]); // Ctrl+C (复制)
557
+ await Keyboard.sequence([Key.Meta, Key.V]); // Cmd+V (粘贴,macOS)
558
+
559
+ // 按键按下和释放
560
+ await Keyboard.down(Key.Shift);
561
+ await Keyboard.click(Key.A);
562
+ await Keyboard.up(Key.Shift);
563
+ ```
564
+
565
+ ### 剪贴板操作
566
+
567
+ ```javascript
568
+ const path = require('path');
569
+ const { Clipboard, ImageData } = require('rust-rpa');
570
+
571
+ // 读取剪贴板
572
+ const text = await Clipboard.readText();
573
+ console.log('剪贴板内容:', text);
574
+
575
+ // 写入剪贴板
576
+ await Clipboard.writeText('Hello, Clipboard!');
577
+
578
+ // 写入图片到剪贴板(支持文件路径、Buffer、base64)
579
+ await Clipboard.writeImage(path.join(__dirname, 'test/images/ding_copy.png'));
580
+
581
+ // 写入文件到剪贴板(粘贴时为文件)
582
+ await Clipboard.writeFile(path.join(__dirname, 'rust-rpa.darwin-x64.node'));
583
+
584
+ // 执行粘贴操作(使用 Cmd/Ctrl+V)
585
+ await Clipboard.paste();
586
+
587
+ // pasteText / pasteImage 会自动恢复剪贴板;pasteFile 不会自动恢复
588
+ await Clipboard.pasteText('直接粘贴文本');
589
+ await Clipboard.pasteImage('image.png');
590
+ await Clipboard.pasteFile('/path/to/file.pdf');
591
+ ```
592
+
593
+ ### 图像处理
594
+
595
+ ```javascript
596
+ const fs = require('fs');
597
+ const { Monitor, ImageData } = require('rust-rpa');
598
+
599
+ // 方式 1: 从截图获取图片
600
+ const monitors = Monitor.all();
601
+ const screenshot = await monitors[0].captureImage();
602
+
603
+ // 方式 2: 从文件加载图片
604
+ const image = await ImageData.fromFile('input.png');
605
+
606
+ // 方式 3: 从 Buffer 加载图片
607
+ const buffer = fs.readFileSync('input.png');
608
+ const imageFromBuffer = await ImageData.fromBuffer(buffer);
609
+
610
+ // 缩放图片
611
+ const resized = await image.resize(800, 600);
612
+
613
+ // 裁剪图片
614
+ const cropped = await image.crop(100, 100, 400, 300);
615
+
616
+ // 转换为灰度图
617
+ const grayscale = await image.grayscale();
618
+
619
+ // 保存图片(自动根据扩展名识别格式)
620
+ await resized.toFile('output.png'); // 保存为 PNG
621
+ await grayscale.toFile('gray.jpg'); // 保存为 JPEG
622
+ await cropped.toFile('cropped.bmp'); // 保存为 BMP
623
+
624
+ // 转换为 Buffer(用于进一步处理)
625
+ const pngBuffer = await image.toPng();
626
+ fs.writeFileSync('screenshot.png', pngBuffer);
627
+
628
+ const jpegBuffer = await image.toJpeg();
629
+ fs.writeFileSync('screenshot.jpg', jpegBuffer);
630
+
631
+ // 获取原始像素数据
632
+ const rawData = image.getRawData(); // Buffer,RGBA 格式
633
+ console.log(`Data size: ${rawData.length} bytes`);
634
+ console.log(`First pixel: R=${rawData[0]}, G=${rawData[1]}, B=${rawData[2]}, A=${rawData[3]}`);
635
+
636
+ // 获取图片元信息
637
+ const meta = image.metadata();
638
+ console.log(`Size: ${meta.width}x${meta.height}`);
639
+ console.log(`Channels: ${meta.channels}, Data: ${meta.dataSize} bytes`);
640
+
641
+ // 链式操作
642
+ const processed = await ImageData.fromFile('input.png')
643
+ .then(img => img.resize(800, 600))
644
+ .then(img => img.grayscale())
645
+ .then(img => img.crop(100, 100, 400, 300));
646
+ await processed.toFile('processed.png');
647
+
648
+ // 查找图标(模板匹配)
649
+ const template = await ImageData.fromFile('icon.png');
650
+
651
+ // 使用默认参数(threshold=0.8,全图搜索)
652
+ const result = await image.findIcon(template);
653
+
654
+ if (result.found) {
655
+ console.log(`找到匹配!`);
656
+ console.log(` 相似度: ${(result.score * 100).toFixed(2)}%`);
657
+ console.log(` 位置: (${result.x}, ${result.y})`);
658
+ } else {
659
+ console.log('未找到匹配');
660
+ }
661
+
662
+ // 自定义阈值
663
+ const result1 = await image.findIcon(template, { threshold: 0.9 });
664
+
665
+ // 在指定区域内查找
666
+ const result2 = await image.findIcon(template, {
667
+ threshold: 0.8,
668
+ regions: [
669
+ { x: 0, y: 0, width: 800, height: 600 } // 只在左上角区域搜索
670
+ ]
671
+ });
672
+ ```
673
+
674
+ 更多示例请查看 `examples/` 和 `test/` 目录:
675
+
676
+ ```bash
677
+ # 运行截屏示例
678
+ node examples/screenshot-demo.js
679
+
680
+ # 运行窗口列表示例
681
+ node examples/list-windows.js
682
+
683
+ # 运行剪贴板示例
684
+ node examples/clipboard-demo.js
685
+
686
+ # 运行图像处理示例
687
+ node examples/image-processing-demo.js
688
+
689
+ # 运行测试
690
+ npm test
691
+ ```
692
+
693
+ ## 平台支持
694
+
695
+ | 平台 | 架构 | 状态 | 备注 |
696
+ |----------|-------------|--------|-------|
697
+ | Windows | x64 | ✅ 支持 | 可能需要管理员权限 |
698
+ | Windows | x86 (32位) | ✅ 支持 | 可能需要管理员权限 |
699
+ | macOS | x64 (Intel) | ✅ 支持 | 需要辅助功能权限 |
700
+ | macOS | ARM64 (Apple Silicon) | ✅ 支持 | 需要辅助功能权限 |
701
+ | Linux | 全部 | ❌ 不支持 | 当前版本无计划 |
702
+
703
+ ## 性能
704
+
705
+ - 窗口枚举通常在 < 100ms 内完成
706
+ - 异步操作不会阻塞 Node.js 事件循环
707
+ - 最小的内存开销
708
+
709
+ ## 开发
710
+
711
+ 从源码构建和开发指南请参阅 [dev.md](./dev.md)。
712
+
713
+ ## 路线图
714
+
715
+ - [x] 窗口枚举和信息获取(基于 XCap)
716
+ - [x] 鼠标和键盘自动化(Mouse/Keyboard 类)
717
+ - [x] 屏幕截图(Monitor/Window 截图)
718
+ - [x] 多显示器支持
719
+ - [x] 图像格式转换(PNG/JPEG/BMP)
720
+ - [x] 图像裁剪功能
721
+ - [x] 图像缩放功能(Lanczos3 高质量算法)
722
+ - [x] 图像识别(模板匹配)
723
+ - [x] 图像灰度化
724
+ - [x] 图像数据访问(原始像素、元信息)
725
+ - [x] 图像保存到文件
726
+ - [x] 剪贴板操作
727
+ - [x] 窗口操作(置顶、移动、调整大小、父进程查询)
728
+ - [ ] 进程管理
729
+
730
+ ## 更新日志
731
+
732
+ ### 0.1.4
733
+
734
+ #### BREAKCHANGE
735
+ - `toJson()`改为`toJSON()`
736
+ - `window` `toJSON()` 返回对象中取消`bounds`
737
+
738
+
739
+ ### 0.1.3
740
+ #### 功能
741
+ - **新增 `Window.getSize()` 方法**: 返回 `{width, height}` 对象,提供便捷的窗口尺寸获取方式
742
+ - **新增 `Window.currentMonitorId()` 方法**: 获取窗口所在显示器的 ID,比 `currentMonitor()` 更轻量
743
+ - **`Window.toJSON()` 增强**: 现在包含 `monitorId` 字段,提供窗口所在显示器的 ID 信息
744
+
745
+ #### 修复
746
+ - **Window 数据一致性修复**: 统一 `getBounds()` 和 `toJSON()`、`setBounds()` 使用 XCap 数据源,确保与截图功能使用相同的坐标系统,屏幕宽高和窗口大小位置都使用逻辑像素,解决 Windows 环境下坐标不一致问题。
747
+ - `bringToFront`在mac中仅将当前窗口顶置,避免顶置其父窗口
748
+ - windows缩放后,应用程序大小和屏幕大小使用的逻辑像素逻辑不一致,都改为使用真实逻辑像素,减少后续处理
749
+
750
+ ### 0.1.2
751
+
752
+ #### 修复
753
+ - `windows`上的`.node`文件调用失败修复
754
+
755
+ ### 0.1.1
756
+
757
+ #### 新增
758
+
759
+ - **CLI 命令行工具**:安装后可通过 `rpa` 命令直接使用,支持窗口/鼠标/键盘/剪贴板/截图/显示器/权限等全部功能
760
+ - **Permission 类**:新增 `checkAccessibility()` 和 `checkScreenCapture()`,macOS 自动弹出系统授权对话框,Windows 检查管理员权限
761
+ - **Window.parentId()**:获取窗口所属进程的父进程 ID
762
+ - **Window.bringToFront()**:将窗口置于最前,改为异步方法返回 `Promise<void>`
763
+ - **Window.setBounds()**:设置窗口位置和大小,改为异步方法返回 `Promise<void>`
764
+ - **Clipboard.pasteText(text)**:写入文本并粘贴,完成后自动恢复剪贴板原始内容
765
+ - **Clipboard.pasteImage(source)**:写入图片并粘贴,完成后自动恢复剪贴板原始内容
766
+ - **Clipboard.pasteFile(paths)**:写入文件并粘贴,不会自动恢复剪贴板原始内容
767
+ - **ImageData.fromBase64(base64)**:从 base64 字符串或 data URI 创建 ImageData 对象
768
+
769
+ #### BreakChange
770
+
771
+ - **Clipboard.writeImage(source)**:简化接口,不再需要手动传 `width`/`height`,支持文件路径、Buffer、base64 字符串、data URI 四种输入方式
772
+ - **bringWindowToFrontById**:已移除公开暴露,改为 `bringToFront()` 实例方法内部调用
773
+ - **WindowToJson / MonitorToJson**:修正 TypeScript 定义中的字段命名为 camelCase(与 napi-rs 实际输出一致)
774
+ - **Node.js 版本要求**:最低版本从 14 提升至 18
775
+
776
+ ## 许可证
777
+
778
+ MIT
779
+
780
+ ## 致谢
781
+
782
+ 构建基于以下项目:
783
+ - [Rust](https://www.rust-lang.org/) - Rust 编程语言
784
+ - [napi-rs](https://napi.rs/) - Node.js 的 Rust 绑定
785
+ - [XCap](https://github.com/nashaofu/xcap) - 跨平台截图库
786
+ - [windows-rs](https://github.com/microsoft/windows-rs) - Windows 的 Rust 绑定
787
+ - [core-foundation-rs](https://github.com/servo/core-foundation-rs) - macOS 的 Rust 绑定