@aspiresys/visor 1.2.10 → 1.3.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/types.d.ts CHANGED
@@ -1,10 +1,174 @@
1
- export interface Region {
1
+ /**
2
+ * Represents a rectangular screen region.
3
+ *
4
+ * A Region can be returned by:
5
+ * - image matching
6
+ * - OCR text search
7
+ * - region-based searches
8
+ *
9
+ * Provides helper methods for:
10
+ * - mouse interaction
11
+ * - OCR operations
12
+ * - image matching
13
+ * - geometry calculations
14
+ * Example:
15
+ *
16
+ * const dialog = await visor.find("dialog.png");
17
+ *
18
+ * const saveButton = await dialog.find("save.png");
19
+ *
20
+ * await saveButton.click();
21
+ *
22
+ * const text = await dialog.findText("Submit");
23
+ */
24
+ export declare class Region {
2
25
  x: number;
3
26
  y: number;
4
27
  width: number;
5
28
  height: number;
6
29
  confidence: number;
30
+ private static configWarningShown;
31
+ constructor(x: number, y: number, width: number, height: number, confidence?: number);
32
+ private checkConfig;
33
+ /**
34
+ * Returns the center point of the region.
35
+ *
36
+ * Useful for:
37
+ * - mouse movement
38
+ * - coordinate calculations
39
+ * - custom interactions
40
+ */
41
+ center(): {
42
+ x: number;
43
+ y: number;
44
+ };
45
+ /**
46
+ * Moves the mouse to the center of the region.
47
+ *
48
+ * Mouse coordinates are automatically
49
+ * adjusted for display scaling.
50
+ */
51
+ move(): Promise<void>;
52
+ /**
53
+ * Performs a left click on the region.
54
+ *
55
+ * Clicks the center of the region by default.
56
+ *
57
+ * Optional offset can be supplied to click
58
+ * a specific position relative to the center.
59
+ */
60
+ click(offset?: Offset): Promise<void>;
61
+ /**
62
+ * Performs a double left click on the region.
63
+ *
64
+ * Optional offset can be supplied to click
65
+ * a specific position relative to the center.
66
+ */
67
+ doubleClick(offset?: Offset): Promise<void>;
68
+ /**
69
+ * Performs a right click on the region.
70
+ *
71
+ * Optional offset can be supplied to click
72
+ * a specific position relative to the center.
73
+ */
74
+ rightClick(offset?: Offset): Promise<void>;
75
+ /**
76
+ * Checks whether the supplied coordinates
77
+ * are located inside this region.
78
+ *
79
+ * Returns:
80
+ * - true if inside
81
+ * - false otherwise
82
+ */
83
+ contains(x: number, y: number): boolean;
84
+ /**
85
+ * Creates a new Region shifted by the
86
+ * supplied x and y offsets.
87
+ *
88
+ * Original region remains unchanged.
89
+ */
90
+ offset(x: number, y: number): Region;
91
+ /**
92
+ * Checks whether this region overlaps
93
+ * with another region.
94
+ *
95
+ * Returns:
96
+ * - true if regions intersect
97
+ * - false otherwise
98
+ */
99
+ intersects(region: Region): boolean;
100
+ /**
101
+ * Returns the total area of the region
102
+ * in pixels.
103
+ */
104
+ area(): number;
105
+ /**
106
+ * Extracts OCR text from this region.
107
+ *
108
+ * Returns full OCR output including:
109
+ * - text
110
+ * - TSV data
111
+ * - HOCR data
112
+ * - block information
113
+ */
114
+ readText(): Promise<any>;
115
+ /**
116
+ * Searches for text within this region.
117
+ *
118
+ * Returns the matching text region
119
+ * if found, otherwise null.
120
+ *
121
+ * Supports selecting a specific match
122
+ * using the index parameter.
123
+ */
124
+ findText(text: string, index?: number): Promise<Region>;
125
+ /**
126
+ * Checks whether the supplied text
127
+ * exists within this region.
128
+ *
129
+ * Returns:
130
+ * - true if found
131
+ * - false otherwise
132
+ */
133
+ existsText(text: string): Promise<boolean>;
134
+ /**
135
+ * Searches for an image within this region.
136
+ *
137
+ * Matching is performed only inside
138
+ * the current region, improving speed
139
+ * and reducing false positives.
140
+ *
141
+ * Returns:
142
+ * - matching Region if found
143
+ * - null otherwise
144
+ */
145
+ find(image: string, confidence?: number): Promise<Region>;
146
+ /**
147
+ * Finds all image matches within this region.
148
+ *
149
+ * Matching is performed only inside
150
+ * the current region.
151
+ *
152
+ * Returns an array of matching regions.
153
+ */
154
+ findAll(image: string, confidence?: number): Promise<Region[]>;
155
+ /**
156
+ * Checks whether an image exists
157
+ * within this region.
158
+ *
159
+ * Returns:
160
+ * - true if found
161
+ * - false otherwise
162
+ */
163
+ exists(image: string, confidence?: number): Promise<boolean>;
7
164
  }
165
+ /**
166
+ * Represents an offset relative to
167
+ * a region's center point.
168
+ *
169
+ * Useful for clicking specific locations
170
+ * within a matched region.
171
+ */
8
172
  export interface Offset {
9
173
  x: number;
10
174
  y: number;
package/dist/types.js CHANGED
@@ -1,2 +1,270 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Region = void 0;
4
+ const nut_js_1 = require("@nut-tree-fork/nut-js");
5
+ const mouse_1 = require("./mouse");
6
+ const ocr_1 = require("./ocr");
7
+ const text_1 = require("./text");
8
+ const matcher_1 = require("./matcher");
9
+ const screen_1 = require("./screen");
10
+ const path_1 = require("./path");
11
+ const config_1 = require("./config");
12
+ const console_1 = require("console");
13
+ const display_1 = require("./display");
14
+ /**
15
+ * Represents a rectangular screen region.
16
+ *
17
+ * A Region can be returned by:
18
+ * - image matching
19
+ * - OCR text search
20
+ * - region-based searches
21
+ *
22
+ * Provides helper methods for:
23
+ * - mouse interaction
24
+ * - OCR operations
25
+ * - image matching
26
+ * - geometry calculations
27
+ * Example:
28
+ *
29
+ * const dialog = await visor.find("dialog.png");
30
+ *
31
+ * const saveButton = await dialog.find("save.png");
32
+ *
33
+ * await saveButton.click();
34
+ *
35
+ * const text = await dialog.findText("Submit");
36
+ */
37
+ class Region {
38
+ constructor(x, y, width, height, confidence = 1) {
39
+ this.x = x;
40
+ this.y = y;
41
+ this.width = width;
42
+ this.height = height;
43
+ this.confidence = confidence;
44
+ }
45
+ checkConfig() {
46
+ if (Region.configWarningShown) {
47
+ return;
48
+ }
49
+ const missing = [];
50
+ if (!config_1.visorConfig.imagePath) {
51
+ missing.push("imagePath");
52
+ }
53
+ if (!config_1.visorConfig.outputPath) {
54
+ missing.push("outputPath");
55
+ }
56
+ if (missing.length > 0) {
57
+ (0, console_1.warn)(`[VISOR] Configuration incomplete.\n` +
58
+ `Missing: ${missing.join(", ")}\n\n` +
59
+ `Relative paths may not work correctly.\n` +
60
+ `Use visor.loadConfig(...) or provide absolute paths directly to functions.`);
61
+ }
62
+ if (!config_1.visorConfig.initialized) {
63
+ config_1.visorConfig.scaleFactor = (0, display_1.getWindowsScaleFactor)();
64
+ (0, console_1.warn)(`[VISOR] Configuration not loaded.\n` +
65
+ `Auto-detected scaleFactor=${config_1.visorConfig.scaleFactor}.\n`);
66
+ }
67
+ if (missing.length > 0 || !config_1.visorConfig.initialized) {
68
+ Region.configWarningShown = true;
69
+ }
70
+ }
71
+ /**
72
+ * Returns the center point of the region.
73
+ *
74
+ * Useful for:
75
+ * - mouse movement
76
+ * - coordinate calculations
77
+ * - custom interactions
78
+ */
79
+ center() {
80
+ return {
81
+ x: this.x + Math.floor(this.width / 2),
82
+ y: this.y + Math.floor(this.height / 2)
83
+ };
84
+ }
85
+ /**
86
+ * Moves the mouse to the center of the region.
87
+ *
88
+ * Mouse coordinates are automatically
89
+ * adjusted for display scaling.
90
+ */
91
+ async move() {
92
+ await (0, mouse_1.moveToRegion)(this);
93
+ }
94
+ /**
95
+ * Performs a left click on the region.
96
+ *
97
+ * Clicks the center of the region by default.
98
+ *
99
+ * Optional offset can be supplied to click
100
+ * a specific position relative to the center.
101
+ */
102
+ async click(offset) {
103
+ await (0, mouse_1.clickRegion)(this, offset);
104
+ }
105
+ /**
106
+ * Performs a double left click on the region.
107
+ *
108
+ * Optional offset can be supplied to click
109
+ * a specific position relative to the center.
110
+ */
111
+ async doubleClick(offset) {
112
+ await (0, mouse_1.moveToRegion)(this, offset);
113
+ await nut_js_1.mouse.doubleClick(nut_js_1.Button.LEFT);
114
+ }
115
+ /**
116
+ * Performs a right click on the region.
117
+ *
118
+ * Optional offset can be supplied to click
119
+ * a specific position relative to the center.
120
+ */
121
+ async rightClick(offset) {
122
+ await (0, mouse_1.moveToRegion)(this, offset);
123
+ await nut_js_1.mouse.click(nut_js_1.Button.RIGHT);
124
+ }
125
+ /**
126
+ * Checks whether the supplied coordinates
127
+ * are located inside this region.
128
+ *
129
+ * Returns:
130
+ * - true if inside
131
+ * - false otherwise
132
+ */
133
+ contains(x, y) {
134
+ return (x >= this.x) && (x <= this.x + this.width) && (y >= this.y) && (y <= this.y + this.height);
135
+ }
136
+ /**
137
+ * Creates a new Region shifted by the
138
+ * supplied x and y offsets.
139
+ *
140
+ * Original region remains unchanged.
141
+ */
142
+ offset(x, y) {
143
+ return new Region(this.x + x, this.y + y, this.width, this.height, this.confidence);
144
+ }
145
+ /**
146
+ * Checks whether this region overlaps
147
+ * with another region.
148
+ *
149
+ * Returns:
150
+ * - true if regions intersect
151
+ * - false otherwise
152
+ */
153
+ intersects(region) {
154
+ return (this.x <= region.x + region.width) && (this.x + this.width >= region.x) && (this.y <= region.y + region.height) && (this.y + this.height >= region.y);
155
+ }
156
+ /**
157
+ * Returns the total area of the region
158
+ * in pixels.
159
+ */
160
+ area() {
161
+ return this.width * this.height;
162
+ }
163
+ /**
164
+ * Extracts OCR text from this region.
165
+ *
166
+ * Returns full OCR output including:
167
+ * - text
168
+ * - TSV data
169
+ * - HOCR data
170
+ * - block information
171
+ */
172
+ async readText() {
173
+ this.checkConfig();
174
+ return await (0, ocr_1.extractTextFromRegion)(this);
175
+ }
176
+ /**
177
+ * Searches for text within this region.
178
+ *
179
+ * Returns the matching text region
180
+ * if found, otherwise null.
181
+ *
182
+ * Supports selecting a specific match
183
+ * using the index parameter.
184
+ */
185
+ async findText(text, index = 0) {
186
+ this.checkConfig();
187
+ const match = await (0, text_1.findText)(text, index, this);
188
+ if (match == null) {
189
+ return null;
190
+ }
191
+ return match.offset(this.x, this.y);
192
+ }
193
+ /**
194
+ * Checks whether the supplied text
195
+ * exists within this region.
196
+ *
197
+ * Returns:
198
+ * - true if found
199
+ * - false otherwise
200
+ */
201
+ async existsText(text) {
202
+ this.checkConfig();
203
+ return await (0, text_1.existsText)(text, this);
204
+ }
205
+ /**
206
+ * Searches for an image within this region.
207
+ *
208
+ * Matching is performed only inside
209
+ * the current region, improving speed
210
+ * and reducing false positives.
211
+ *
212
+ * Returns:
213
+ * - matching Region if found
214
+ * - null otherwise
215
+ */
216
+ async find(image, confidence = 0.8) {
217
+ this.checkConfig();
218
+ const screenshot = await (0, screen_1.captureScreen)();
219
+ const croppedScreenshot = await (0, matcher_1.cropMat)(screenshot, this);
220
+ const template = await (0, matcher_1.loadTemplate)((0, path_1.resolveImagePath)(image));
221
+ try {
222
+ const match = (0, matcher_1.matchInspectorTemplate)(croppedScreenshot, template, confidence, false);
223
+ if (!match) {
224
+ return null;
225
+ }
226
+ return match.offset(this.x, this.y);
227
+ }
228
+ finally {
229
+ screenshot.delete();
230
+ croppedScreenshot.delete();
231
+ template.delete();
232
+ }
233
+ }
234
+ /**
235
+ * Finds all image matches within this region.
236
+ *
237
+ * Matching is performed only inside
238
+ * the current region.
239
+ *
240
+ * Returns an array of matching regions.
241
+ */
242
+ async findAll(image, confidence = 0.8) {
243
+ this.checkConfig();
244
+ const screenshot = await (0, screen_1.captureScreen)();
245
+ const croppedScreenshot = await (0, matcher_1.cropMat)(screenshot, this);
246
+ const template = await (0, matcher_1.loadTemplate)((0, path_1.resolveImagePath)(image));
247
+ try {
248
+ const matches = (0, matcher_1.findAllMatches)(croppedScreenshot, template, confidence);
249
+ return matches.map(match => match.offset(this.x, this.y));
250
+ }
251
+ finally {
252
+ screenshot.delete();
253
+ croppedScreenshot.delete();
254
+ template.delete();
255
+ }
256
+ }
257
+ /**
258
+ * Checks whether an image exists
259
+ * within this region.
260
+ *
261
+ * Returns:
262
+ * - true if found
263
+ * - false otherwise
264
+ */
265
+ async exists(image, confidence = 0.8) {
266
+ return await this.find(image, confidence) !== null;
267
+ }
268
+ }
269
+ exports.Region = Region;
270
+ Region.configWarningShown = false;
Binary file
@@ -5,6 +5,9 @@
5
5
  <link rel="stylesheet" href="styles.css" />
6
6
  </head>
7
7
  <body>
8
+ <div id="imageModal" class="image-modal">
9
+ <img id="modalImage"/>
10
+ </div>
8
11
  <div class="container">
9
12
  <header class="header">
10
13
  <h3>VISOR INSPECTOR</h3>
@@ -22,9 +25,13 @@
22
25
  <div class="controls-grid">
23
26
  <div class="control-group">
24
27
  <div class="input-wrapper">
25
- <span class="input-hint">Confidence</span>
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>
28
+ <span class="input-hint" title="Range [0, 1]">Confidence</span>
29
+ <input type="range" id="confRange" min="0" max="1" value="0.8" step="0.01"
30
+ oninput="document.getElementById('confidenceInput').value = this.value"/>
31
+ <input id="confidenceInput" type="number" min="0" max="1" step="0.01" value="0.8"
32
+ onchange="document.getElementById('confRange').value = this.value"/>
33
+ <!--<span class="input-hint">Multi Sacle Match:</span><input id="multiScaleCheck" type="checkbox" value="false"/>-->
34
+ <span class="input-hint">Current Template:</span><span id="currentTemplate" class="template-status">None</span>
28
35
  </div>
29
36
  </div>
30
37
  </div>
@@ -36,22 +43,12 @@
36
43
  <canvas id="screenCanvas" class="canvas"></canvas>
37
44
  </div>
38
45
  <div class="result-panel">
46
+ <h3 class="section-title"> Template </h3>
47
+ <img id="templatePreview" class="template-preview" alt="No Template Loaded" src="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs="/>
39
48
  <h3 class="section-title"> Match Result </h3>
40
49
  <div id="matchResult"> No Match Tested </div>
41
- </div>
50
+ </div>
42
51
  </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
52
  </div>
56
53
  <script src="./src/renderer.js"></script>
57
54
  </body>
package/inspector/main.js CHANGED
@@ -135,12 +135,17 @@ ipcMain.handle("test-match",
135
135
  const capturePath = path.join(__dirname, "assets", "capture.png");
136
136
  const screen = await visorMatcher.loadScreen(capturePath);
137
137
  const template = await visorMatcher.loadScreen(data.templatePath);
138
- const result = visorMatcher
139
- .matchTemplate(
140
- screen,
141
- template,
142
- data.confidence
143
- );
138
+ const result = visorMatcher.findAllInspectorMatches(
139
+ screen,
140
+ template,
141
+ data.confidence
142
+ );
143
+ ` const result = visorMatcher.matchInspectorTemplate(
144
+ screen,
145
+ template,
146
+ data.confidence,
147
+ data.isMultiMatch
148
+ );`
144
149
  console.log("match Result:", result);
145
150
  screen.delete();
146
151
  template.delete();