@aspiresys/visor 1.2.10 → 1.2.11
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/index.d.ts +4 -3
- package/dist/index.js +12 -2
- package/inspector/index.html +5 -14
- package/inspector/src/renderer.js +45 -2
- package/inspector/styles.css +6 -8
- package/package.json +1 -1
- package/readme.md +139 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Key, Button, Point } from "@nut-tree-fork/nut-js";
|
|
2
2
|
import { Region } from "./types";
|
|
3
3
|
export declare class Visor {
|
|
4
|
-
mouse
|
|
5
|
-
keyboard
|
|
4
|
+
private mouse;
|
|
5
|
+
private keyboard;
|
|
6
6
|
Key: typeof Key;
|
|
7
7
|
Button: typeof Button;
|
|
8
8
|
private configWarningShown;
|
|
@@ -719,11 +719,12 @@ export declare class Visor {
|
|
|
719
719
|
*
|
|
720
720
|
* @param x X coordinate.
|
|
721
721
|
* @param y Y coordinate.
|
|
722
|
+
* @param inspectorCoords Boolean to represent if the coords are from visor-inspector
|
|
722
723
|
*
|
|
723
724
|
* @example
|
|
724
725
|
* await visor.moveMouse(500, 300);
|
|
725
726
|
*/
|
|
726
|
-
moveMouse(x: number, y: number): Promise<void>;
|
|
727
|
+
moveMouse(x: number, y: number, inspectorCoords?: boolean): Promise<void>;
|
|
727
728
|
/**
|
|
728
729
|
* Returns the current mouse cursor
|
|
729
730
|
* position.
|
package/dist/index.js
CHANGED
|
@@ -336,8 +336,12 @@ class Visor {
|
|
|
336
336
|
const start = Date.now();
|
|
337
337
|
const screen = await (0, screen_1.captureScreen)();
|
|
338
338
|
const template = await (0, matcher_1.loadTemplate)((0, path_1.resolveImagePath)(image));
|
|
339
|
+
const scaleFactor = config_1.visorConfig.scaleFactor;
|
|
339
340
|
try {
|
|
340
341
|
const result = (0, matcher_1.matchTemplate)(screen, template, confidence);
|
|
342
|
+
if (result) {
|
|
343
|
+
(0, logger_1.log)(`[FIND] Actual coords: (${Math.floor(result.x * scaleFactor)}, ${Math.floor(result.y * scaleFactor)})`);
|
|
344
|
+
}
|
|
341
345
|
(0, logger_1.log)(`[FIND] Total: ${Date.now() - start} ms`);
|
|
342
346
|
return result;
|
|
343
347
|
}
|
|
@@ -929,13 +933,19 @@ CONF:${m.confidence.toFixed(3)}
|
|
|
929
933
|
*
|
|
930
934
|
* @param x X coordinate.
|
|
931
935
|
* @param y Y coordinate.
|
|
936
|
+
* @param inspectorCoords Boolean to represent if the coords are from visor-inspector
|
|
932
937
|
*
|
|
933
938
|
* @example
|
|
934
939
|
* await visor.moveMouse(500, 300);
|
|
935
940
|
*/
|
|
936
|
-
async moveMouse(x, y) {
|
|
941
|
+
async moveMouse(x, y, inspectorCoords = false) {
|
|
937
942
|
this.checkConfig();
|
|
938
|
-
|
|
943
|
+
if (inspectorCoords) {
|
|
944
|
+
await this.mouse.move((0, nut_js_1.straightTo)(new nut_js_1.Point(x, y)));
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
947
|
+
const scaleFactor = config_1.visorConfig.scaleFactor;
|
|
948
|
+
await this.mouse.move((0, nut_js_1.straightTo)(new nut_js_1.Point(Math.floor(x / scaleFactor), Math.floor(y / scaleFactor))));
|
|
939
949
|
}
|
|
940
950
|
/**
|
|
941
951
|
* Returns the current mouse cursor
|
package/inspector/index.html
CHANGED
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
<div class="input-wrapper">
|
|
25
25
|
<span class="input-hint">Confidence</span>
|
|
26
26
|
<input id="confidenceInput" type="number" min="0" max="1" step="0.01" value="0.8" />
|
|
27
|
-
<span class="input-hint">(0.0 - 1.0)</span>
|
|
27
|
+
<span class="input-hint">(0.0 - 1.0) </span>
|
|
28
|
+
Current Template: <span id="currentTemplate" class="template-status">None</span>
|
|
28
29
|
</div>
|
|
29
30
|
</div>
|
|
30
31
|
</div>
|
|
@@ -36,22 +37,12 @@
|
|
|
36
37
|
<canvas id="screenCanvas" class="canvas"></canvas>
|
|
37
38
|
</div>
|
|
38
39
|
<div class="result-panel">
|
|
40
|
+
<h3 class="section-title"> Template </h3>
|
|
41
|
+
<img id="templatePreview" class="template-preview" alt="No Template Loaded" src="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs="/>
|
|
39
42
|
<h3 class="section-title"> Match Result </h3>
|
|
40
43
|
<div id="matchResult"> No Match Tested </div>
|
|
41
|
-
|
|
44
|
+
</div>
|
|
42
45
|
</div>
|
|
43
|
-
<section class="template-section">
|
|
44
|
-
<h3 class="section-title">Template Management</h3>
|
|
45
|
-
<div class="template-grid">
|
|
46
|
-
<div class="template-info">
|
|
47
|
-
<p>
|
|
48
|
-
<strong>Current Template: </strong>
|
|
49
|
-
<span id="currentTemplate" class="template-status">None</span>
|
|
50
|
-
</p>
|
|
51
|
-
</div>
|
|
52
|
-
<img id="templatePreview" class="template-preview" />
|
|
53
|
-
</div>
|
|
54
|
-
</section>
|
|
55
46
|
</div>
|
|
56
47
|
<script src="./src/renderer.js"></script>
|
|
57
48
|
</body>
|
|
@@ -30,6 +30,7 @@ captureBtn.addEventListener("click",
|
|
|
30
30
|
const scale = image.width > maxWidth ? maxWidth / image.width : 1;
|
|
31
31
|
canvas.width = image.width * scale;
|
|
32
32
|
canvas.height = image.height * scale;
|
|
33
|
+
matchResult.textContent = " No Match Tested ";
|
|
33
34
|
ctx.drawImage(
|
|
34
35
|
image,
|
|
35
36
|
0,
|
|
@@ -125,8 +126,18 @@ saveBtn.addEventListener("click",
|
|
|
125
126
|
height: Math.abs(endY - startY),
|
|
126
127
|
outputPath: filePath
|
|
127
128
|
});
|
|
129
|
+
const fileName = filePath.split(/[\\/]/).pop();
|
|
128
130
|
selectedTemplatePath = filePath;
|
|
129
|
-
currentTemplate.textContent =
|
|
131
|
+
currentTemplate.textContent = fileName;
|
|
132
|
+
currentTemplate.title = filePath;
|
|
133
|
+
matchResult.textContent = " No Match Tested ";
|
|
134
|
+
ctx.drawImage(
|
|
135
|
+
image,
|
|
136
|
+
0,
|
|
137
|
+
0,
|
|
138
|
+
canvas.width,
|
|
139
|
+
canvas.height
|
|
140
|
+
);
|
|
130
141
|
templatePreview.src = filePath + "?t=" + Date.now();
|
|
131
142
|
alert("Template Saved");
|
|
132
143
|
}
|
|
@@ -155,8 +166,30 @@ testMatchBtn.addEventListener("click", async () => {
|
|
|
155
166
|
|
|
156
167
|
if (!result) {
|
|
157
168
|
matchResult.textContent = "✗ MATCH NOT FOUND";
|
|
169
|
+
ctx.drawImage(
|
|
170
|
+
image,
|
|
171
|
+
0,
|
|
172
|
+
0,
|
|
173
|
+
canvas.width,
|
|
174
|
+
canvas.height
|
|
175
|
+
);
|
|
158
176
|
return;
|
|
159
177
|
}
|
|
178
|
+
ctx.drawImage(
|
|
179
|
+
image,
|
|
180
|
+
0,
|
|
181
|
+
0,
|
|
182
|
+
canvas.width,
|
|
183
|
+
canvas.height
|
|
184
|
+
);
|
|
185
|
+
ctx.strokeStyle = "lime";
|
|
186
|
+
ctx.lineWidth = 3;
|
|
187
|
+
ctx.strokeRect(
|
|
188
|
+
result.x / imageScaleX,
|
|
189
|
+
result.y / imageScaleY,
|
|
190
|
+
result.width / imageScaleX,
|
|
191
|
+
result.height / imageScaleY
|
|
192
|
+
);
|
|
160
193
|
matchResult.textContent = `
|
|
161
194
|
✓ MATCH FOUND
|
|
162
195
|
|
|
@@ -178,8 +211,18 @@ loadTemplateBtn.addEventListener("click",
|
|
|
178
211
|
if (!path) {
|
|
179
212
|
return;
|
|
180
213
|
}
|
|
214
|
+
const fileName = path.split(/[\\/]/).pop();
|
|
181
215
|
selectedTemplatePath = path;
|
|
182
|
-
currentTemplate.textContent =
|
|
216
|
+
currentTemplate.textContent = fileName;
|
|
217
|
+
currentTemplate.title = path;
|
|
218
|
+
matchResult.textContent = " No Match Tested ";
|
|
219
|
+
ctx.drawImage(
|
|
220
|
+
image,
|
|
221
|
+
0,
|
|
222
|
+
0,
|
|
223
|
+
canvas.width,
|
|
224
|
+
canvas.height
|
|
225
|
+
);
|
|
183
226
|
templatePreview.src = path + "?t=" + Date.now();
|
|
184
227
|
}
|
|
185
228
|
);
|
package/inspector/styles.css
CHANGED
|
@@ -238,6 +238,7 @@ input[type="number"]:focus {
|
|
|
238
238
|
display: block;
|
|
239
239
|
margin-top: 15px;
|
|
240
240
|
box-shadow: 0 0 20px rgba(0, 212, 255, 0.1);
|
|
241
|
+
width: stretch;
|
|
241
242
|
}
|
|
242
243
|
|
|
243
244
|
/* TEMPLATE SECTION */
|
|
@@ -268,26 +269,23 @@ input[type="number"]:focus {
|
|
|
268
269
|
}
|
|
269
270
|
|
|
270
271
|
.template-status {
|
|
271
|
-
display: inline-block;
|
|
272
|
-
padding: 4px 7px;
|
|
273
|
-
background: rgba(76, 175, 80, 0.15);
|
|
274
|
-
border: 1px solid rgba(76, 175, 80, 0.3);
|
|
275
|
-
border-radius: 6px;
|
|
276
272
|
color: #4caf50;
|
|
277
|
-
font-
|
|
278
|
-
width: fit-content;
|
|
273
|
+
font-size: small;
|
|
279
274
|
}
|
|
280
275
|
|
|
281
276
|
.template-preview {
|
|
282
277
|
border: 2px solid #00d4ff;
|
|
283
278
|
border-radius: 8px;
|
|
279
|
+
width: 130px;
|
|
284
280
|
max-width: 100%;
|
|
285
|
-
height:
|
|
281
|
+
height: 130px;
|
|
286
282
|
max-height: 150px;
|
|
287
283
|
object-fit: contain;
|
|
288
284
|
background: #0a0a0a;
|
|
289
285
|
box-shadow: 0 0 20px rgba(0, 212, 255, 0.1);
|
|
290
286
|
display: none;
|
|
287
|
+
margin-top: 15px;
|
|
288
|
+
margin-bottom: 15px;
|
|
291
289
|
}
|
|
292
290
|
|
|
293
291
|
.template-preview[src]:not([src=""]) {
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -22,6 +22,8 @@ Visor is designed for automating desktop workflows using visual interactions ins
|
|
|
22
22
|
* Region OCR support
|
|
23
23
|
* Automatic display scaling detection
|
|
24
24
|
* Mouse automation
|
|
25
|
+
* Region-based mouse automation
|
|
26
|
+
* Target offset support
|
|
25
27
|
* Keyboard automation
|
|
26
28
|
* Drag & drop support
|
|
27
29
|
* Multi-theme image handling
|
|
@@ -51,6 +53,23 @@ npm install @aspiresys/visor
|
|
|
51
53
|
|
|
52
54
|
---
|
|
53
55
|
|
|
56
|
+
# Visor Inspector
|
|
57
|
+
|
|
58
|
+
Visor includes an optional desktop Inspector tool for:
|
|
59
|
+
|
|
60
|
+
* Capturing templates
|
|
61
|
+
* Testing image matches
|
|
62
|
+
* Measuring screen coordinates
|
|
63
|
+
* Validating confidence thresholds
|
|
64
|
+
|
|
65
|
+
Run:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npx visor-inspector
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
54
73
|
# Quick Start
|
|
55
74
|
|
|
56
75
|
```ts
|
|
@@ -167,6 +186,67 @@ const region =
|
|
|
167
186
|
|
|
168
187
|
---
|
|
169
188
|
|
|
189
|
+
## Region-Based Automation
|
|
190
|
+
|
|
191
|
+
Regions can be obtained from:
|
|
192
|
+
|
|
193
|
+
* visor.find()
|
|
194
|
+
* visor.findAll()
|
|
195
|
+
* visor.findText()
|
|
196
|
+
* Visor Inspector
|
|
197
|
+
|
|
198
|
+
### Move To Region
|
|
199
|
+
|
|
200
|
+
```ts
|
|
201
|
+
const region =
|
|
202
|
+
await visor.find(
|
|
203
|
+
"save.png"
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
await visor.moveToRegion(
|
|
207
|
+
region
|
|
208
|
+
);
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Click Region
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
const region =
|
|
215
|
+
await visor.find(
|
|
216
|
+
"save.png"
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
await visor.clickRegion(
|
|
220
|
+
region
|
|
221
|
+
);
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Double Click Region
|
|
225
|
+
|
|
226
|
+
```ts
|
|
227
|
+
await visor.doubleClickRegion({
|
|
228
|
+
x: 100,
|
|
229
|
+
y: 200,
|
|
230
|
+
width: 150,
|
|
231
|
+
height: 50
|
|
232
|
+
});
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Right Click Region
|
|
236
|
+
|
|
237
|
+
```ts
|
|
238
|
+
await visor.rightClickRegion({
|
|
239
|
+
x: 100,
|
|
240
|
+
y: 200,
|
|
241
|
+
width: 150,
|
|
242
|
+
height: 50
|
|
243
|
+
});
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Display scaling is automatically applied when using region-based APIs.
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
170
250
|
## Check Image Exists
|
|
171
251
|
|
|
172
252
|
```ts
|
|
@@ -230,6 +310,47 @@ await visor.hover("menu.png");
|
|
|
230
310
|
|
|
231
311
|
---
|
|
232
312
|
|
|
313
|
+
## Target Offsets
|
|
314
|
+
|
|
315
|
+
Target offsets allow mouse actions to be performed relative to the center of a matched image.
|
|
316
|
+
|
|
317
|
+
Useful for:
|
|
318
|
+
|
|
319
|
+
* Dropdown arrows
|
|
320
|
+
* Adjacent controls
|
|
321
|
+
* Dynamic layouts
|
|
322
|
+
* Composite UI elements
|
|
323
|
+
|
|
324
|
+
### Click With Offset
|
|
325
|
+
|
|
326
|
+
```ts
|
|
327
|
+
await visor.click(
|
|
328
|
+
"search.png",
|
|
329
|
+
0.8,
|
|
330
|
+
{
|
|
331
|
+
x: 50,
|
|
332
|
+
y: 0
|
|
333
|
+
}
|
|
334
|
+
);
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### Hover With Offset
|
|
338
|
+
|
|
339
|
+
```ts
|
|
340
|
+
await visor.hover(
|
|
341
|
+
"menu.png",
|
|
342
|
+
0.8,
|
|
343
|
+
{
|
|
344
|
+
x: -20,
|
|
345
|
+
y: 10
|
|
346
|
+
}
|
|
347
|
+
);
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
Offsets are applied relative to the center of the matched region before display scaling adjustments are performed.
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
233
354
|
# OCR Automation
|
|
234
355
|
|
|
235
356
|
Visor includes OCR automation powered by Tesseract.js.
|
|
@@ -351,6 +472,21 @@ await visor.moveMouse(
|
|
|
351
472
|
|
|
352
473
|
---
|
|
353
474
|
|
|
475
|
+
### Move To Inspector Region
|
|
476
|
+
|
|
477
|
+
```ts
|
|
478
|
+
await visor.moveToRegion({
|
|
479
|
+
x: 90,
|
|
480
|
+
y: 61,
|
|
481
|
+
width: 138,
|
|
482
|
+
height: 69
|
|
483
|
+
});
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
Region coordinates can be copied directly from Visor Inspector match results.
|
|
487
|
+
|
|
488
|
+
---
|
|
489
|
+
|
|
354
490
|
## Scroll Down
|
|
355
491
|
|
|
356
492
|
```ts
|
|
@@ -497,9 +633,9 @@ visor.loadConfig({
|
|
|
497
633
|
|
|
498
634
|
# Roadmap
|
|
499
635
|
|
|
500
|
-
*
|
|
501
|
-
*
|
|
502
|
-
*
|
|
636
|
+
* Match visualization overlay
|
|
637
|
+
* Inspector coordinate picker
|
|
638
|
+
* Multi-monitor support improvements
|
|
503
639
|
* Parallel image matching
|
|
504
640
|
* Advanced OCR tuning
|
|
505
641
|
* Electron recorder
|