@aspiresys/visor 1.0.0
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/dist/app.d.ts +45 -0
- package/dist/app.js +102 -0
- package/dist/config.d.ts +5 -0
- package/dist/config.js +8 -0
- package/dist/index.d.ts +875 -0
- package/dist/index.js +1098 -0
- package/dist/logger.d.ts +1 -0
- package/dist/logger.js +9 -0
- package/dist/matcher.d.ts +4 -0
- package/dist/matcher.js +69 -0
- package/dist/mouse.d.ts +3 -0
- package/dist/mouse.js +24 -0
- package/dist/ocr.d.ts +23 -0
- package/dist/ocr.js +83 -0
- package/dist/path.d.ts +1 -0
- package/dist/path.js +15 -0
- package/dist/screen.d.ts +2 -0
- package/dist/screen.js +28 -0
- package/dist/src/index.d.ts +6 -0
- package/dist/src/index.js +39 -0
- package/dist/src/matcher.d.ts +3 -0
- package/dist/src/matcher.js +33 -0
- package/dist/src/mouse.d.ts +2 -0
- package/dist/src/mouse.js +18 -0
- package/dist/src/screen.d.ts +1 -0
- package/dist/src/screen.js +20 -0
- package/dist/src/types.d.ts +7 -0
- package/dist/src/types.js +2 -0
- package/dist/text.d.ts +13 -0
- package/dist/text.js +51 -0
- package/dist/types.d.ts +7 -0
- package/dist/types.js +2 -0
- package/package.json +41 -0
- package/readme.md +566 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1098 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.visor = exports.Visor = void 0;
|
|
4
|
+
const screen_1 = require("./screen");
|
|
5
|
+
const matcher_1 = require("./matcher");
|
|
6
|
+
const mouse_1 = require("./mouse");
|
|
7
|
+
const text_1 = require("./text");
|
|
8
|
+
const ocr_1 = require("./ocr");
|
|
9
|
+
const config_1 = require("./config");
|
|
10
|
+
const app_1 = require("./app");
|
|
11
|
+
const nut_js_1 = require("@nut-tree-fork/nut-js");
|
|
12
|
+
const path_1 = require("./path");
|
|
13
|
+
class Visor {
|
|
14
|
+
constructor() {
|
|
15
|
+
this.mouse = nut_js_1.mouse;
|
|
16
|
+
this.keyboard = nut_js_1.keyboard;
|
|
17
|
+
this.Key = nut_js_1.Key;
|
|
18
|
+
this.Button = nut_js_1.Button;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Finds an image on screen using OpenCV
|
|
22
|
+
* and performs a left mouse click
|
|
23
|
+
* on the matched region.
|
|
24
|
+
*
|
|
25
|
+
* @param image Optional image filename or path.
|
|
26
|
+
*
|
|
27
|
+
* @param confidence Match confidence threshold.
|
|
28
|
+
*
|
|
29
|
+
* Accepted range:
|
|
30
|
+
* 0.0 to 1.0
|
|
31
|
+
*
|
|
32
|
+
* Default:
|
|
33
|
+
* 0.8
|
|
34
|
+
*
|
|
35
|
+
* Recommended values:
|
|
36
|
+
* - 0.7 for dynamic UI
|
|
37
|
+
* - 0.8 for standard usage
|
|
38
|
+
* - 0.9+ for strict matching
|
|
39
|
+
*
|
|
40
|
+
* Lower confidence increases
|
|
41
|
+
* flexibility but may increase
|
|
42
|
+
* false positives.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* await visor.click("save.png");
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* await visor.click("./images/save.png", 0.9);
|
|
49
|
+
*/
|
|
50
|
+
async click(image, confidence = 0.8) {
|
|
51
|
+
if (!image) {
|
|
52
|
+
await this.mouse.click(this.Button.LEFT);
|
|
53
|
+
console.log("[MOUSE] Left click completed");
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const region = await this.find(image, confidence);
|
|
57
|
+
if (!region) {
|
|
58
|
+
throw new Error(`Image not found: ${image}`);
|
|
59
|
+
}
|
|
60
|
+
console.log("[CV] Match found:", region);
|
|
61
|
+
await (0, mouse_1.clickRegion)(region);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Performs a right mouse click.
|
|
65
|
+
*
|
|
66
|
+
* If an image is provided, the framework
|
|
67
|
+
* first finds the image on screen and
|
|
68
|
+
* moves the mouse to the matched region
|
|
69
|
+
* before performing the click.
|
|
70
|
+
*
|
|
71
|
+
* @param image Image filename or path.
|
|
72
|
+
* @param confidence Match confidence threshold.
|
|
73
|
+
*
|
|
74
|
+
* Accepted range:
|
|
75
|
+
* 0.0 to 1.0
|
|
76
|
+
*
|
|
77
|
+
* Default:
|
|
78
|
+
* 0.8
|
|
79
|
+
* @throws Error if image is not found.
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* await visor.rightClick("file.png");
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* await visor.rightClick();
|
|
86
|
+
*/
|
|
87
|
+
async rightClick(image, confidence = 0.8) {
|
|
88
|
+
if (image) {
|
|
89
|
+
const region = await this.find(image, confidence);
|
|
90
|
+
if (!region) {
|
|
91
|
+
throw new Error(`Image not found: ${image}`);
|
|
92
|
+
}
|
|
93
|
+
await (0, mouse_1.moveToRegion)(region);
|
|
94
|
+
}
|
|
95
|
+
await this.mouse.click(this.Button.RIGHT);
|
|
96
|
+
console.log("[MOUSE] Right click completed");
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Performs a double left mouse click.
|
|
100
|
+
*
|
|
101
|
+
* If an image is provided, the framework
|
|
102
|
+
* first finds the image on screen and
|
|
103
|
+
* moves the mouse to the matched region
|
|
104
|
+
* before performing the click.
|
|
105
|
+
*
|
|
106
|
+
* @param image Image filename or path.
|
|
107
|
+
* @param confidence Match confidence threshold.
|
|
108
|
+
*
|
|
109
|
+
* Accepted range:
|
|
110
|
+
* 0.0 to 1.0
|
|
111
|
+
*
|
|
112
|
+
* Default:
|
|
113
|
+
* 0.8
|
|
114
|
+
*
|
|
115
|
+
* @throws Error if image is not found.
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* await visor.doubleClick("folder.png");
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* await visor.doubleClick();
|
|
122
|
+
*/
|
|
123
|
+
async doubleClick(image, confidence = 0.8) {
|
|
124
|
+
if (image) {
|
|
125
|
+
const region = await this.find(image, confidence);
|
|
126
|
+
if (!region) {
|
|
127
|
+
throw new Error(`Image not found: ${image}`);
|
|
128
|
+
}
|
|
129
|
+
await (0, mouse_1.moveToRegion)(region);
|
|
130
|
+
}
|
|
131
|
+
await this.mouse.doubleClick(this.Button.LEFT);
|
|
132
|
+
console.log("[MOUSE] Double click completed");
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Finds the best matching image on screen
|
|
136
|
+
* using OpenCV template matching.
|
|
137
|
+
*
|
|
138
|
+
* @param image Image filename or path.
|
|
139
|
+
*
|
|
140
|
+
* @param confidence Match confidence threshold.
|
|
141
|
+
*
|
|
142
|
+
* Accepted range:
|
|
143
|
+
* 0.0 to 1.0
|
|
144
|
+
*
|
|
145
|
+
* Default:
|
|
146
|
+
* 0.8
|
|
147
|
+
*
|
|
148
|
+
* Recommended values:
|
|
149
|
+
* - 0.7 for dynamic UI
|
|
150
|
+
* - 0.8 for standard usage
|
|
151
|
+
* - 0.9+ for strict matching
|
|
152
|
+
*
|
|
153
|
+
* Higher confidence improves
|
|
154
|
+
* matching accuracy but may fail
|
|
155
|
+
* on scaled or slightly modified UI.
|
|
156
|
+
*
|
|
157
|
+
* @returns Best matched region or null.
|
|
158
|
+
*
|
|
159
|
+
* @example
|
|
160
|
+
* const region =
|
|
161
|
+
* await visor.find("save.png");
|
|
162
|
+
*/
|
|
163
|
+
async find(image, confidence = 0.8) {
|
|
164
|
+
const screen = await (0, screen_1.captureScreen)();
|
|
165
|
+
const template = (0, matcher_1.loadTemplate)((0, path_1.resolveImagePath)(image));
|
|
166
|
+
try {
|
|
167
|
+
return (0, matcher_1.matchTemplate)(screen, template, confidence);
|
|
168
|
+
}
|
|
169
|
+
finally {
|
|
170
|
+
screen.delete();
|
|
171
|
+
template.delete();
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Checks whether an image exists
|
|
176
|
+
* on the current screen.
|
|
177
|
+
*
|
|
178
|
+
* @param image Image filename or path.
|
|
179
|
+
* @param confidence Match confidence threshold.
|
|
180
|
+
*
|
|
181
|
+
* Accepted range:
|
|
182
|
+
* 0.0 to 1.0
|
|
183
|
+
*
|
|
184
|
+
* Default:
|
|
185
|
+
* 0.8
|
|
186
|
+
*
|
|
187
|
+
* @returns True if image exists.
|
|
188
|
+
*
|
|
189
|
+
* @example
|
|
190
|
+
* const exists =
|
|
191
|
+
* await visor.exists("login.png");
|
|
192
|
+
*/
|
|
193
|
+
async exists(image, confidence = 0.8) {
|
|
194
|
+
return (await this.find(image, confidence)) !== null;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Finds all matching occurrences of an image
|
|
198
|
+
* on the current screen using OpenCV.
|
|
199
|
+
*
|
|
200
|
+
* @param image Image filename or path.
|
|
201
|
+
* @param confidence Match confidence threshold.
|
|
202
|
+
*
|
|
203
|
+
* Accepted range:
|
|
204
|
+
* 0.0 to 1.0
|
|
205
|
+
*
|
|
206
|
+
* Default:
|
|
207
|
+
* 0.8
|
|
208
|
+
*
|
|
209
|
+
* Lower confidence may return
|
|
210
|
+
* more false-positive matches.
|
|
211
|
+
*
|
|
212
|
+
* @returns Array of matched regions.
|
|
213
|
+
*
|
|
214
|
+
* @example
|
|
215
|
+
* const matches =
|
|
216
|
+
* await visor.findAll("icon.png");
|
|
217
|
+
*/
|
|
218
|
+
async findAll(image, confidence = 0.8) {
|
|
219
|
+
const screen = await (0, screen_1.captureScreen)();
|
|
220
|
+
const template = (0, matcher_1.loadTemplate)((0, path_1.resolveImagePath)(image));
|
|
221
|
+
try {
|
|
222
|
+
return (0, matcher_1.findAllMatches)(screen, template, confidence);
|
|
223
|
+
}
|
|
224
|
+
finally {
|
|
225
|
+
screen.delete();
|
|
226
|
+
template.delete();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Finds all image matches and prints
|
|
231
|
+
* a formatted match preview including
|
|
232
|
+
* coordinates and confidence values.
|
|
233
|
+
*
|
|
234
|
+
* Useful for debugging and visual
|
|
235
|
+
* automation tuning.
|
|
236
|
+
*
|
|
237
|
+
* @param image Image filename or path.
|
|
238
|
+
* @param confidence Match confidence threshold.
|
|
239
|
+
*
|
|
240
|
+
* Accepted range:
|
|
241
|
+
* 0.0 to 1.0
|
|
242
|
+
*
|
|
243
|
+
* Default:
|
|
244
|
+
* 0.8
|
|
245
|
+
*
|
|
246
|
+
* @returns Array of matched regions.
|
|
247
|
+
*
|
|
248
|
+
* @example
|
|
249
|
+
* await visor.previewMatches("button.png");
|
|
250
|
+
*/
|
|
251
|
+
async previewMatches(image, confidence = 0.8) {
|
|
252
|
+
const matches = await this.findAll(image, confidence);
|
|
253
|
+
console.log("\n=== MATCH PREVIEW ===\n");
|
|
254
|
+
matches.forEach((m, i) => {
|
|
255
|
+
console.log(`#${i + 1}
|
|
256
|
+
X:${m.x}
|
|
257
|
+
Y:${m.y}
|
|
258
|
+
W:${m.width}
|
|
259
|
+
H:${m.height}
|
|
260
|
+
CONF:${m.confidence.toFixed(3)}
|
|
261
|
+
`);
|
|
262
|
+
});
|
|
263
|
+
return matches;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Waits until an image appears
|
|
267
|
+
* on the screen.
|
|
268
|
+
*
|
|
269
|
+
* @param image Image filename or path.
|
|
270
|
+
*
|
|
271
|
+
* @param confidence Match confidence threshold.
|
|
272
|
+
*
|
|
273
|
+
* Accepted range:
|
|
274
|
+
* 0.0 to 1.0
|
|
275
|
+
*
|
|
276
|
+
* Default:
|
|
277
|
+
* 0.8
|
|
278
|
+
*
|
|
279
|
+
* Recommended values:
|
|
280
|
+
* - 0.7 for dynamic UI
|
|
281
|
+
* - 0.8 for standard usage
|
|
282
|
+
* - 0.9+ for strict matching
|
|
283
|
+
*
|
|
284
|
+
* @param timeout Timeout duration
|
|
285
|
+
* in milliseconds.
|
|
286
|
+
*
|
|
287
|
+
* Default:
|
|
288
|
+
* 5000ms
|
|
289
|
+
*
|
|
290
|
+
* Recommended range:
|
|
291
|
+
* 1000ms to 30000ms
|
|
292
|
+
*
|
|
293
|
+
* @throws Error if timeout occurs.
|
|
294
|
+
*
|
|
295
|
+
* @example
|
|
296
|
+
* await visor.wait("loading-done.png");
|
|
297
|
+
*/
|
|
298
|
+
async wait(image, confidence = 0.8, timeout = 5000) {
|
|
299
|
+
const interval = 1000;
|
|
300
|
+
const attempts = timeout / interval;
|
|
301
|
+
for (let i = 0; i < attempts; i++) {
|
|
302
|
+
if (await this.exists(image, confidence)) {
|
|
303
|
+
return true;
|
|
304
|
+
}
|
|
305
|
+
await this.sleep(interval);
|
|
306
|
+
}
|
|
307
|
+
throw new Error(`Timeout waiting for image: ${image}`);
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Waits until an image disappears
|
|
311
|
+
* from the screen.
|
|
312
|
+
*
|
|
313
|
+
* @param image Image filename or path.
|
|
314
|
+
* @param confidence Match confidence threshold.
|
|
315
|
+
*
|
|
316
|
+
* Accepted range:
|
|
317
|
+
* 0.0 to 1.0
|
|
318
|
+
*
|
|
319
|
+
* Default:
|
|
320
|
+
* 0.8
|
|
321
|
+
* @param timeout Timeout duration
|
|
322
|
+
* in milliseconds.
|
|
323
|
+
*
|
|
324
|
+
* Default:
|
|
325
|
+
* 5000ms
|
|
326
|
+
*
|
|
327
|
+
* Recommended range:
|
|
328
|
+
* 1000ms to 30000ms
|
|
329
|
+
* @throws Error if timeout occurs.
|
|
330
|
+
*
|
|
331
|
+
* @example
|
|
332
|
+
* await visor.waitToVanish("spinner.png");
|
|
333
|
+
*/
|
|
334
|
+
async waitToVanish(image, confidence = 0.8, timeout = 5000) {
|
|
335
|
+
const start = Date.now();
|
|
336
|
+
while (Date.now() - start < timeout) {
|
|
337
|
+
if (!(await this.exists(image, confidence))) {
|
|
338
|
+
return true;
|
|
339
|
+
}
|
|
340
|
+
await this.sleep(1000);
|
|
341
|
+
}
|
|
342
|
+
throw new Error(`Timeout waiting for image to vanish: ${image}`);
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Waits until specific text appears
|
|
346
|
+
* on screen using OCR.
|
|
347
|
+
*
|
|
348
|
+
* Performance Note:
|
|
349
|
+
* OCR operations are computationally
|
|
350
|
+
* expensive and may take 1–5 seconds
|
|
351
|
+
* depending on screen complexity.
|
|
352
|
+
*
|
|
353
|
+
* @param text Text to search for.
|
|
354
|
+
*
|
|
355
|
+
* @param timeout Timeout duration
|
|
356
|
+
* in milliseconds.
|
|
357
|
+
*
|
|
358
|
+
* Default:
|
|
359
|
+
* 5000ms
|
|
360
|
+
*
|
|
361
|
+
* Recommended range:
|
|
362
|
+
* 1000ms to 30000ms
|
|
363
|
+
*
|
|
364
|
+
* @throws Error if timeout occurs.
|
|
365
|
+
*
|
|
366
|
+
* @example
|
|
367
|
+
* await visor.waitText("Success");
|
|
368
|
+
*/
|
|
369
|
+
async waitText(text, timeout = 5000) {
|
|
370
|
+
const start = Date.now();
|
|
371
|
+
while (Date.now() - start < timeout) {
|
|
372
|
+
if (await this.existsText(text)) {
|
|
373
|
+
return true;
|
|
374
|
+
}
|
|
375
|
+
await this.sleep(1000);
|
|
376
|
+
}
|
|
377
|
+
throw new Error(`Timeout waiting for text: ${text}`);
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Waits until specific text disappears
|
|
381
|
+
* from screen using OCR.
|
|
382
|
+
* Performance Note:
|
|
383
|
+
* OCR operations are computationally
|
|
384
|
+
* expensive and may take 1–5 seconds
|
|
385
|
+
* depending on screen complexity.
|
|
386
|
+
*
|
|
387
|
+
* @param text Text to search for.
|
|
388
|
+
* @param timeout Timeout duration
|
|
389
|
+
* in milliseconds.
|
|
390
|
+
*
|
|
391
|
+
* Default:
|
|
392
|
+
* 5000ms
|
|
393
|
+
*
|
|
394
|
+
* Recommended range:
|
|
395
|
+
* 1000ms to 30000ms
|
|
396
|
+
*
|
|
397
|
+
* @throws Error if timeout occurs.
|
|
398
|
+
*
|
|
399
|
+
* @example
|
|
400
|
+
* await visor.waitTextToVanish("Loading");
|
|
401
|
+
*/
|
|
402
|
+
async waitTextToVanish(text, timeout = 5000) {
|
|
403
|
+
const start = Date.now();
|
|
404
|
+
while (Date.now() - start < timeout) {
|
|
405
|
+
if (!(await this.existsText(text))) {
|
|
406
|
+
return true;
|
|
407
|
+
}
|
|
408
|
+
await this.sleep(1000);
|
|
409
|
+
}
|
|
410
|
+
throw new Error(`Timeout waiting for text to vanish: ${text}`);
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Finds text on screen using OCR
|
|
414
|
+
* and performs a left mouse click
|
|
415
|
+
* on the matched text region.
|
|
416
|
+
*
|
|
417
|
+
* @param text Text to search for.
|
|
418
|
+
*
|
|
419
|
+
* @throws Error if text is not found.
|
|
420
|
+
*
|
|
421
|
+
* @example
|
|
422
|
+
* await visor.clickText("Login");
|
|
423
|
+
*/
|
|
424
|
+
async clickText(text) {
|
|
425
|
+
const region = await (0, text_1.findText)(text);
|
|
426
|
+
if (!region) {
|
|
427
|
+
throw new Error(`Text not found: ${text}`);
|
|
428
|
+
}
|
|
429
|
+
await (0, mouse_1.clickRegion)(region);
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Finds text on screen or inside
|
|
433
|
+
* a specific region using OCR.
|
|
434
|
+
*
|
|
435
|
+
* @param text Text to search for.
|
|
436
|
+
* @param region Optional search region.
|
|
437
|
+
*
|
|
438
|
+
* @returns Matched text region or null.
|
|
439
|
+
*
|
|
440
|
+
* @example
|
|
441
|
+
* const region =
|
|
442
|
+
* await visor.findText("Submit");
|
|
443
|
+
*/
|
|
444
|
+
async findText(text, region) {
|
|
445
|
+
return await (0, text_1.findText)(text, region);
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Checks whether specific text exists
|
|
449
|
+
* on screen using OCR.
|
|
450
|
+
*
|
|
451
|
+
* @param text Text to search for.
|
|
452
|
+
* @param region Optional search region.
|
|
453
|
+
*
|
|
454
|
+
* @returns True if text exists.
|
|
455
|
+
*
|
|
456
|
+
* @example
|
|
457
|
+
* const exists =
|
|
458
|
+
* await visor.existsText("Teams");
|
|
459
|
+
*/
|
|
460
|
+
async existsText(text, region) {
|
|
461
|
+
return await (0, text_1.existsText)(text, region);
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Performs OCR on a specific
|
|
465
|
+
* screen region.
|
|
466
|
+
*
|
|
467
|
+
* @param region Screen region coordinates.
|
|
468
|
+
*
|
|
469
|
+
* @returns OCR result object.
|
|
470
|
+
*
|
|
471
|
+
* @example
|
|
472
|
+
* const result =
|
|
473
|
+
* await visor.readRegion({
|
|
474
|
+
* x: 100,
|
|
475
|
+
* y: 100,
|
|
476
|
+
* width: 500,
|
|
477
|
+
* height: 300
|
|
478
|
+
* });
|
|
479
|
+
*/
|
|
480
|
+
async readRegion(region) {
|
|
481
|
+
return await (0, ocr_1.extractTextFromRegion)(region);
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Performs OCR on the entire screen
|
|
485
|
+
* using Tesseract OCR.
|
|
486
|
+
*
|
|
487
|
+
* Captures the current screen,
|
|
488
|
+
* preprocesses the image, and
|
|
489
|
+
* extracts readable text content.
|
|
490
|
+
*
|
|
491
|
+
* Performance Note:
|
|
492
|
+
* OCR is computationally expensive
|
|
493
|
+
* and may take 1–5 seconds depending
|
|
494
|
+
* on:
|
|
495
|
+
* - screen complexity
|
|
496
|
+
* - text density
|
|
497
|
+
* - display resolution
|
|
498
|
+
* - system performance
|
|
499
|
+
*
|
|
500
|
+
* Returns:
|
|
501
|
+
* - extracted text
|
|
502
|
+
* - OCR metadata
|
|
503
|
+
* - TSV layout information
|
|
504
|
+
* - HOCR data
|
|
505
|
+
*
|
|
506
|
+
* @returns OCR result object.
|
|
507
|
+
*
|
|
508
|
+
* @example
|
|
509
|
+
* const result =
|
|
510
|
+
* await visor.readScreen();
|
|
511
|
+
*
|
|
512
|
+
* console.log(result.text);
|
|
513
|
+
*/
|
|
514
|
+
async readScreen() {
|
|
515
|
+
return await (0, ocr_1.extractTextFromRegion)();
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Captures a screenshot of the
|
|
519
|
+
* current screen and saves it
|
|
520
|
+
* to the specified path.
|
|
521
|
+
*
|
|
522
|
+
* Supported formats depend on
|
|
523
|
+
* the output file extension.
|
|
524
|
+
*
|
|
525
|
+
* @param path Output image path.
|
|
526
|
+
*
|
|
527
|
+
* @example
|
|
528
|
+
* await visor.captureScreenshot(
|
|
529
|
+
* "./screenshots/home.png"
|
|
530
|
+
* );
|
|
531
|
+
*/
|
|
532
|
+
async captureScreenshot(path) {
|
|
533
|
+
console.log(`[VISOR] Saving screenshot: ${path}`);
|
|
534
|
+
await (0, screen_1.saveScreenshot)(path);
|
|
535
|
+
console.log("[VISOR] Screenshot saved");
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* Opens a desktop application,
|
|
539
|
+
* executable, file, folder,
|
|
540
|
+
* or URL using Windows shell.
|
|
541
|
+
*
|
|
542
|
+
* Performance Note:
|
|
543
|
+
* Some Electron-based applications
|
|
544
|
+
* may require additional startup
|
|
545
|
+
* stabilization time.
|
|
546
|
+
*
|
|
547
|
+
* @param command App name, executable,
|
|
548
|
+
* file path, or URL.
|
|
549
|
+
*
|
|
550
|
+
* @example
|
|
551
|
+
* await visor.openApp("notepad");
|
|
552
|
+
*
|
|
553
|
+
* @example
|
|
554
|
+
* await visor.openApp("chrome.exe");
|
|
555
|
+
*/
|
|
556
|
+
async openApp(command) {
|
|
557
|
+
console.log(`[VISOR] Opening app: ${command}`);
|
|
558
|
+
await (0, app_1.openApp)(command);
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Drags one visual element and
|
|
562
|
+
* drops it onto another using
|
|
563
|
+
* image matching.
|
|
564
|
+
*
|
|
565
|
+
* @param source Source image.
|
|
566
|
+
*
|
|
567
|
+
* @param target Target image.
|
|
568
|
+
*
|
|
569
|
+
* @param confidence Match confidence threshold.
|
|
570
|
+
*
|
|
571
|
+
* Accepted range:
|
|
572
|
+
* 0.0 to 1.0
|
|
573
|
+
*
|
|
574
|
+
* Default:
|
|
575
|
+
* 0.8
|
|
576
|
+
*
|
|
577
|
+
* Recommended values:
|
|
578
|
+
* - 0.7 for dynamic UI
|
|
579
|
+
* - 0.8 for standard usage
|
|
580
|
+
* - 0.9+ for strict matching
|
|
581
|
+
*
|
|
582
|
+
* @throws Error if source or target
|
|
583
|
+
* image is not found.
|
|
584
|
+
*
|
|
585
|
+
* @example
|
|
586
|
+
* await visor.dragDrop(
|
|
587
|
+
* "file.png",
|
|
588
|
+
* "folder.png"
|
|
589
|
+
* );
|
|
590
|
+
*/
|
|
591
|
+
async dragDrop(source, target, confidence = 0.8) {
|
|
592
|
+
const src = await this.find(source, confidence);
|
|
593
|
+
const dst = await this.find(target, confidence);
|
|
594
|
+
if (!src || !dst) {
|
|
595
|
+
throw new Error("Source or target not found");
|
|
596
|
+
}
|
|
597
|
+
await (0, mouse_1.moveToRegion)(src);
|
|
598
|
+
await this.mouse.pressButton(this.Button.LEFT);
|
|
599
|
+
await (0, mouse_1.moveToRegion)(dst);
|
|
600
|
+
await this.mouse.releaseButton(this.Button.LEFT);
|
|
601
|
+
console.log("[MOUSE] Drag and drop completed");
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* Moves the mouse cursor to the
|
|
605
|
+
* matched image location without
|
|
606
|
+
* performing a click.
|
|
607
|
+
*
|
|
608
|
+
* @param image Image filename or path.
|
|
609
|
+
* @param confidence Match confidence threshold.
|
|
610
|
+
*
|
|
611
|
+
* Accepted range:
|
|
612
|
+
* 0.0 to 1.0
|
|
613
|
+
*
|
|
614
|
+
* Default:
|
|
615
|
+
* 0.8
|
|
616
|
+
*
|
|
617
|
+
* @throws Error if image is not found.
|
|
618
|
+
*
|
|
619
|
+
* @example
|
|
620
|
+
* await visor.hover("menu.png");
|
|
621
|
+
*/
|
|
622
|
+
async hover(image, confidence = 0.8) {
|
|
623
|
+
const region = await this.find(image, confidence);
|
|
624
|
+
if (!region) {
|
|
625
|
+
throw new Error(`Image not found: ${image}`);
|
|
626
|
+
}
|
|
627
|
+
await (0, mouse_1.moveToRegion)(region);
|
|
628
|
+
console.log("[MOUSE] Hover completed");
|
|
629
|
+
}
|
|
630
|
+
/**
|
|
631
|
+
* Scrolls the mouse wheel downward.
|
|
632
|
+
*
|
|
633
|
+
* @param amount Scroll amount.
|
|
634
|
+
*
|
|
635
|
+
* Default:
|
|
636
|
+
* 500
|
|
637
|
+
*
|
|
638
|
+
* Recommended range:
|
|
639
|
+
* 100 to 3000
|
|
640
|
+
*
|
|
641
|
+
* @example
|
|
642
|
+
* await visor.scrollDown(1000);
|
|
643
|
+
*/
|
|
644
|
+
async scrollDown(amount = 500) {
|
|
645
|
+
await this.mouse.scrollDown(amount);
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Scrolls the mouse wheel upward.
|
|
649
|
+
*
|
|
650
|
+
* @param amount Scroll amount.
|
|
651
|
+
*
|
|
652
|
+
* Default:
|
|
653
|
+
* 500
|
|
654
|
+
*
|
|
655
|
+
* Recommended range:
|
|
656
|
+
* 100 to 3000
|
|
657
|
+
*
|
|
658
|
+
* @example
|
|
659
|
+
* await visor.scrollUp(1000);
|
|
660
|
+
*/
|
|
661
|
+
async scrollUp(amount = 500) {
|
|
662
|
+
await this.mouse.scrollUp(amount);
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Types text using keyboard automation.
|
|
666
|
+
*
|
|
667
|
+
* @param text Text to type.
|
|
668
|
+
*
|
|
669
|
+
* @example
|
|
670
|
+
* await visor.type("Hello World");
|
|
671
|
+
*/
|
|
672
|
+
async type(text) {
|
|
673
|
+
await this.keyboard.type(text);
|
|
674
|
+
}
|
|
675
|
+
/**
|
|
676
|
+
* Presses one or more keyboard keys.
|
|
677
|
+
*
|
|
678
|
+
* Supports both single key presses
|
|
679
|
+
* and hotkey combinations.
|
|
680
|
+
*
|
|
681
|
+
* @param keys Keyboard keys to press.
|
|
682
|
+
*
|
|
683
|
+
* @example
|
|
684
|
+
* await visor.press(visor.Key.Enter);
|
|
685
|
+
*
|
|
686
|
+
* @example
|
|
687
|
+
* await visor.press(
|
|
688
|
+
* visor.Key.LeftControl,
|
|
689
|
+
* visor.Key.S
|
|
690
|
+
* );
|
|
691
|
+
*/
|
|
692
|
+
async press(...keys) {
|
|
693
|
+
await this.keyboard.pressKey(...keys);
|
|
694
|
+
await this.keyboard.releaseKey(...keys);
|
|
695
|
+
}
|
|
696
|
+
/**
|
|
697
|
+
* Pauses execution for a specified
|
|
698
|
+
* duration.
|
|
699
|
+
*
|
|
700
|
+
* Useful for:
|
|
701
|
+
* - UI stabilization
|
|
702
|
+
* - animation completion
|
|
703
|
+
* - Electron rendering delays
|
|
704
|
+
* - OCR synchronization
|
|
705
|
+
* - desktop transition handling
|
|
706
|
+
*
|
|
707
|
+
* Recommended for short stabilization
|
|
708
|
+
* waits rather than long fixed delays.
|
|
709
|
+
*
|
|
710
|
+
* @param ms Delay duration
|
|
711
|
+
* in milliseconds.
|
|
712
|
+
*
|
|
713
|
+
* Recommended range:
|
|
714
|
+
* 200ms to 5000ms
|
|
715
|
+
*
|
|
716
|
+
* Excessive sleep usage may
|
|
717
|
+
* slow automation execution.
|
|
718
|
+
*
|
|
719
|
+
* @example
|
|
720
|
+
* await visor.sleep(2000);
|
|
721
|
+
*/
|
|
722
|
+
async sleep(ms) {
|
|
723
|
+
return new Promise(r => setTimeout(r, ms));
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* Moves the mouse cursor to the
|
|
727
|
+
* specified screen coordinates.
|
|
728
|
+
*
|
|
729
|
+
* Coordinates should match the
|
|
730
|
+
* current display scaling setup.
|
|
731
|
+
*
|
|
732
|
+
* @param x X coordinate.
|
|
733
|
+
* @param y Y coordinate.
|
|
734
|
+
*
|
|
735
|
+
* @example
|
|
736
|
+
* await visor.moveMouse(500, 300);
|
|
737
|
+
*/
|
|
738
|
+
async moveMouse(x, y) {
|
|
739
|
+
await this.mouse.move((0, nut_js_1.straightTo)(new nut_js_1.Point(x, y)));
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* Returns the current mouse cursor
|
|
743
|
+
* position.
|
|
744
|
+
*
|
|
745
|
+
* @returns Current mouse coordinates.
|
|
746
|
+
*
|
|
747
|
+
* @example
|
|
748
|
+
* const pos =
|
|
749
|
+
* await visor.getMousePosition();
|
|
750
|
+
*/
|
|
751
|
+
async getMousePosition() {
|
|
752
|
+
return await this.mouse.getPosition();
|
|
753
|
+
}
|
|
754
|
+
/**
|
|
755
|
+
* Sets the display scaling factor
|
|
756
|
+
* used for coordinate normalization
|
|
757
|
+
* and image matching.
|
|
758
|
+
*
|
|
759
|
+
* Required for:
|
|
760
|
+
* - high-DPI displays
|
|
761
|
+
* - Windows display scaling
|
|
762
|
+
* - accurate mouse positioning
|
|
763
|
+
* - reliable template matching
|
|
764
|
+
*
|
|
765
|
+
* Common values:
|
|
766
|
+
* - 1.0 = 100% scaling
|
|
767
|
+
* - 1.25 = 125% scaling
|
|
768
|
+
* - 1.5 = 150% scaling
|
|
769
|
+
* - 2.0 = 200% scaling
|
|
770
|
+
*
|
|
771
|
+
* Incorrect scaling values may cause:
|
|
772
|
+
* - inaccurate clicks
|
|
773
|
+
* - failed image matching
|
|
774
|
+
* - incorrect OCR regions
|
|
775
|
+
* - offset mouse movement
|
|
776
|
+
*
|
|
777
|
+
* @param scale Display scaling factor.
|
|
778
|
+
*
|
|
779
|
+
* Recommended range:
|
|
780
|
+
* 1.0 to 2.0
|
|
781
|
+
*
|
|
782
|
+
* @example
|
|
783
|
+
* visor.setScaleFactor(1.5);
|
|
784
|
+
*/
|
|
785
|
+
setScaleFactor(scale) {
|
|
786
|
+
config_1.visorConfig.scaleFactor = scale;
|
|
787
|
+
}
|
|
788
|
+
/**
|
|
789
|
+
* Returns the current display
|
|
790
|
+
* scaling factor.
|
|
791
|
+
*
|
|
792
|
+
* @returns Current scale factor.
|
|
793
|
+
*
|
|
794
|
+
* @example
|
|
795
|
+
* const scale =
|
|
796
|
+
* visor.getScaleFactor();
|
|
797
|
+
*/
|
|
798
|
+
getScaleFactor() {
|
|
799
|
+
return config_1.visorConfig.scaleFactor;
|
|
800
|
+
}
|
|
801
|
+
/**
|
|
802
|
+
* Enables or disables debug logging.
|
|
803
|
+
*
|
|
804
|
+
* @param mode Debug mode state.
|
|
805
|
+
*
|
|
806
|
+
* @example
|
|
807
|
+
* visor.setDebug(true);
|
|
808
|
+
*/
|
|
809
|
+
setDebug(mode) {
|
|
810
|
+
config_1.visorConfig.debug = mode;
|
|
811
|
+
}
|
|
812
|
+
/**
|
|
813
|
+
* Sets the default image search path
|
|
814
|
+
* used for visual automation APIs.
|
|
815
|
+
*
|
|
816
|
+
* @param path Image directory path.
|
|
817
|
+
*
|
|
818
|
+
* @example
|
|
819
|
+
* visor.setImagePath("./images");
|
|
820
|
+
*/
|
|
821
|
+
setImagePath(path) {
|
|
822
|
+
config_1.visorConfig.imagePath = path;
|
|
823
|
+
}
|
|
824
|
+
/**
|
|
825
|
+
* Returns the current default
|
|
826
|
+
* image search path.
|
|
827
|
+
*
|
|
828
|
+
* @returns Image path.
|
|
829
|
+
*
|
|
830
|
+
* @example
|
|
831
|
+
* const path =
|
|
832
|
+
* visor.getImagePath();
|
|
833
|
+
*/
|
|
834
|
+
getImagePath() {
|
|
835
|
+
return config_1.visorConfig.imagePath;
|
|
836
|
+
}
|
|
837
|
+
/**
|
|
838
|
+
* Closes a desktop application
|
|
839
|
+
* using Windows taskkill.
|
|
840
|
+
*
|
|
841
|
+
* @param processName Process executable name.
|
|
842
|
+
*
|
|
843
|
+
* @example
|
|
844
|
+
* await visor.closeApp("notepad.exe");
|
|
845
|
+
*
|
|
846
|
+
* @example
|
|
847
|
+
* await visor.closeApp("ms-teams.exe");
|
|
848
|
+
*/
|
|
849
|
+
async closeApp(processName) {
|
|
850
|
+
await (0, app_1.closeApp)(processName);
|
|
851
|
+
}
|
|
852
|
+
/**
|
|
853
|
+
* Loads multiple framework settings
|
|
854
|
+
* at once from a configuration object.
|
|
855
|
+
*
|
|
856
|
+
* Useful for centralized framework
|
|
857
|
+
* initialization and project setup.
|
|
858
|
+
*
|
|
859
|
+
* Supported settings:
|
|
860
|
+
* - scaleFactor
|
|
861
|
+
* - imagePath
|
|
862
|
+
* - debug
|
|
863
|
+
*
|
|
864
|
+
* @param config Framework configuration object.
|
|
865
|
+
*
|
|
866
|
+
* @example
|
|
867
|
+
* visor.loadConfig({
|
|
868
|
+
* scaleFactor: 1.5,
|
|
869
|
+
* imagePath: "./images",
|
|
870
|
+
* debug: true
|
|
871
|
+
* });
|
|
872
|
+
*/
|
|
873
|
+
loadConfig(config) {
|
|
874
|
+
if (config.scaleFactor !== undefined) {
|
|
875
|
+
this.setScaleFactor(config.scaleFactor);
|
|
876
|
+
}
|
|
877
|
+
if (config.imagePath) {
|
|
878
|
+
this.setImagePath(config.imagePath);
|
|
879
|
+
}
|
|
880
|
+
if (config.debug !== undefined) {
|
|
881
|
+
this.setDebug(config.debug);
|
|
882
|
+
}
|
|
883
|
+
console.log("[VISOR] Config loaded");
|
|
884
|
+
}
|
|
885
|
+
/**
|
|
886
|
+
* Finds the first matching image
|
|
887
|
+
* from a list of possible images.
|
|
888
|
+
*
|
|
889
|
+
* Useful for handling:
|
|
890
|
+
* - light/dark themes
|
|
891
|
+
* - UI variations
|
|
892
|
+
* - multiple visual states
|
|
893
|
+
*
|
|
894
|
+
* @param images Array of image filenames or paths.
|
|
895
|
+
* @param confidence Match confidence threshold.
|
|
896
|
+
*
|
|
897
|
+
* Accepted range:
|
|
898
|
+
* 0.0 to 1.0
|
|
899
|
+
*
|
|
900
|
+
* Default:
|
|
901
|
+
* 0.8
|
|
902
|
+
*
|
|
903
|
+
* @returns Object containing matched image
|
|
904
|
+
* and region, or null if none matched.
|
|
905
|
+
*
|
|
906
|
+
* @example
|
|
907
|
+
* const result =
|
|
908
|
+
* await visor.findAny([
|
|
909
|
+
* "chat-light.png",
|
|
910
|
+
* "chat-dark.png"
|
|
911
|
+
* ]);
|
|
912
|
+
*/
|
|
913
|
+
async findAny(images, confidence = 0.8) {
|
|
914
|
+
const screen = await (0, screen_1.captureScreen)();
|
|
915
|
+
try {
|
|
916
|
+
for (const image of images) {
|
|
917
|
+
const template = (0, matcher_1.loadTemplate)((0, path_1.resolveImagePath)(image));
|
|
918
|
+
try {
|
|
919
|
+
const region = (0, matcher_1.matchTemplate)(screen, template, confidence);
|
|
920
|
+
if (region) {
|
|
921
|
+
return {
|
|
922
|
+
image,
|
|
923
|
+
region
|
|
924
|
+
};
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
finally {
|
|
928
|
+
template.delete();
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
return null;
|
|
932
|
+
}
|
|
933
|
+
finally {
|
|
934
|
+
screen.delete();
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
/**
|
|
938
|
+
* Checks whether any image from
|
|
939
|
+
* a list exists on the current screen.
|
|
940
|
+
*
|
|
941
|
+
* Useful for handling:
|
|
942
|
+
* - multiple themes
|
|
943
|
+
* - alternate UI layouts
|
|
944
|
+
* - dynamic visual states
|
|
945
|
+
*
|
|
946
|
+
* @param images Array of image filenames or paths.
|
|
947
|
+
* @param confidence Match confidence threshold.
|
|
948
|
+
*
|
|
949
|
+
* Accepted range:
|
|
950
|
+
* 0.0 to 1.0
|
|
951
|
+
*
|
|
952
|
+
* Default:
|
|
953
|
+
* 0.8
|
|
954
|
+
*
|
|
955
|
+
* @returns True if any image exists.
|
|
956
|
+
*
|
|
957
|
+
* @example
|
|
958
|
+
* const exists =
|
|
959
|
+
* await visor.existsAny([
|
|
960
|
+
* "save-light.png",
|
|
961
|
+
* "save-dark.png"
|
|
962
|
+
* ]);
|
|
963
|
+
*/
|
|
964
|
+
async existsAny(images, confidence = 0.8) {
|
|
965
|
+
return (await this.findAny(images, confidence)) !== null;
|
|
966
|
+
}
|
|
967
|
+
/**
|
|
968
|
+
* Finds the first matching image
|
|
969
|
+
* from a list and performs a
|
|
970
|
+
* left mouse click on it.
|
|
971
|
+
*
|
|
972
|
+
* Useful for handling:
|
|
973
|
+
* - light/dark themes
|
|
974
|
+
* - UI variations
|
|
975
|
+
* - dynamic buttons/icons
|
|
976
|
+
*
|
|
977
|
+
* @param images Array of image
|
|
978
|
+
* filenames or paths.
|
|
979
|
+
*
|
|
980
|
+
* @param confidence Match confidence threshold.
|
|
981
|
+
*
|
|
982
|
+
* Accepted range:
|
|
983
|
+
* 0.0 to 1.0
|
|
984
|
+
*
|
|
985
|
+
* Default:
|
|
986
|
+
* 0.8
|
|
987
|
+
*
|
|
988
|
+
* Recommended values:
|
|
989
|
+
* - 0.7 for dynamic UI
|
|
990
|
+
* - 0.8 for standard usage
|
|
991
|
+
* - 0.9+ for strict matching
|
|
992
|
+
*
|
|
993
|
+
* Lower confidence increases
|
|
994
|
+
* flexibility but may increase
|
|
995
|
+
* false positives.
|
|
996
|
+
*
|
|
997
|
+
* @throws Error if none of the
|
|
998
|
+
* images are found.
|
|
999
|
+
*
|
|
1000
|
+
* @example
|
|
1001
|
+
* await visor.clickAny([
|
|
1002
|
+
* "send-light.png",
|
|
1003
|
+
* "send-dark.png"
|
|
1004
|
+
* ]);
|
|
1005
|
+
*/
|
|
1006
|
+
async clickAny(images, confidence = 0.8) {
|
|
1007
|
+
const result = await this.findAny(images, confidence);
|
|
1008
|
+
if (!result) {
|
|
1009
|
+
throw new Error(`None of the images were found`);
|
|
1010
|
+
}
|
|
1011
|
+
console.log(`[VISOR] Clicking image: ${result.image}`);
|
|
1012
|
+
await (0, mouse_1.clickRegion)(result.region);
|
|
1013
|
+
}
|
|
1014
|
+
/**
|
|
1015
|
+
* Waits until any image from
|
|
1016
|
+
* a list appears on screen.
|
|
1017
|
+
*
|
|
1018
|
+
* Useful for handling:
|
|
1019
|
+
* - multiple themes
|
|
1020
|
+
* - alternate UI layouts
|
|
1021
|
+
* - dynamic application states
|
|
1022
|
+
*
|
|
1023
|
+
* @param images Array of image
|
|
1024
|
+
* filenames or paths.
|
|
1025
|
+
*
|
|
1026
|
+
* @param confidence Match confidence threshold.
|
|
1027
|
+
*
|
|
1028
|
+
* Accepted range:
|
|
1029
|
+
* 0.0 to 1.0
|
|
1030
|
+
*
|
|
1031
|
+
* Default:
|
|
1032
|
+
* 0.8
|
|
1033
|
+
*
|
|
1034
|
+
* Recommended values:
|
|
1035
|
+
* - 0.7 for dynamic UI
|
|
1036
|
+
* - 0.8 for standard usage
|
|
1037
|
+
* - 0.9+ for strict matching
|
|
1038
|
+
*
|
|
1039
|
+
* Lower confidence increases
|
|
1040
|
+
* flexibility but may increase
|
|
1041
|
+
* false positives.
|
|
1042
|
+
*
|
|
1043
|
+
* @param timeout Timeout duration
|
|
1044
|
+
* in milliseconds.
|
|
1045
|
+
*
|
|
1046
|
+
* Default:
|
|
1047
|
+
* 5000ms
|
|
1048
|
+
*
|
|
1049
|
+
* Recommended range:
|
|
1050
|
+
* 1000ms to 30000ms
|
|
1051
|
+
*
|
|
1052
|
+
* @returns Object containing matched image
|
|
1053
|
+
* and region.
|
|
1054
|
+
*
|
|
1055
|
+
* @throws Error if timeout occurs.
|
|
1056
|
+
*
|
|
1057
|
+
* @example
|
|
1058
|
+
* await visor.waitAny([
|
|
1059
|
+
* "home-light.png",
|
|
1060
|
+
* "home-dark.png"
|
|
1061
|
+
* ]);
|
|
1062
|
+
*/
|
|
1063
|
+
async waitAny(images, confidence = 0.8, timeout = 5000) {
|
|
1064
|
+
const interval = 1000;
|
|
1065
|
+
const attempts = timeout / interval;
|
|
1066
|
+
for (let i = 0; i < attempts; i++) {
|
|
1067
|
+
console.log(`[WAITANY] Attempt ${i + 1}`);
|
|
1068
|
+
for (const image of images) {
|
|
1069
|
+
console.log(`[WAITANY] Checking ${image}`);
|
|
1070
|
+
const exists = await this.exists(image, confidence);
|
|
1071
|
+
if (exists) {
|
|
1072
|
+
console.log(`[WAITANY] Found ${image}`);
|
|
1073
|
+
return true;
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
await this.sleep(interval);
|
|
1077
|
+
}
|
|
1078
|
+
throw new Error("Timeout waiting for images");
|
|
1079
|
+
}
|
|
1080
|
+
/**
|
|
1081
|
+
* Terminates shared OCR worker.
|
|
1082
|
+
*
|
|
1083
|
+
* Useful for framework cleanup
|
|
1084
|
+
* after long automation sessions.
|
|
1085
|
+
*
|
|
1086
|
+
* Recommended for:
|
|
1087
|
+
* - long-running automation
|
|
1088
|
+
* - memory cleanup
|
|
1089
|
+
* - graceful framework shutdown
|
|
1090
|
+
* @example
|
|
1091
|
+
* await visor.terminateOCR();
|
|
1092
|
+
*/
|
|
1093
|
+
async terminateOCR() {
|
|
1094
|
+
await (0, ocr_1.terminateOCR)();
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
exports.Visor = Visor;
|
|
1098
|
+
exports.visor = new Visor();
|